From 82b542709c6848b2fd19f7eda5ec88ed3fb72c38 Mon Sep 17 00:00:00 2001 From: Marc Stibane Date: Sun, 5 Nov 2023 11:58:07 +0100 Subject: SendDone: Prevent double execution --- TalerWallet.xcodeproj/project.pbxproj | 12 +-- TalerWallet1/Views/Peer2peer/PaymentPurpose.swift | 12 ++- TalerWallet1/Views/Peer2peer/SendDone.swift | 99 -------------------- TalerWallet1/Views/Peer2peer/SendDoneV.swift | 106 ++++++++++++++++++++++ TalerWallet1/Views/Peer2peer/SendPurpose.swift | 13 ++- 5 files changed, 127 insertions(+), 115 deletions(-) delete mode 100644 TalerWallet1/Views/Peer2peer/SendDone.swift create mode 100644 TalerWallet1/Views/Peer2peer/SendDoneV.swift diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj index 9ca384c..a521fc8 100644 --- a/TalerWallet.xcodeproj/project.pbxproj +++ b/TalerWallet.xcodeproj/project.pbxproj @@ -62,7 +62,7 @@ 4E3EAE422A990778009F1BE8 /* KeyboardResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EAD117529F672FA008EDD0B /* KeyboardResponder.swift */; }; 4E3EAE432A990778009F1BE8 /* TransactionRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB095302989CBFE0043A8A1 /* TransactionRowView.swift */; }; 4E3EAE442A990778009F1BE8 /* PublicConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EA1ABBD29A3833A008821EA /* PublicConstants.swift */; }; - 4E3EAE452A990778009F1BE8 /* SendDone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB3136029FEE79B007D68BC /* SendDone.swift */; }; + 4E3EAE452A990778009F1BE8 /* SendDoneV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB3136029FEE79B007D68BC /* SendDoneV.swift */; }; 4E3EAE462A990778009F1BE8 /* TextFieldAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB095482989CBFE0043A8A1 /* TextFieldAlert.swift */; }; 4E3EAE472A990778009F1BE8 /* QuiteSomeCoins.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBA82AC2A3F580500E5F39A /* QuiteSomeCoins.swift */; }; 4E3EAE482A990778009F1BE8 /* PayTemplateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBA56402A7FF5200084948B /* PayTemplateView.swift */; }; @@ -227,7 +227,7 @@ 4EB0956E2989CBFE0043A8A1 /* Model+Pending.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift */; }; 4EB0956F2989CBFE0043A8A1 /* PendingOpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0954D2989CBFE0043A8A1 /* PendingOpView.swift */; }; 4EB095702989CBFE0043A8A1 /* PendingOpsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0954E2989CBFE0043A8A1 /* PendingOpsListView.swift */; }; - 4EB3136129FEE79B007D68BC /* SendDone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB3136029FEE79B007D68BC /* SendDone.swift */; }; + 4EB3136129FEE79B007D68BC /* SendDoneV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB3136029FEE79B007D68BC /* SendDoneV.swift */; }; 4EB431672A1E55C700C5690E /* ManualWithdrawDone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB431662A1E55C700C5690E /* ManualWithdrawDone.swift */; }; 4EBA563F2A7FD9390084948B /* SuperScriptDigits.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBA563E2A7FD9390084948B /* SuperScriptDigits.swift */; }; 4EBA56412A7FF5200084948B /* PayTemplateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBA56402A7FF5200084948B /* PayTemplateView.swift */; }; @@ -402,7 +402,7 @@ 4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Model+Pending.swift"; sourceTree = ""; }; 4EB0954D2989CBFE0043A8A1 /* PendingOpView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PendingOpView.swift; sourceTree = ""; }; 4EB0954E2989CBFE0043A8A1 /* PendingOpsListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PendingOpsListView.swift; sourceTree = ""; }; - 4EB3136029FEE79B007D68BC /* SendDone.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendDone.swift; sourceTree = ""; }; + 4EB3136029FEE79B007D68BC /* SendDoneV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendDoneV.swift; sourceTree = ""; }; 4EB431662A1E55C700C5690E /* ManualWithdrawDone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualWithdrawDone.swift; sourceTree = ""; }; 4EBA563E2A7FD9390084948B /* SuperScriptDigits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuperScriptDigits.swift; sourceTree = ""; }; 4EBA56402A7FF5200084948B /* PayTemplateView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayTemplateView.swift; sourceTree = ""; }; @@ -773,7 +773,7 @@ children = ( 4E40E0BD29F25ABB00B85369 /* SendAmount.swift */, 4E7940DD29FC307C00A9AEA1 /* SendPurpose.swift */, - 4EB3136029FEE79B007D68BC /* SendDone.swift */, + 4EB3136029FEE79B007D68BC /* SendDoneV.swift */, 4E9320442A1645B600A87B0E /* RequestPayment.swift */, 4E9320462A164BC700A87B0E /* PaymentPurpose.swift */, ); @@ -1085,7 +1085,7 @@ 4E3EAE422A990778009F1BE8 /* KeyboardResponder.swift in Sources */, 4E3EAE432A990778009F1BE8 /* TransactionRowView.swift in Sources */, 4E3EAE442A990778009F1BE8 /* PublicConstants.swift in Sources */, - 4E3EAE452A990778009F1BE8 /* SendDone.swift in Sources */, + 4E3EAE452A990778009F1BE8 /* SendDoneV.swift in Sources */, 4E3EAE462A990778009F1BE8 /* TextFieldAlert.swift in Sources */, 4E3EAE472A990778009F1BE8 /* QuiteSomeCoins.swift in Sources */, 4E3EAE482A990778009F1BE8 /* PayTemplateView.swift in Sources */, @@ -1194,7 +1194,7 @@ 4EAD117629F672FA008EDD0B /* KeyboardResponder.swift in Sources */, 4EB095572989CBFE0043A8A1 /* TransactionRowView.swift in Sources */, 4EA1ABBE29A3833A008821EA /* PublicConstants.swift in Sources */, - 4EB3136129FEE79B007D68BC /* SendDone.swift in Sources */, + 4EB3136129FEE79B007D68BC /* SendDoneV.swift in Sources */, 4EB0956B2989CBFE0043A8A1 /* TextFieldAlert.swift in Sources */, 4EBA82AD2A3F580500E5F39A /* QuiteSomeCoins.swift in Sources */, 4EBA56412A7FF5200084948B /* PayTemplateView.swift in Sources */, diff --git a/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift b/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift index d8b49bb..373daa8 100644 --- a/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift +++ b/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift @@ -18,6 +18,7 @@ struct PaymentPurpose: View { @AppStorage("iconOnly") var iconOnly: Bool = false let navTitle = String(localized: "NavTitle_Request_Subject", defaultValue: "Request", comment: "NavTitle for entering the subject for Request-Payment") + @State private var transactionStarted: Bool = false @FocusState private var isFocused: Bool private var label: String { @@ -63,11 +64,12 @@ struct PaymentPurpose: View { let emptyStr = "" let disabled = (expireDays == 0) || (summary.count < 1) NavigationLink(destination: LazyView { - SendDone(stack: stack.push(), - amountToSend: nil, - amountToReceive: amount, - summary: summary, - expireDays: expireDays) + SendDoneV(stack: stack.push(), + amountToSend: nil, + amountToReceive: amount, + summary: summary, + expireDays: expireDays, + transactionStarted: $transactionStarted) }) { Text("Request \(label) \(scopeInfo.currency)") // .accessibilityFont(buttonFont) diff --git a/TalerWallet1/Views/Peer2peer/SendDone.swift b/TalerWallet1/Views/Peer2peer/SendDone.swift deleted file mode 100644 index 3cf0ad4..0000000 --- a/TalerWallet1/Views/Peer2peer/SendDone.swift +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This file is part of GNU Taler, ©2022-23 Taler Systems S.A. - * See LICENSE.md - */ -import SwiftUI -import taler_swift -import SymLog - -// Called when initiating a P2P transaction: Send coins or Send Request(Invoice) -struct SendDone: View { - private let symLog = SymLogV(0) - let stack: CallStack - let navTitle = String(localized: "P2P Ready") -#if DEBUG - @AppStorage("developerMode") var developerMode: Bool = true -#else - @AppStorage("developerMode") var developerMode: Bool = false -#endif - @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic - - let amountToSend: Amount? - let amountToReceive: Amount? - let summary: String - let expireDays: UInt - - @EnvironmentObject private var model: WalletModel - - @State private var transactionId: String? - - func reloadOneAction(_ transactionId: String) async throws -> Transaction { - return try await model.getTransactionByIdT(transactionId) - } - - var body: some View { -#if DEBUG - let _ = Self._printChanges() - let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear -#endif - VStack { - if let transactionId { - TransactionDetailView(stack: stack.push(), - transactionId: transactionId, - reloadAction: reloadOneAction, - navTitle: navTitle, - doneAction: ViewState.shared.popToRootView, - abortAction: nil, - deleteAction: nil, - failAction: nil, - suspendAction: nil, - resumeAction: nil) - .navigationBarBackButtonHidden(true) - .interactiveDismissDisabled() // can only use "Done" button to dismiss - } else { - WithdrawProgressView(message: "Loading...") - } - } -// .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all)) - .navigationTitle(navTitle) - .task { - symLog.log(".task") - do { - let timestamp = developerMode ? Timestamp.inSomeMinutes(expireDays > 20 ? (24*60) - : expireDays > 5 ? 60 : 3) - : Timestamp.inSomeDays(expireDays) - if let amountToSend { - let terms = PeerContractTerms(amount: amountToSend, - summary: summary, - purse_expiration: timestamp) - // TODO: user might choose baseURL - let response = try await model.initiatePeerPushDebitM(nil, terms: terms) - // will switch from WithdrawProgressView to TransactionDetailView - transactionId = response.transactionId - } else if let amountToReceive { - let terms = PeerContractTerms(amount: amountToReceive, - summary: summary, - purse_expiration: timestamp) - // TODO: user might choose baseURL - let response = try await model.initiatePeerPullCreditM(nil, terms: terms) - // will switch from WithdrawProgressView to TransactionDetailView - transactionId = response.transactionId - } else { fatalError() } - } catch { // TODO: error - symLog.log(error.localizedDescription) - } - } // task - } -} -// MARK: - -struct SendNow_Previews: PreviewProvider { - static var previews: some View { - Group { - SendDone(stack: CallStack("Preview"), - amountToSend: try! Amount(fromString: LONGCURRENCY + ":4.8"), - amountToReceive: nil, - summary: "some subject/purpose", - expireDays: 0) - } - } -} diff --git a/TalerWallet1/Views/Peer2peer/SendDoneV.swift b/TalerWallet1/Views/Peer2peer/SendDoneV.swift new file mode 100644 index 0000000..84b31ee --- /dev/null +++ b/TalerWallet1/Views/Peer2peer/SendDoneV.swift @@ -0,0 +1,106 @@ +/* + * This file is part of GNU Taler, ©2022-23 Taler Systems S.A. + * See LICENSE.md + */ +import SwiftUI +import taler_swift +import SymLog + +// Called when initiating a P2P transaction: Send coins or Send Request(Invoice) +struct SendDoneV: View { + private let symLog = SymLogV() + let stack: CallStack + let navTitle = String(localized: "P2P Ready") +#if DEBUG + @AppStorage("developerMode") var developerMode: Bool = true +#else + @AppStorage("developerMode") var developerMode: Bool = false +#endif + @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic + + let amountToSend: Amount? + let amountToReceive: Amount? + let summary: String + let expireDays: UInt + @Binding var transactionStarted: Bool + + @EnvironmentObject private var model: WalletModel + + @State private var transactionId: String? = nil + + func reloadOneAction(_ transactionId: String) async throws -> Transaction { + return try await model.getTransactionByIdT(transactionId) + } + + var body: some View { +#if DEBUG + let _ = Self._printChanges() + let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear +#endif + VStack { + if let transactionId { + TransactionDetailView(stack: stack.push(), + transactionId: transactionId, + reloadAction: reloadOneAction, + navTitle: navTitle, + doneAction: ViewState.shared.popToRootView, + abortAction: nil, + deleteAction: nil, + failAction: nil, + suspendAction: nil, + resumeAction: nil) + .navigationBarBackButtonHidden(true) + .interactiveDismissDisabled() // can only use "Done" button to dismiss + } else { + WithdrawProgressView(message: "Loading...") + } + } +// .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all)) + .navigationTitle(navTitle) + .task { + symLog.log(".task") + do { + guard transactionStarted == false else { +// TODO: logger.warning("Try to start P2P a second time") + symLog.log("Yikes❗️ Try to start P2P a second time") + return + } + transactionStarted = true + let timestamp = developerMode ? Timestamp.inSomeMinutes(expireDays > 20 ? (24*60) + : expireDays > 5 ? 60 : 3) + : Timestamp.inSomeDays(expireDays) + if let amountToSend { + let terms = PeerContractTerms(amount: amountToSend, + summary: summary, + purse_expiration: timestamp) + // TODO: user might choose baseURL + let response = try await model.initiatePeerPushDebitM(nil, terms: terms) + // will switch from WithdrawProgressView to TransactionDetailView + transactionId = response.transactionId + } else if let amountToReceive { + let terms = PeerContractTerms(amount: amountToReceive, + summary: summary, + purse_expiration: timestamp) + // TODO: user might choose baseURL + let response = try await model.initiatePeerPullCreditM(nil, terms: terms) + // will switch from WithdrawProgressView to TransactionDetailView + transactionId = response.transactionId + } else { fatalError() } + } catch { // TODO: error + symLog.log(error.localizedDescription) + } + } // task + } +} +// MARK: - +//struct SendNow_Previews: PreviewProvider { +// static var previews: some View { +// Group { +// SendDoneV(stack: CallStack("Preview"), +// amountToSend: try! Amount(fromString: LONGCURRENCY + ":4.8"), +// amountToReceive: nil, +// summary: "some subject/purpose", +// expireDays: 0) +// } +// } +//} diff --git a/TalerWallet1/Views/Peer2peer/SendPurpose.swift b/TalerWallet1/Views/Peer2peer/SendPurpose.swift index edb3cd9..3fc658c 100644 --- a/TalerWallet1/Views/Peer2peer/SendPurpose.swift +++ b/TalerWallet1/Views/Peer2peer/SendPurpose.swift @@ -19,6 +19,8 @@ struct SendPurpose: View { @AppStorage("iconOnly") var iconOnly: Bool = false let navTitle = String(localized: "NavTitle_Send_Subject", defaultValue: "Subject", comment: "NavTitle for entering the subject for Send-Money") + @State private var transactionStarted: Bool = false + private var value: String { // let mag = pow(10, formatter.maximumFractionDigits) // return formatter.string(for: Decimal(centsToTransfer) / mag) ?? "" @@ -81,11 +83,12 @@ struct SendPurpose: View { let emptyStr = "" let disabled = (expireDays == 0) || (summary.count < 1) // TODO: check amountAvailable NavigationLink(destination: LazyView { - SendDone(stack: stack.push(), - amountToSend: amount, - amountToReceive: nil, - summary: summary, - expireDays: expireDays) + SendDoneV(stack: stack.push(), + amountToSend: amount, + amountToReceive: nil, + summary: summary, + expireDays: expireDays, + transactionStarted: $transactionStarted) }) { Text("Send \(value) \(amountAvailable.currencyStr) now", comment: "first is value, second currencyString") // TODO: currency formatter } -- cgit v1.2.3