taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

commit 78e7674feec48454bd9e1bf4791e14acc6b7fa4e
parent e6fc4a6c1383e0cd52d9c13df9e04cc5f14cb279
Author: Marc Stibane <marc@taler.net>
Date:   Mon, 26 Jun 2023 07:19:06 +0200

cleaned up P2P

Diffstat:
MTalerWallet.xcodeproj/project.pbxproj | 4++--
MTalerWallet1/Model/Model+P2P.swift | 238+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
MTalerWallet1/Views/Peer2peer/SendAmount.swift | 2+-
MTalerWallet1/Views/Peer2peer/SendPurpose.swift | 2+-
MTalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift | 2+-
MTalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift | 34++++++++++------------------------
MTalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift | 9+++------
7 files changed, 173 insertions(+), 118 deletions(-)

diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ @@ -276,8 +276,8 @@ isa = PBXGroup; children = ( 4E3B4BC02A41E6C200CC88B8 /* P2pReceiveURIView.swift */, - 4E578E932A4822D500F21F1C /* P2pPayURIView.swift */, 4E3B4BC22A42252300CC88B8 /* P2pAcceptDone.swift */, + 4E578E932A4822D500F21F1C /* P2pPayURIView.swift */, ); path = P2P_Sheets; sourceTree = "<group>"; diff --git a/TalerWallet1/Model/Model+P2P.swift b/TalerWallet1/Model/Model+P2P.swift @@ -8,21 +8,44 @@ import AnyCodable //import SymLog fileprivate let ASYNCDELAY: UInt = 0 //set e.g to 6 or 9 seconds for debugging -// MARK: - PeerContractTerms +// MARK: common structures struct PeerContractTerms: Codable { let amount: Amount let summary: String let purse_expiration: Timestamp } -// MARK: - -/// The result from CheckPeerPushDebit +// MARK: - PeerPushDebit +/// Check if initiating a peer push payment is possible, check fees +struct AmountResponse: Codable { + let effectiveAmount: Amount + let rawAmount: Amount +} +fileprivate struct GetMaxPeerPushAmount: WalletBackendFormattedRequest { + typealias Response = AmountResponse + func operation() -> String { return "GetMaxPeerPushAmount" } + func args() -> Args { return Args(currency: currency) } + + var currency: String + struct Args: Encodable { + var currency: String + } +} +extension WalletModel { + @MainActor + func getMaxPeerPushAmountM(_ currency: String) // M for MainActor + async throws -> AmountResponse { + let request = GetMaxPeerPushAmount(currency: currency) + let response = try await sendRequest(request, ASYNCDELAY) + return response + } +} // getMaxPeerPushAmountM +// - - - - - - struct CheckPeerPushDebitResponse: Codable { let exchangeBaseUrl: String? let amountRaw: Amount let amountEffective: Amount let maxExpirationDate: Timestamp? // TODO: limit expiration (30 days or 7 days) } -/// A request to check fees before sending coins to another wallet. fileprivate struct CheckPeerPushDebit: WalletBackendFormattedRequest { typealias Response = CheckPeerPushDebitResponse func operation() -> String { return "checkPeerPushDebit" } @@ -33,8 +56,50 @@ fileprivate struct CheckPeerPushDebit: WalletBackendFormattedRequest { var amount: Amount } } -// MARK: - -/// The result from CheckPeerPullCredit +extension WalletModel { + @MainActor + func checkPeerPushDebitM(_ amount: Amount) // M for MainActor + async throws -> CheckPeerPushDebitResponse { + let request = CheckPeerPushDebit(amount: amount) + let response = try await sendRequest(request, ASYNCDELAY) + return response + } +} // checkPeerPushDebitM +// - - - - - - +/// Initiate an outgoing peer push payment, send coins +struct InitiatePeerPushDebitResponse: Codable { + let contractPriv: String + let mergePriv: String + let pursePub: String + let exchangeBaseUrl: String + let talerUri: String + let transactionId: String +} +fileprivate struct InitiatePeerPushDebit: WalletBackendFormattedRequest { + typealias Response = InitiatePeerPushDebitResponse + func operation() -> String { return "initiatePeerPushDebit" } + func args() -> Args { return Args(exchangeBaseUrl: exchangeBaseUrl, + partialContractTerms: partialContractTerms) } + + var exchangeBaseUrl: String? + var partialContractTerms: PeerContractTerms + struct Args: Encodable { + var exchangeBaseUrl: String? + var partialContractTerms: PeerContractTerms + } +} +extension WalletModel { + @MainActor + func initiatePeerPushDebitM(_ baseURL: String?, terms: PeerContractTerms) // M for MainActor + async throws -> InitiatePeerPushDebitResponse { + let request = InitiatePeerPushDebit(exchangeBaseUrl: baseURL, + partialContractTerms: terms) + let response = try await sendRequest(request, ASYNCDELAY) + return response + } +} // initiatePeerPushDebitM +// MARK: - PeerPullCredit +/// Check fees before sending an invoice to another wallet struct CheckPeerPullCreditResponse: Codable { let scopeInfo: ScopeInfo? let exchangeBaseUrl: String? @@ -42,7 +107,6 @@ struct CheckPeerPullCreditResponse: Codable { let amountEffective: Amount var numCoins: Int? // Number of coins this amountEffective will create } -/// A request to check fees before invoicing another wallet. fileprivate struct CheckPeerPullCredit: WalletBackendFormattedRequest { typealias Response = CheckPeerPullCreditResponse func operation() -> String { return "checkPeerPullCredit" } @@ -57,22 +121,26 @@ fileprivate struct CheckPeerPullCredit: WalletBackendFormattedRequest { var amount: Amount } } -// MARK: - -/// The result from InitiatePeerPushDebit -struct InitiatePeerPushDebitResponse: Codable { - let contractPriv: String - let mergePriv: String - let pursePub: String - let exchangeBaseUrl: String +extension WalletModel { + @MainActor + func checkPeerPullCreditM(_ amount: Amount, exchangeBaseUrl: String?) // M for MainActor + async throws -> CheckPeerPullCreditResponse { + let request = CheckPeerPullCredit(exchangeBaseUrl: exchangeBaseUrl, amount: amount) + let response = try await sendRequest(request, ASYNCDELAY) + return response + } +} // checkPeerPullCreditM +// - - - - - - +/// Initiate an outgoing peer pull payment, send an invoice +struct InitiatePeerPullCreditResponse: Codable { let talerUri: String let transactionId: String } -/// A request to send coins to another wallet. -fileprivate struct InitiatePeerPushDebit: WalletBackendFormattedRequest { - typealias Response = InitiatePeerPushDebitResponse - func operation() -> String { return "initiatePeerPushDebit" } +fileprivate struct InitiatePeerPullCredit: WalletBackendFormattedRequest { + typealias Response = InitiatePeerPullCreditResponse + func operation() -> String { return "initiatePeerPullCredit" } func args() -> Args { return Args(exchangeBaseUrl: exchangeBaseUrl, - partialContractTerms: partialContractTerms) } + partialContractTerms: partialContractTerms) } var exchangeBaseUrl: String? var partialContractTerms: PeerContractTerms @@ -81,14 +149,25 @@ fileprivate struct InitiatePeerPushDebit: WalletBackendFormattedRequest { var partialContractTerms: PeerContractTerms } } -// MARK: - +extension WalletModel { + @MainActor + func initiatePeerPullCreditM(_ baseURL: String?, terms: PeerContractTerms) // M for MainActor + async throws -> InitiatePeerPullCreditResponse { + let request = InitiatePeerPullCredit(exchangeBaseUrl: baseURL, + partialContractTerms: terms) + let response = try await sendRequest(request, ASYNCDELAY) + return response + } +} // initiatePeerPullCreditM +// MARK: - PeerPushCredit +/// Prepare an incoming peer push payment, receive coins struct PreparePeerPushCreditResponse: Codable { let contractTerms: PeerContractTerms let amountRaw: Amount let amountEffective: Amount - let transactionId: String // after scanning the transaction already is created in the local DB + // the dialog transaction is already created in the local DB - must either accept or delete + let transactionId: String } -/// A request to receive coins from another wallet. fileprivate struct PreparePeerPushCredit: WalletBackendFormattedRequest { typealias Response = PreparePeerPushCreditResponse func operation() -> String { return "preparePeerPushCredit" } @@ -99,10 +178,20 @@ fileprivate struct PreparePeerPushCredit: WalletBackendFormattedRequest { var talerUri: String } } -// MARK: - -fileprivate struct ConfirmPeerPushCredit: WalletBackendFormattedRequest { - struct Response: Decodable {} - func operation() -> String { return "confirmPeerPushCredit" } +extension WalletModel { + @MainActor + func preparePeerPushCreditM(_ talerUri: String) // M for MainActor + async throws -> PreparePeerPushCreditResponse { + let request = PreparePeerPushCredit(talerUri: talerUri) + let response = try await sendRequest(request, ASYNCDELAY) + return response + } +} // preparePeerPushCreditM +// - - - - - - +/// Accept an incoming peer push payment +fileprivate struct AcceptPeerPushCredit: WalletBackendFormattedRequest { + struct Response: Decodable {} // no result - getting no error back means success + func operation() -> String { return "confirmPeerPushCredit" } // should be "acceptPeerPushCredit" func args() -> Args { return Args(transactionId: transactionId) } var transactionId: String @@ -110,78 +199,61 @@ fileprivate struct ConfirmPeerPushCredit: WalletBackendFormattedRequest { var transactionId: String } } -// MARK: - -/// The result from InitiatePeerPullCredit -struct InitiatePeerPullCreditResponse: Codable { - let talerUri: String - let transactionId: String -} -/// A request to send a payment request to another wallet. -fileprivate struct InitiatePeerPullCredit: WalletBackendFormattedRequest { - typealias Response = InitiatePeerPullCreditResponse - func operation() -> String { return "initiatePeerPullCredit" } - func args() -> Args { return Args(exchangeBaseUrl: exchangeBaseUrl, - partialContractTerms: partialContractTerms) } - - var exchangeBaseUrl: String? - var partialContractTerms: PeerContractTerms - struct Args: Encodable { - var exchangeBaseUrl: String? - var partialContractTerms: PeerContractTerms - } -} -// MARK: - extension WalletModel { - /// query exchange for fees (sending coins). Networking involved - @MainActor - func checkPeerPushDebitM(_ amount: Amount) // M for MainActor - async throws -> CheckPeerPushDebitResponse { - let request = CheckPeerPushDebit(amount: amount) - let response = try await sendRequest(request, ASYNCDELAY) - return response - } - /// generate peer-push. Networking involved @MainActor - func initiatePeerPushDebitM(_ baseURL: String?, terms: PeerContractTerms) // M for MainActor - async throws -> InitiatePeerPushDebitResponse { - let request = InitiatePeerPushDebit(exchangeBaseUrl: baseURL, - partialContractTerms: terms) + func acceptPeerPushCreditM(_ transactionId: String) // M for MainActor + async throws -> Decodable { + let request = AcceptPeerPushCredit(transactionId: transactionId) let response = try await sendRequest(request, ASYNCDELAY) return response } +} // acceptPeerPushCreditM +// MARK: - PeerPullDebit +/// Prepare an incoming peer push invoice, pay coins +struct PreparePeerPullDebitResponse: Codable { + let contractTerms: PeerContractTerms + let amountRaw: Amount + let amountEffective: Amount + // the dialog transaction is already created in the local DB - must either accept or delete + let transactionId: String +} +fileprivate struct PreparePeerPullDebit: WalletBackendFormattedRequest { + typealias Response = PreparePeerPullDebitResponse + func operation() -> String { return "preparePeerPullDebit" } + func args() -> Args { return Args(talerUri: talerUri) } - /// query exchange for fees (invoice coins). Networking involved - @MainActor - func checkPeerPullCreditM(_ amount: Amount, exchangeBaseUrl: String?) // M for MainActor - async throws -> CheckPeerPullCreditResponse { - let request = CheckPeerPullCredit(exchangeBaseUrl: exchangeBaseUrl, amount: amount) - let response = try await sendRequest(request, ASYNCDELAY) - return response + var talerUri: String + struct Args: Encodable { + var talerUri: String } - /// generate peer-pull. Networking involved +} +extension WalletModel { @MainActor - func initiatePeerPullCreditM(_ baseURL: String?, terms: PeerContractTerms) // M for MainActor - async throws -> InitiatePeerPullCreditResponse { - let request = InitiatePeerPullCredit(exchangeBaseUrl: baseURL, - partialContractTerms: terms) + func preparePeerPullDebitM(_ talerUri: String) // M for MainActor + async throws -> PreparePeerPullDebitResponse { + let request = PreparePeerPullDebit(talerUri: talerUri) let response = try await sendRequest(request, ASYNCDELAY) return response } +} // preparePeerPullDebitM +// - - - - - - +/// Confirm incoming peer push invoice and pay +fileprivate struct ConfirmPeerPullDebit: WalletBackendFormattedRequest { + struct Response: Decodable {} // no result - getting no error back means success + func operation() -> String { return "confirmPeerPullDebit" } + func args() -> Args { return Args(transactionId: transactionId) } - /// prepare peer-pull. Networking involved - @MainActor - func preparePeerPushCreditM(_ talerUri: String) // M for MainActor - async throws -> PreparePeerPushCreditResponse { - let request = PreparePeerPushCredit(talerUri: talerUri) - let response = try await sendRequest(request, ASYNCDELAY) - return response + var transactionId: String + struct Args: Encodable { + var transactionId: String } - /// confirm peer-pull. Networking involved +} +extension WalletModel { @MainActor - func confirmPeerPushCreditM(_ transactionId: String) // M for MainActor + func confirmPeerPullDebitM(_ transactionId: String) // M for MainActor async throws -> Decodable { - let request = ConfirmPeerPushCredit(transactionId: transactionId) + let request = ConfirmPeerPullDebit(transactionId: transactionId) let response = try await sendRequest(request, ASYNCDELAY) return response } -} +} // confirmPeerPullDebitM diff --git a/TalerWallet1/Views/Peer2peer/SendAmount.swift b/TalerWallet1/Views/Peer2peer/SendAmount.swift @@ -10,7 +10,7 @@ import SymLog struct SendAmount: View { private let symLog = SymLogV() - let amountAvailable: Amount + let amountAvailable: Amount // TODO: GetMaxPeerPushAmount @Binding var centsToTransfer: UInt64 @Binding var summary: String diff --git a/TalerWallet1/Views/Peer2peer/SendPurpose.swift b/TalerWallet1/Views/Peer2peer/SendPurpose.swift @@ -93,7 +93,7 @@ struct SendPurpose: View { .task { symLog.log(".task") do { -// peerPushCheck = try await model.checkPeerPushDebitM(amount) + } catch { // TODO: error symLog.log(error.localizedDescription) } diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift @@ -44,7 +44,7 @@ struct P2pAcceptDone: View { DebugViewC.shared.setSheetID(SHEET_RCV_P2P_ACCEPT) }.task { do { - _ = try await model.confirmPeerPushCreditM(transactionId) + _ = try await model.acceptPeerPushCreditM(transactionId) finished = true playSound(success: true) } catch { // TODO: error diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift @@ -12,14 +12,14 @@ struct P2pPayURIView: View { private let symLog = SymLogV() let navTitle = String(localized: "Pay P2P Invoice") - // the URL from the bank website + // the scanned URL let url: URL + @EnvironmentObject private var model: WalletModel @State private var peerPullDebitResponse: PreparePeerPullDebitResponse? var body: some View { - let badURL = "Error in URL: \(url)" VStack { if let peerPullDebitResponse { List { @@ -27,35 +27,21 @@ struct P2pPayURIView: View { let effective = peerPullDebitResponse.amountEffective let currency = raw.currencyStr let fee = try! Amount.diff(raw, effective) - let outColor = WalletColors().transactionColor(false) - let inColor = WalletColors().transactionColor(true) - - ThreeAmountsView(topTitle: String(localized: "Amount to receive:"), + ThreeAmountsView(topTitle: String(localized: "Amount to pay:"), topAmount: raw, fee: fee, - bottomTitle: String(localized: "\(currency) to be obtained:"), + bottomTitle: String(localized: "\(currency) to be spent:"), bottomAmount: effective, - large: false, pending: false, incoming: true, + large: false, pending: false, incoming: false, baseURL: nil) } .navigationTitle(navTitle) - let tosAccepted = true // TODO: https://bugs.gnunet.org/view.php?id=7869 - if tosAccepted { - NavigationLink(destination: LazyView { + + NavigationLink(destination: LazyView { P2pAcceptDone(transactionId: peerPullDebitResponse.transactionId) }) { - Text("Confirm Withdrawal") // SHEET_WITHDRAW_ACCEPT + Text("Confirm Payment", comment:"pay P2P invoice") // SHEET_PAY_P2P }.buttonStyle(TalerButtonStyle(type: .prominent)) .padding() - } else { - NavigationLink(destination: LazyView { - WithdrawTOSView(exchangeBaseUrl: nil, - viewID: SHEET_RCV_P2P_TOS, - acceptAction: nil) // pop back to here - }) { - Text("Check Terms of Service") - }.buttonStyle(TalerButtonStyle(type: .prominent)) - .padding() - } } else { // Yikes no details or no baseURL // WithdrawProgressView(message: url.host ?? badURL) @@ -64,12 +50,12 @@ struct P2pPayURIView: View { } .onAppear() { symLog.log("onAppear") - DebugViewC.shared.setSheetID(SHEET_RCV_P2P) + DebugViewC.shared.setSheetID(SHEET_PAY_P2P) } .task { do { // TODO: cancelled symLog.log(".task") - let ppDebitResponse = try await model.preparePeerPushCreditM(url.absoluteString) + let ppDebitResponse = try await model.preparePeerPullDebitM(url.absoluteString) peerPullDebitResponse = ppDebitResponse } catch { // TODO: error symLog.log(error.localizedDescription) diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift @@ -12,14 +12,14 @@ struct P2pReceiveURIView: View { private let symLog = SymLogV() let navTitle = String(localized: "Accept P2P Receive") - // the URL from the bank website + // the scanned URL let url: URL + @EnvironmentObject private var model: WalletModel @State private var peerPushCreditResponse: PreparePeerPushCreditResponse? var body: some View { - let badURL = "Error in URL: \(url)" VStack { if let peerPushCreditResponse { List { @@ -27,9 +27,6 @@ struct P2pReceiveURIView: View { let effective = peerPushCreditResponse.amountEffective let currency = raw.currencyStr let fee = try! Amount.diff(raw, effective) - let outColor = WalletColors().transactionColor(false) - let inColor = WalletColors().transactionColor(true) - ThreeAmountsView(topTitle: String(localized: "Amount to receive:"), topAmount: raw, fee: fee, bottomTitle: String(localized: "\(currency) to be obtained:"), @@ -43,7 +40,7 @@ struct P2pReceiveURIView: View { NavigationLink(destination: LazyView { P2pAcceptDone(transactionId: peerPushCreditResponse.transactionId) }) { - Text("Confirm Withdrawal") // SHEET_WITHDRAW_ACCEPT + Text("Accept Withdrawal") // SHEET_WITHDRAW_ACCEPT }.buttonStyle(TalerButtonStyle(type: .prominent)) .padding() } else {