damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit e885f38c54f763b88a5ddd31b54f6c50805cc613
parent 3dbdc42d8b8441f1547e4ff1919f2330e498fdf8
Author: William Casarin <jb55@jb55.com>
Date:   Thu, 22 Jun 2023 11:15:51 +0200

refactor: switch CustomizeZapView to use a model

Changelog-Fixed: Fix CustomizeZapView from randomly disappearing

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 12++++++++++++
Adamus/Models/Zaps/CustomizeZapModel.swift | 30++++++++++++++++++++++++++++++
Mdamus/Views/Zaps/CustomizeZapView.swift | 93+++++++++++++++++++++++++++++++++++--------------------------------------------
3 files changed, 83 insertions(+), 52 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -170,6 +170,7 @@ 4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD19283AA67F008EE7EF /* Bech32.swift */; }; 4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */; }; 4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C987B56283FD07F0042CE38 /* FollowersModel.swift */; }; + 4C9AA1482A44442E003F49FD /* CustomizeZapModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9AA1472A44442E003F49FD /* CustomizeZapModel.swift */; }; 4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */; }; 4C9BB83429C12D9900FC4E37 /* EventProfileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */; }; 4C9F18E229AA9B6C008C55EC /* CustomizeZapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9F18E129AA9B6C008C55EC /* CustomizeZapView.swift */; }; @@ -613,6 +614,7 @@ 4C90BD19283AA67F008EE7EF /* Bech32.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32.swift; sourceTree = "<group>"; }; 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Tests.swift; sourceTree = "<group>"; }; 4C987B56283FD07F0042CE38 /* FollowersModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersModel.swift; sourceTree = "<group>"; }; + 4C9AA1472A44442E003F49FD /* CustomizeZapModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeZapModel.swift; sourceTree = "<group>"; }; 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayName.swift; sourceTree = "<group>"; }; 4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfileName.swift; sourceTree = "<group>"; }; 4C9F18E129AA9B6C008C55EC /* CustomizeZapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeZapView.swift; sourceTree = "<group>"; }; @@ -870,6 +872,7 @@ 4C0A3F8D280F63FF000448DE /* Models */ = { isa = PBXGroup; children = ( + 4C9AA1462A444422003F49FD /* Zaps */, 4C54AA0829A55416003E4487 /* Notifications */, 3AA247FC297E3CFF0090C62D /* RepostsModel.swift */, 4C0A3F8E280F640A000448DE /* ThreadModel.swift */, @@ -1160,6 +1163,14 @@ path = Buttons; sourceTree = "<group>"; }; + 4C9AA1462A444422003F49FD /* Zaps */ = { + isa = PBXGroup; + children = ( + 4C9AA1472A44442E003F49FD /* CustomizeZapModel.swift */, + ); + path = Zaps; + sourceTree = "<group>"; + }; 4CAAD8AE29888A9B00060CEA /* Relays */ = { isa = PBXGroup; children = ( @@ -1764,6 +1775,7 @@ 4CC7AAE7297EFA7B00430951 /* Zap.swift in Sources */, 4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */, 4C7D09682A0AE9B200943473 /* NWCScannerView.swift in Sources */, + 4C9AA1482A44442E003F49FD /* CustomizeZapModel.swift in Sources */, 4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */, 7C95CAEE299DCEF1009DCB67 /* KFOptionSetter+.swift in Sources */, 4C7D09722A0AEF5E00943473 /* DamusGradient.swift in Sources */, diff --git a/damus/Models/Zaps/CustomizeZapModel.swift b/damus/Models/Zaps/CustomizeZapModel.swift @@ -0,0 +1,30 @@ +// +// CustomizeZapModel.swift +// damus +// +// Created by William Casarin on 2023-06-22. +// + +import Foundation + + +class CustomizeZapModel: ObservableObject { + @Published var comment: String = "" + @Published var custom_amount: String = "" + @Published var custom_amount_sats: Int? = nil + @Published var zap_type: ZapType = .pub + @Published var invoice: String = "" + @Published var error: String? = nil + @Published var showing_wallet_selector: Bool = false + @Published var zapping: Bool = false + @Published var show_zap_types: Bool = false + + init() { + } + + func set_defaults(settings: UserSettingsStore) { + self.zap_type = settings.default_zap_type + self.custom_amount = String(settings.default_zap_amount) + self.custom_amount_sats = settings.default_zap_amount + } +} diff --git a/damus/Views/Zaps/CustomizeZapView.swift b/damus/Views/Zaps/CustomizeZapView.swift @@ -48,18 +48,10 @@ struct CustomizeZapView: View { let state: DamusState let target: ZapTarget let lnurl: String - @State var comment: String - @State var custom_amount: String - @State var custom_amount_sats: Int? - @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] + @StateObject var model: CustomizeZapModel = CustomizeZapModel() @Environment(\.dismiss) var dismiss @Environment(\.colorScheme) var colorScheme @@ -72,21 +64,12 @@ struct CustomizeZapView: View { } init(state: DamusState, target: ZapTarget, lnurl: String) { - self._comment = State(initialValue: "") self.target = target self.zap_amounts = get_zap_amount_items(state.settings.default_zap_amount) - self._error = State(initialValue: nil) - self._invoice = State(initialValue: "") - self._showing_wallet_selector = State(initialValue: false) - self._zap_type = State(initialValue: state.settings.default_zap_type) - self._custom_amount = State(initialValue: String(state.settings.default_zap_amount)) - self._custom_amount_sats = State(initialValue: nil) - self._zapping = State(initialValue: false) self.lnurl = lnurl self.state = state } - func amount_parts(_ n: Int) -> [ZapAmountItem] { var i: Int = -1 let start = n * 3 @@ -101,7 +84,10 @@ struct CustomizeZapView: View { func AmountsPart(n: Int) -> some View { HStack(alignment: .center, spacing: 15) { ForEach(amount_parts(n)) { entry in - ZapAmountButton(zapAmountItem: entry, action: {custom_amount_sats = entry.amount; custom_amount = String(entry.amount)}) + ZapAmountButton(zapAmountItem: entry, action: { + model.custom_amount_sats = entry.amount + model.custom_amount = String(entry.amount) + }) } } } @@ -125,17 +111,17 @@ struct CustomizeZapView: View { .font(.headline) .frame(width: 70, height: 70) .foregroundColor(fontColor()) - .background(custom_amount_sats == zapAmountItem.amount ? fillColor() : DamusColors.adaptableGrey) + .background(model.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)) + .stroke(DamusColors.purple.opacity(model.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) { + TextField("", text: $model.custom_amount) + .placeholder(when: model.custom_amount.isEmpty, alignment: .center) { Text(verbatim: 0.formatted()) } .accentColor(.clear) @@ -143,16 +129,16 @@ struct CustomizeZapView: View { .minimumScaleFactor(0.01) .keyboardType(.numberPad) .multilineTextAlignment(.center) - .onReceive(Just(custom_amount)) { newValue in + .onChange(of: model.custom_amount) { newValue in if let parsed = handle_string_amount(new_value: newValue) { - self.custom_amount = parsed.formatted() - self.custom_amount_sats = parsed + model.custom_amount = parsed.formatted() + model.custom_amount_sats = parsed } else { - self.custom_amount = "" - self.custom_amount_sats = nil + model.custom_amount = "" + model.custom_amount_sats = nil } } - Text(verbatim: satsString(custom_amount_sats ?? 0)) + Text(verbatim: satsString(model.custom_amount_sats ?? 0)) .font(.system(size: 18, weight: .heavy)) } } @@ -160,12 +146,12 @@ struct CustomizeZapView: View { var ZapReply: some View { HStack { if #available(iOS 16.0, *) { - TextField(NSLocalizedString("Send a message with your zap...", comment: "Placeholder text for a comment to send as part of a zap to the user."), text: $comment, axis: .vertical) + TextField(NSLocalizedString("Send a message with your zap...", comment: "Placeholder text for a comment to send as part of a zap to the user."), text: $model.comment, axis: .vertical) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) .lineLimit(5) } else { - TextField(NSLocalizedString("Send a message with your zap...", comment: "Placeholder text for a comment to send as part of a zap to the user."), text: $comment) + TextField(NSLocalizedString("Send a message with your zap...", comment: "Placeholder text for a comment to send as part of a zap to the user."), text: $model.comment) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) } @@ -179,24 +165,24 @@ struct CustomizeZapView: View { var ZapButton: some View { VStack { - if zapping { + if model.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, target: target, lnurl: lnurl, is_custom: true, comment: comment, amount_sats: amount, zap_type: zap_type) - self.zapping = true + let amount = model.custom_amount_sats + send_zap(damus_state: state, target: target, lnurl: lnurl, is_custom: true, comment: model.comment, amount_sats: amount, zap_type: model.zap_type) + model.zapping = true } - .disabled(custom_amount_sats == 0 || custom_amount.isEmpty) + .disabled(model.custom_amount_sats == 0 || model.custom_amount.isEmpty) .font(.system(size: 28, weight: .bold)) .frame(width: 130, height: 50) .foregroundColor(.white) .background(LINEAR_GRADIENT) - .opacity(custom_amount_sats == 0 || custom_amount.isEmpty ? 0.5 : 1.0) + .opacity(model.custom_amount_sats == 0 || model.custom_amount.isEmpty ? 0.5 : 1.0) .clipShape(Capsule()) } - if let error { + if let error = model.error { Text(error) .foregroundColor(.red) } @@ -212,30 +198,30 @@ struct CustomizeZapView: View { return } - self.zapping = false + model.zapping = false switch zap_ev.type { case .failed(let err): switch err { case .fetching_invoice: - self.error = NSLocalizedString("Error fetching lightning invoice", comment: "Message to display when there was an error fetching a lightning invoice while attempting to zap.") + model.error = NSLocalizedString("Error fetching lightning invoice", comment: "Message to display when there was an error fetching a lightning invoice while attempting to zap.") case .bad_lnurl: - self.error = NSLocalizedString("Invalid lightning address", comment: "Message to display when there was an error attempting to zap due to an invalid lightning address.") + model.error = NSLocalizedString("Invalid lightning address", comment: "Message to display when there was an error attempting to zap due to an invalid lightning address.") case .canceled: - self.error = NSLocalizedString("Zap attempt from connected wallet was canceled.", comment: "Message to display when a zap from the user's connected wallet was canceled.") + model.error = NSLocalizedString("Zap attempt from connected wallet was canceled.", comment: "Message to display when a zap from the user's connected wallet was canceled.") case .send_failed: - self.error = NSLocalizedString("Zap attempt from connected wallet failed.", comment: "Message to display when sending a zap from the user's connected wallet failed.") + model.error = NSLocalizedString("Zap attempt from connected wallet failed.", comment: "Message to display when sending a zap from the user's connected wallet failed.") } break case .got_zap_invoice(let inv): if state.settings.show_wallet_selector { - self.invoice = inv - self.showing_wallet_selector = true + model.invoice = inv + model.showing_wallet_selector = true } else { end_editing() let wallet = state.settings.default_wallet.model open_with_wallet(wallet: wallet, invoice: inv) - self.showing_wallet_selector = false + model.showing_wallet_selector = false dismiss() } case .sent_from_nwc: @@ -260,7 +246,7 @@ struct CustomizeZapView: View { Spacer() } - .sheet(isPresented: $show_zap_types) { + .sheet(isPresented: $model.show_zap_types) { if #available(iOS 16.0, *) { ZapPicker .presentationDetents([.medium]) @@ -269,8 +255,11 @@ struct CustomizeZapView: View { ZapPicker } } - .sheet(isPresented: $showing_wallet_selector) { - SelectWalletView(default_wallet: state.settings.default_wallet, showingSelectWallet: $showing_wallet_selector, our_pubkey: state.pubkey, invoice: invoice) + .sheet(isPresented: $model.showing_wallet_selector) { + SelectWalletView(default_wallet: state.settings.default_wallet, showingSelectWallet: $model.showing_wallet_selector, our_pubkey: state.pubkey, invoice: model.invoice) + } + .onAppear { + model.set_defaults(settings: state.settings) } .onReceive(handle_notify(.zapping)) { notif in receive_zap(notif: notif) @@ -283,9 +272,9 @@ struct CustomizeZapView: View { func ZapTypeButton() -> some View { Button(action: { - show_zap_types = true + model.show_zap_types = true }) { - switch zap_type { + switch model.zap_type { case .pub: Image("globe") Text("Public", comment: "Button text to indicate that the zap type is a public zap.") @@ -308,7 +297,7 @@ struct CustomizeZapView: View { } var ZapPicker: some View { - ZapTypePicker(zap_type: $zap_type, settings: state.settings, profiles: state.profiles, pubkey: target.pubkey) + ZapTypePicker(zap_type: $model.zap_type, settings: state.settings, profiles: state.profiles, pubkey: target.pubkey) } }