commit 8227be18736f1e1bdf5ca1ef6c60b93add62b31e
parent cbd92539a6755942e34653037c4afe8e7aa5932b
Author: ericholguin <eric.holguinsanchez@gmail.com>
Date: Tue, 18 Apr 2023 20:12:26 -0600
Updated custom zap view
Changelog-Changed: New looks to the custom zaps view
Diffstat:
1 file changed, 205 insertions(+), 88 deletions(-)
diff --git a/damus/Views/Zaps/CustomizeZapView.swift b/damus/Views/Zaps/CustomizeZapView.swift
@@ -52,16 +52,25 @@ struct CustomizeZapView: View {
@State var comment: String
@State var custom_amount: String
@State var custom_amount_sats: Int?
- @State var selected_amount: ZapAmountItem
@State var zap_type: ZapType
@State var invoice: String
@State var error: String?
@State var showing_wallet_selector: Bool
@State var zapping: Bool
+ @State var show_zap_types: Bool = false
let zap_amounts: [ZapAmountItem]
@Environment(\.dismiss) var dismiss
+ @Environment(\.colorScheme) var colorScheme
+
+ func fillColor() -> Color {
+ colorScheme == .light ? DamusColors.white : DamusColors.black
+ }
+
+ func fontColor() -> Color {
+ colorScheme == .light ? DamusColors.black : DamusColors.white
+ }
init(state: DamusState, event: NostrEvent, lnurl: String) {
self._comment = State(initialValue: "")
@@ -70,56 +79,190 @@ struct CustomizeZapView: View {
self._error = State(initialValue: nil)
self._invoice = State(initialValue: "")
self._showing_wallet_selector = State(initialValue: false)
- self._custom_amount = State(initialValue: "")
self._zap_type = State(initialValue: .pub)
- let selected = get_default_zap_amount_item(state.settings.default_zap_amount)
+ let default_amount = get_default_zap_amount_item(state.settings.default_zap_amount)
self._selected_amount = State(initialValue: selected)
+ self._custom_amount = State(initialValue: String(default_amount))
self._custom_amount_sats = State(initialValue: nil)
self._zapping = State(initialValue: false)
self.lnurl = lnurl
self.state = state
}
- var zap_type_desc: String {
- switch zap_type {
+ func zap_type_desc(type: ZapType) -> String {
+ switch type {
case .pub:
- return NSLocalizedString("Everyone on can see that you zapped", comment: "Description of public zap type where the zap is sent publicly and identifies the user who sent it.")
+ return NSLocalizedString("Everyone will see that you zapped", comment: "Description of public zap type where the zap is sent publicly and identifies the user who sent it.")
case .anon:
- return NSLocalizedString("No one can see that you zapped", comment: "Description of anonymous zap type where the zap is sent anonymously and does not identify the user who sent it.")
+ return NSLocalizedString("No one will see that you zapped", comment: "Description of anonymous zap type where the zap is sent anonymously and does not identify the user who sent it.")
case .priv:
let pk = event.pubkey
let prof = state.profiles.lookup(id: pk)
let name = Profile.displayName(profile: prof, pubkey: pk).username
- return String.localizedStringWithFormat(NSLocalizedString("private_zap_description", value: "Only '%@' can see that you zapped them", comment: "Description of private zap type where the zap is sent privately and does not identify the user to the public."), name)
+ return String.localizedStringWithFormat(NSLocalizedString("private_zap_description", value: "Only '%@' will see that you zapped them", comment: "Description of private zap type where the zap is sent privately and does not identify the user to the public."), name)
case .non_zap:
- return NSLocalizedString("No zaps are sent, only a lightning payment.", comment: "Description of non-zap type where sats are sent to the user's wallet as a regular Lightning payment, not as a zap.")
+ return NSLocalizedString("No zaps will be sent, only a lightning payment.", comment: "Description of non-zap type where sats are sent to the user's wallet as a regular Lightning payment, not as a zap.")
}
}
var ZapTypePicker: some View {
- Picker(NSLocalizedString("Zap Type", comment: "Header text to indicate that the picker below it is to choose the type of zap to send."), selection: $zap_type) {
- Text("Public", comment: "Picker option to indicate that a zap should be sent publicly and identify the user as who sent it.").tag(ZapType.pub)
- Text("Private", comment: "Picker option to indicate that a zap should be sent privately and not identify the user to the public.").tag(ZapType.priv)
- Text("Anonymous", comment: "Picker option to indicate that a zap should be sent anonymously and not identify the user as who sent it.").tag(ZapType.anon)
- Text(verbatim: NSLocalizedString("none_zap_type", value: "None", comment: "Picker option to indicate that sats should be sent to the user's wallet as a regular Lightning payment, not as a zap.")).tag(ZapType.non_zap)
+ VStack(spacing: 20) {
+ Text("Zap type")
+ .font(.system(size: 18, weight: .heavy))
+ ZapTypeSelection(text: "Public", comment: "Picker option to indicate that a zap should be sent publicly and identify the user as who sent it.", img: "person.2.circle.fill", action: {zap_type = ZapType.pub}, type: ZapType.pub)
+ ZapTypeSelection(text: "Private", comment: "Picker option to indicate that a zap should be sent privately and not identify the user to the public.", img: "lock.circle.fill", action: {zap_type = ZapType.priv}, type: ZapType.priv)
+ ZapTypeSelection(text: "Anonymous", comment: "Picker option to indicate that a zap should be sent anonymously and not identify the user as who sent it.", img: "person.crop.circle.fill.badge.questionmark", action: {zap_type = ZapType.anon}, type: ZapType.anon)
+ ZapTypeSelection(text: "None", comment: "Picker option to indicate that sats should be sent to the user's wallet as a regular Lightning payment, not as a zap.", img: "bolt.circle.fill", action: {zap_type = ZapType.non_zap}, type: ZapType.non_zap)
+ }
+ .padding(.horizontal)
+ }
+
+ func ZapTypeSelection(text: LocalizedStringKey, comment: StaticString, img: String, action: @escaping () -> (), type: ZapType) -> some View {
+ Button(action: action) {
+ VStack(alignment: .leading, spacing: 5) {
+ HStack {
+ Image(systemName: img)
+ .foregroundColor(.gray)
+ .font(.system(size: 24))
+ Text(text, comment: comment)
+ .font(.system(size: 20, weight: .semibold))
+ Spacer()
+ }
+ .padding(.horizontal)
+ Text(zap_type_desc(type: type))
+ .padding(.horizontal)
+ .foregroundColor(.gray)
+ .font(.system(size: 16))
+ }
}
- .pickerStyle(.menu)
+ .frame(minWidth: 400, maxWidth: .infinity, minHeight: 50, maxHeight: 70)
+ .foregroundColor(fontColor())
+ .background(zap_type == type ? fillColor() : DamusColors.adaptableGrey)
+ .cornerRadius(15)
+ .overlay(RoundedRectangle(cornerRadius: 15)
+ .stroke(DamusColors.purple.opacity(zap_type == type ? 1.0 : 0.0), lineWidth: 2))
+ }
+
+ func ZapTypeButton() -> some View {
+ Button(action: {
+ show_zap_types = true
+ }) {
+ switch zap_type {
+ case .pub:
+ Image(systemName: "person.2")
+ Text("Public")
+ case .anon:
+ Image(systemName: "person.fill.questionmark")
+ Text("Anonymous")
+ case .priv:
+ Image(systemName: "lock")
+ Text("Private")
+ case .non_zap:
+ Image(systemName: "bolt")
+ Text("None")
+ }
+ }
+ .font(.headline)
+ .foregroundColor(fontColor())
+ .padding(EdgeInsets(top: 5, leading: 15, bottom: 5, trailing: 15))
+ .background(DamusColors.adaptableGrey)
+ .cornerRadius(15)
}
var AmountPicker: some View {
- Picker(NSLocalizedString("Zap Amount", comment: "Title of picker that allows selection of predefined amounts to zap."), selection: $selected_amount) {
- ForEach(zap_amounts) { entry in
- let fmt = format_msats_abbrev(Int64(entry.amount) * 1000)
- HStack(alignment: .firstTextBaseline) {
- Text("\(entry.icon)")
- .frame(width: 30)
- Text("\(fmt)")
- .frame(width: 50)
+ ScrollView(.horizontal, showsIndicators: false) {
+ HStack(alignment: .center, spacing: 15) {
+ ForEach(zap_amounts) { entry in
+ ZapAmountButton(zapAmountItem: entry, action: {custom_amount_sats = entry.amount; custom_amount = String(entry.amount)})
}
- .tag(entry)
+ }
+ .padding(10)
+ }
+ }
+
+ func ZapAmountButton(zapAmountItem: ZapAmountItem, action: @escaping () -> ()) -> some View {
+ Button(action: action) {
+ let fmt = format_msats_abbrev(Int64(zapAmountItem.amount) * 1000)
+ Text("\(zapAmountItem.icon)\n\(fmt)")
+ }
+ .font(.headline)
+ .frame(width: 70, height: 70)
+ .foregroundColor(fontColor())
+ .background(custom_amount_sats == zapAmountItem.amount ? fillColor() : DamusColors.adaptableGrey)
+ .cornerRadius(15)
+ .overlay(RoundedRectangle(cornerRadius: 15)
+ .stroke(DamusColors.purple.opacity(custom_amount_sats == zapAmountItem.amount ? 1.0 : 0.0), lineWidth: 2))
+ }
+
+ var CustomZapTextField: some View {
+ VStack(alignment: .center, spacing: 0) {
+ TextField("", text: $custom_amount)
+ .placeholder(when: custom_amount.isEmpty, alignment: .center) {
+ Text(String("0"))
+ }
+ .accentColor(.clear)
+ .font(.system(size: 72, weight: .heavy))
+ .minimumScaleFactor(0.01)
+ .keyboardType(.numberPad)
+ .multilineTextAlignment(.center)
+ .onReceive(Just(custom_amount)) { newValue in
+ if let parsed = handle_string_amount(new_value: newValue) {
+ self.custom_amount = parsed.formatted()
+ self.custom_amount_sats = parsed
+ } else {
+ self.custom_amount = ""
+ self.custom_amount_sats = nil
+ }
+ }
+ Text("sats")
+ .font(.system(size: 18, weight: .heavy))
+ }
+ }
+
+ var ZapReply: some View {
+ HStack {
+ if #available(iOS 16.0, *) {
+ TextField(NSLocalizedString("Send a reply with your zap...", comment: "Placeholder text for a comment to send as part of a zap to the user."), text: $comment, axis: .vertical)
+ .autocorrectionDisabled(true)
+ .textInputAutocapitalization(.never)
+ .lineLimit(5)
+ } else {
+ TextField(NSLocalizedString("Send a reply with your zap...", comment: "Placeholder text for a comment to send as part of a zap to the user."), text: $comment)
+ .autocorrectionDisabled(true)
+ .textInputAutocapitalization(.never)
+ }
+ }
+ .frame(minHeight: 30)
+ .padding(10)
+ .background(.secondary.opacity(0.2))
+ .cornerRadius(10)
+ .padding(.horizontal, 10)
+ }
+
+ var ZapButton: some View {
+ VStack {
+ if zapping {
+ Text("Zapping...", comment: "Text to indicate that the app is in the process of sending a zap.")
+ } else {
+ Button(NSLocalizedString("Zap ⚡️", comment: "Button to send a zap.")) {
+ let amount = custom_amount_sats
+ send_zap(damus_state: state, event: event, lnurl: lnurl, is_custom: true, comment: comment, amount_sats: amount, zap_type: zap_type)
+ self.zapping = true
+ }
+ .disabled(custom_amount_sats == 0 || custom_amount.isEmpty)
+ .font(.system(size: 28, weight: .bold))
+ .frame(width: 120, height: 50)
+ .foregroundColor(.white)
+ .background(LINEAR_GRADIENT)
+ .opacity(custom_amount_sats == 0 || custom_amount.isEmpty ? 0.5 : 1.0)
+ .clipShape(Capsule())
+ }
+
+ if let error {
+ Text(error)
+ .foregroundColor(.red)
}
}
- .pickerStyle(.wheel)
}
func receive_zap(notif: Notification) {
@@ -154,8 +297,6 @@ struct CustomizeZapView: View {
dismiss()
}
}
-
-
}
var body: some View {
@@ -166,76 +307,52 @@ struct CustomizeZapView: View {
.onReceive(handle_notify(.zapping)) { notif in
receive_zap(notif: notif)
}
- .ignoresSafeArea()
- }
-
- var TheForm: some View {
- Form {
-
- Group {
- Section(content: {
- AmountPicker
- .frame(height: 120)
- }, header: {
- Text("Zap Amount in sats", comment: "Header text to indicate that the picker below it is to choose a pre-defined amount of sats to zap.")
- })
-
- Section(content: {
- // Use the selected sats amount as the placeholder text so that the UI is less confusing.
- // User can type in their custom amount, which hides the placeholder.
- TextField(selected_amount.amount.formatted(), text: $custom_amount)
- .keyboardType(.numberPad)
- .onReceive(Just(custom_amount)) { newValue in
- if let parsed = handle_string_amount(new_value: newValue) {
- self.custom_amount = parsed.formatted()
- self.custom_amount_sats = parsed
- } else {
- self.custom_amount = ""
- self.custom_amount_sats = nil
- }
- }
- }, header: {
- Text("Custom Zap Amount", comment: "Header text to indicate that the text field below it is to enter a custom zap amount.")
- })
-
- Section(content: {
- TextField(NSLocalizedString("Awesome post!", comment: "Placeholder text for a comment to send as part of a zap to the user."), text: $comment)
- }, header: {
- Text("Comment", comment: "Header text to indicate that the text field below it is a comment that will be used to send as part of a zap to the user.")
- })
+ .background(fillColor().edgesIgnoringSafeArea(.all))
+ .onTapGesture {
+ hideKeyboard()
}
- .dismissKeyboardOnTap()
-
- Section(content: {
- ZapTypePicker
- }, header: {
- Text("Zap Type", comment: "Header text to indicate that the picker below it is to choose the type of zap to send.")
- }, footer: {
- Text(zap_type_desc)
- })
+ }
+
+ var CustomZap: some View {
+ VStack(alignment: .center, spacing: 20) {
+ ZapTypeButton()
+ .padding(.top, 50)
- if zapping {
- Text("Zapping...", comment: "Text to indicate that the app is in the process of sending a zap.")
- } else {
- Button(NSLocalizedString("Zap", comment: "Button to send a zap.")) {
- let amount = custom_amount_sats ?? selected_amount.amount
- send_zap(damus_state: state, event: event, lnurl: lnurl, is_custom: true, comment: comment, amount_sats: amount, zap_type: zap_type)
- self.zapping = true
- }
- .zIndex(16)
- }
+ Spacer()
+
+ CustomZapTextField
- if let error {
- Text(error)
- .foregroundColor(.red)
+ AmountPicker
+
+ ZapReply
+
+ ZapButton
+
+ Spacer()
+
+ Spacer()
+ }
+ .sheet(isPresented: $show_zap_types) {
+ if #available(iOS 16.0, *) {
+ ZapTypePicker
+ .presentationDetents([.medium])
+ .presentationDragIndicator(.visible)
+ } else {
+ ZapTypePicker
}
-
}
}
var MainContent: some View {
- TheForm
+ CustomZap
+ }
+}
+
+extension View {
+ func hideKeyboard() {
+ let resign = #selector(UIResponder.resignFirstResponder)
+ UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil)
}
}