taler-ios

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

commit f6a838560b5d7fc6f80b74b33c4baf099c30e331
parent dbf44d68d7673acfc9f7d9a688c0bc2871614012
Author: Marc Stibane <marc@taler.net>
Date:   Tue,  8 Aug 2023 11:54:49 +0200

PaymentView

Diffstat:
MTalerWallet.xcodeproj/project.pbxproj | 8++++----
MTalerWallet1/Views/HelperViews/SelectDays.swift | 12++++++------
DTalerWallet1/Views/Payment/DeleteMe.swift | 79-------------------------------------------------------------------------------
DTalerWallet1/Views/Payment/PaymentURIView.swift | 153-------------------------------------------------------------------------------
ATalerWallet1/Views/Payment/PaymentView.swift | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MTalerWallet1/Views/Peer2peer/PaymentPurpose.swift | 24++++++++++++------------
MTalerWallet1/Views/Peer2peer/SendPurpose.swift | 24++++++++++++------------
MTalerWallet1/Views/Sheets/URLSheet.swift | 2+-
8 files changed, 192 insertions(+), 267 deletions(-)

diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj @@ -68,7 +68,7 @@ 4EB095502989CBFE0043A8A1 /* SettingsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB095262989CBFE0043A8A1 /* SettingsItem.swift */; }; 4EB095522989CBFE0043A8A1 /* ExchangeListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB095292989CBFE0043A8A1 /* ExchangeListView.swift */; }; 4EB095542989CBFE0043A8A1 /* Model+Payment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0952C2989CBFE0043A8A1 /* Model+Payment.swift */; }; - 4EB095552989CBFE0043A8A1 /* PaymentURIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0952D2989CBFE0043A8A1 /* PaymentURIView.swift */; }; + 4EB095552989CBFE0043A8A1 /* PaymentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0952D2989CBFE0043A8A1 /* PaymentView.swift */; }; 4EB095562989CBFE0043A8A1 /* TransactionsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0952F2989CBFE0043A8A1 /* TransactionsListView.swift */; }; 4EB095572989CBFE0043A8A1 /* TransactionRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB095302989CBFE0043A8A1 /* TransactionRowView.swift */; }; 4EB095582989CBFE0043A8A1 /* TransactionDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB095312989CBFE0043A8A1 /* TransactionDetailView.swift */; }; @@ -206,7 +206,7 @@ 4EB095262989CBFE0043A8A1 /* SettingsItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsItem.swift; sourceTree = "<group>"; }; 4EB095292989CBFE0043A8A1 /* ExchangeListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExchangeListView.swift; sourceTree = "<group>"; }; 4EB0952C2989CBFE0043A8A1 /* Model+Payment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Model+Payment.swift"; sourceTree = "<group>"; }; - 4EB0952D2989CBFE0043A8A1 /* PaymentURIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaymentURIView.swift; sourceTree = "<group>"; }; + 4EB0952D2989CBFE0043A8A1 /* PaymentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaymentView.swift; sourceTree = "<group>"; }; 4EB0952F2989CBFE0043A8A1 /* TransactionsListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionsListView.swift; sourceTree = "<group>"; }; 4EB095302989CBFE0043A8A1 /* TransactionRowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionRowView.swift; sourceTree = "<group>"; }; 4EB095312989CBFE0043A8A1 /* TransactionDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionDetailView.swift; sourceTree = "<group>"; }; @@ -451,7 +451,7 @@ 4EB0952A2989CBFE0043A8A1 /* Payment */ = { isa = PBXGroup; children = ( - 4EB0952D2989CBFE0043A8A1 /* PaymentURIView.swift */, + 4EB0952D2989CBFE0043A8A1 /* PaymentView.swift */, ); path = Payment; sourceTree = "<group>"; @@ -775,7 +775,7 @@ 4E578E942A4822D500F21F1C /* P2pPayURIView.swift in Sources */, 4EB095542989CBFE0043A8A1 /* Model+Payment.swift in Sources */, 4EB0954F2989CBFE0043A8A1 /* SettingsView.swift in Sources */, - 4EB095552989CBFE0043A8A1 /* PaymentURIView.swift in Sources */, + 4EB095552989CBFE0043A8A1 /* PaymentView.swift in Sources */, 4EB095612989CBFE0043A8A1 /* WithdrawURIView.swift in Sources */, 4EF840A72A0B85F400EE0D47 /* CopyShare.swift in Sources */, 4EB094ED298979620043A8A1 /* TalerWallet1App.swift in Sources */, diff --git a/TalerWallet1/Views/HelperViews/SelectDays.swift b/TalerWallet1/Views/HelperViews/SelectDays.swift @@ -65,9 +65,9 @@ struct SelectDays: View { } } -struct SelectDays_Previews: PreviewProvider { - static var previews: some View { - @State var expireDays: UInt = 1 - SelectDays(selected: $expireDays, maxExpiration: 20) - } -} +//struct SelectDays_Previews: PreviewProvider { +// static var previews: some View { +// @State var expireDays: UInt = 1 +// SelectDays(selected: $expireDays, maxExpiration: 20) +// } +//} diff --git a/TalerWallet1/Views/Payment/DeleteMe.swift b/TalerWallet1/Views/Payment/DeleteMe.swift @@ -1,79 +0,0 @@ -/* - * This file is part of GNU Taler, ©2022-23 Taler Systems S.A. - * See LICENSE.md - */ -import SwiftUI -import taler_swift -import AVFoundation -import SymLog - -struct PaymentAcceptView: View { - private let symLog = SymLogV() - - let detailsForUri: PaymentDetailsForUri - let acceptAction: () -> Void - - let navTitle = String(localized: "Accept Payment") - - @State private var disabled = false - var body: some View { - Group { - let raw = detailsForUri.amountRaw - let effective = detailsForUri.amountEffective - let currency = raw.currencyStr - let fee = try! Amount.diff(raw, effective) // TODO: different currencies - ThreeAmountsView(topTitle: String(localized: "Amount to pay:"), - topAmount: raw, fee: fee, - bottomTitle: String(localized: "\(currency) to be spent:"), - bottomAmount: effective, - large: true, pending: false, incoming: false, - baseURL: detailsForUri.contractTerms.exchanges.first?.url) - // TODO: payment: popup with all possible exchanges, check fees - .safeAreaInset(edge: .bottom) { - Button(String(localized: "Accept"), action: acceptAction) - .buttonStyle(TalerButtonStyle(type: .prominent)) - .padding(.horizontal) - } - } - .navigationTitle(navTitle) - } -} -// MARK: - -struct PaymentAccept_Previews: PreviewProvider { - static var previews: some View { - let merchant = Merchant(name: "Merchant") - let extra = Extra(articleName: "articleName") - let product = Product(description: "description") - let terms = ContractTerms(amount: try! Amount(fromString: LONGCURRENCY + ":2.2"), - maxFee: try! Amount(fromString: LONGCURRENCY + ":0.2"), - maxWireFee: try! Amount(fromString: LONGCURRENCY + ":0.2"), - merchant: merchant, - extra: extra, - summary: "summary", - timestamp: Timestamp.now(), - payDeadline: Timestamp.tomorrow(), - refundDeadline: Timestamp.tomorrow(), - wireTransferDeadline: Timestamp.tomorrow(), - merchantBaseURL: "merchantBaseURL", - fulfillmentURL: "fulfillmentURL", - publicReorderURL: "publicReorderURL", - auditors: [], - exchanges: [], - orderID: "orderID", - nonce: "nonce", - merchantPub: "merchantPub", - products: [product], - hWire: "hWire", - wireMethod: "wireMethod", - wireFeeAmortization: 0) - let details = PaymentDetailsForUri( - amountRaw: try! Amount(fromString: LONGCURRENCY + ":2.2"), - amountEffective: try! Amount(fromString: LONGCURRENCY + ":2.4"), - noncePriv: "noncePriv", - proposalId: "proposalId", - contractTerms: terms, - contractTermsHash: "termsHash" - ) - PaymentAcceptView(detailsForUri: details, acceptAction: {}) - } -} diff --git a/TalerWallet1/Views/Payment/PaymentURIView.swift b/TalerWallet1/Views/Payment/PaymentURIView.swift @@ -1,153 +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 - -// Will be called either by the user scanning a QR code or tapping the provided link, -// both from the shop's website. We show the payment details -struct PaymentURIView: View { - private let symLog = SymLogV() - let navTitle = String(localized: "Confirm Payment", comment:"pay merchant") - - @EnvironmentObject private var controller: Controller - @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic - - // the scanned URL - let url: URL - - @EnvironmentObject private var model: WalletModel - - func acceptAction(preparePayResult: PreparePayResult) { - Task { - do { - let confirmPayResult = try await model.confirmPayM(preparePayResult.transactionId) -// symLog.log(confirmPayResult as Any) - if confirmPayResult.type != "done" { - controller.playSound(0) - // TODO: show error - } - } catch { - controller.playSound(0) - // TODO: error - symLog.log(error.localizedDescription) - } - dismissTop() - } - } - - @State var preparePayResult: PreparePayResult? = nil - - var body: some View { - if let preparePayResult { - let effective = preparePayResult.amountEffective - List { - let baseURL = preparePayResult.contractTerms.exchanges.first?.url - let raw = preparePayResult.amountRaw - let currency = raw.currencyStr - let topTitle = String(localized: "Amount to pay:") - if let effective { - // TODO: already paid - let fee = try! Amount.diff(raw, effective) // TODO: different currencies - ThreeAmountsView(topTitle: topTitle, - topAmount: raw, fee: fee, - bottomTitle: String(localized: "\(currency) to be spent:"), - bottomAmount: effective, - large: false, pending: false, incoming: false, - baseURL: baseURL) - // TODO: payment: popup with all possible exchanges, check fees - } else if let balanceDetails = preparePayResult.balanceDetails { // Insufficient - Text("You don't have enough \(currency)") - ThreeAmountsView(topTitle: topTitle, - topAmount: raw, fee: nil, - bottomTitle: String(localized: "\(currency) available:"), - bottomAmount: balanceDetails.balanceAvailable, - large: false, pending: false, incoming: false, - baseURL: baseURL) - } else { - // TODO: Error - neither effective nor balanceDetails - Text("Error") - } - } - .listStyle(myListStyle.style).anyView - .safeAreaInset(edge: .bottom) { - if let effective { - Button(navTitle, action: { acceptAction(preparePayResult: preparePayResult) }) - .buttonStyle(TalerButtonStyle(type: .prominent)) - .padding(.horizontal) - } else { - Button("Cancel", action: { dismissTop() }) - .buttonStyle(TalerButtonStyle(type: .bordered)) - .padding(.horizontal) - } - } - .navigationTitle(navTitle) - } else { - let badURL = "Error in Link: \(url)" - WithdrawProgressView(message: url.host ?? badURL) - .navigationTitle("Find Exchange") - .task { - do { - symLog.log(".task") - let result = try await model.preparePayForUriM(url.absoluteString) - preparePayResult = result - } catch { // TODO: error - symLog.log(error.localizedDescription) - } - } - } - } -} -// MARK: - -struct PaymentURIView_Previews: PreviewProvider { - static var previews: some View { - let merchant = Merchant(name: "Merchant") - let extra = Extra(articleName: "articleName") - let product = Product(description: "description") - let terms = MerchantContractTerms(hWire: "hWire", - wireMethod: "wireMethod", - summary: "summary", - summaryI18n: nil, - nonce: "nonce", - amount: try! Amount(fromString: LONGCURRENCY + ":2.2"), - payDeadline: Timestamp.tomorrow(), - maxFee: try! Amount(fromString: LONGCURRENCY + ":0.2"), - merchant: merchant, - merchantPub: "merchantPub", - deliveryDate: nil, - deliveryLocation: nil, - exchanges: [], - products: [product], - refundDeadline: Timestamp.tomorrow(), - wireTransferDeadline: Timestamp.tomorrow(), - timestamp: Timestamp.now(), - orderID: "orderID", - merchantBaseURL: "merchantBaseURL", - fulfillmentURL: "fulfillmentURL", - publicReorderURL: "publicReorderURL", - fulfillmentMessage: nil, - fulfillmentMessageI18n: nil, - wireFeeAmortization: 0, - maxWireFee: try! Amount(fromString: LONGCURRENCY + ":0.2"), - minimumAge: nil, - extra: extra -// auditors: [], - ) - let details = PreparePayResult( - status: PreparePayResultType.paymentPossible, - transactionId: "txn:payment:012345", - contractTerms: terms, - contractTermsHash: "termsHash", - amountRaw: try! Amount(fromString: LONGCURRENCY + ":2.2"), - amountEffective: try! Amount(fromString: LONGCURRENCY + ":2.4"), - balanceDetails: nil, - paid: nil -// , talerUri: "talerURI" - ) - let url = URL(string: "taler://pay/some_amount")! - - PaymentURIView(url: url, preparePayResult: details) - } -} diff --git a/TalerWallet1/Views/Payment/PaymentView.swift b/TalerWallet1/Views/Payment/PaymentView.swift @@ -0,0 +1,157 @@ +/* + * This file is part of GNU Taler, ©2022-23 Taler Systems S.A. + * See LICENSE.md + */ +import SwiftUI +import taler_swift +import SymLog + +// Will be called either by the user scanning a QR code or tapping the provided link, +// both from the shop's website. We show the payment details +struct PaymentView: View { + private let symLog = SymLogV() + let navTitle = String(localized: "Confirm Payment", comment:"pay merchant") + + @EnvironmentObject private var controller: Controller + @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic + + // the scanned URL + let url: URL + + @EnvironmentObject private var model: WalletModel + + func acceptAction(preparePayResult: PreparePayResult) { + Task { + do { + let confirmPayResult = try await model.confirmPayM(preparePayResult.transactionId) +// symLog.log(confirmPayResult as Any) + if confirmPayResult.type != "done" { + controller.playSound(0) + // TODO: show error + } + } catch { + controller.playSound(0) + // TODO: error + symLog.log(error.localizedDescription) + } + dismissTop() + } + } + + @State var preparePayResult: PreparePayResult? = nil + + var body: some View { + if let preparePayResult { + let effective = preparePayResult.amountEffective + List { + let baseURL = preparePayResult.contractTerms.exchanges.first?.url + let raw = preparePayResult.amountRaw + let currency = raw.currencyStr + let topTitle = String(localized: "Amount to pay:") + if let effective { + // TODO: already paid + let fee = try! Amount.diff(raw, effective) // TODO: different currencies + ThreeAmountsView(topTitle: topTitle, + topAmount: raw, fee: fee, + bottomTitle: String(localized: "\(currency) to be spent:"), + bottomAmount: effective, + large: false, pending: false, incoming: false, + baseURL: baseURL) + // TODO: payment: popup with all possible exchanges, check fees + } else if let balanceDetails = preparePayResult.balanceDetails { // Insufficient + Text("You don't have enough \(currency)") + ThreeAmountsView(topTitle: topTitle, + topAmount: raw, fee: nil, + bottomTitle: String(localized: "\(currency) available:"), + bottomAmount: balanceDetails.balanceAvailable, + large: false, pending: false, incoming: false, + baseURL: baseURL) + } else { + // TODO: Error - neither effective nor balanceDetails + Text("Error") + } + } + .listStyle(myListStyle.style).anyView + .safeAreaInset(edge: .bottom) { + if let effective { + Button(navTitle, action: { acceptAction(preparePayResult: preparePayResult) }) + .buttonStyle(TalerButtonStyle(type: .prominent)) + .padding(.horizontal) + } else { + Button("Cancel", action: { dismissTop() }) + .buttonStyle(TalerButtonStyle(type: .bordered)) + .padding(.horizontal) + } + } + .navigationTitle(navTitle) + .onAppear() { + symLog.log("onAppear") + DebugViewC.shared.setSheetID(SHEET_PAYMENT) + } + } else { + let badURL = "Error in Link: \(url)" + WithdrawProgressView(message: url.host ?? badURL) + .navigationTitle("Find Exchange") + .task { + do { + symLog.log(".task") + let result = try await model.preparePayForUriM(url.absoluteString) + preparePayResult = result + } catch { // TODO: error + symLog.log(error.localizedDescription) + } + } + } + } +} +// MARK: - +struct PaymentURIView_Previews: PreviewProvider { + static var previews: some View { + let merchant = Merchant(name: "Merchant") + let extra = Extra(articleName: "articleName") + let product = Product(description: "description") + let terms = MerchantContractTerms(hWire: "hWire", + wireMethod: "wireMethod", + summary: "summary", + summaryI18n: nil, + nonce: "nonce", + amount: try! Amount(fromString: LONGCURRENCY + ":2.2"), + payDeadline: Timestamp.tomorrow(), + maxFee: try! Amount(fromString: LONGCURRENCY + ":0.2"), + merchant: merchant, + merchantPub: "merchantPub", + deliveryDate: nil, + deliveryLocation: nil, + exchanges: [], + products: [product], + refundDeadline: Timestamp.tomorrow(), + wireTransferDeadline: Timestamp.tomorrow(), + timestamp: Timestamp.now(), + orderID: "orderID", + merchantBaseURL: "merchantBaseURL", + fulfillmentURL: "fulfillmentURL", + publicReorderURL: "publicReorderURL", + fulfillmentMessage: nil, + fulfillmentMessageI18n: nil, + wireFeeAmortization: 0, + maxWireFee: try! Amount(fromString: LONGCURRENCY + ":0.2"), + minimumAge: nil, + extra: extra +// auditors: [], + ) + let details = PreparePayResult( + status: PreparePayResultType.paymentPossible, + transactionId: "txn:payment:012345", + contractTerms: terms, + contractTermsHash: "termsHash", + amountRaw: try! Amount(fromString: LONGCURRENCY + ":2.2"), + amountEffective: try! Amount(fromString: LONGCURRENCY + ":2.4"), + balanceDetails: nil, + paid: nil +// , talerUri: "talerURI" + ) + let url = URL(string: "taler://pay/some_amount")! + + PaymentView(url: url, preparePayResult: details) + } +} diff --git a/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift b/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift @@ -95,17 +95,17 @@ struct PaymentPurpose: View { } // MARK: - #if DEBUG -struct PaymentPurpose_Previews: PreviewProvider { - static var previews: some View { - let scopeInfo = ScopeInfo(type: ScopeInfo.ScopeInfoType.exchange, exchangeBaseUrl: DEMOEXCHANGE, currency: LONGCURRENCY) - @State var summary: String = "pUrPoSe" - @State var expireDays: UInt = 0 - PaymentPurpose(scopeInfo: scopeInfo, - centsToTransfer: 5, - fee: "fee", - summary: $summary, - expireDays: $expireDays) +//struct PaymentPurpose_Previews: PreviewProvider { +// static var previews: some View { +// let scopeInfo = ScopeInfo(type: ScopeInfo.ScopeInfoType.exchange, exchangeBaseUrl: DEMOEXCHANGE, currency: LONGCURRENCY) +// @State var summary: String = "pUrPoSe" +// @State var expireDays: UInt = 0 +// PaymentPurpose(scopeInfo: scopeInfo, +// centsToTransfer: 5, +// fee: "fee", +// summary: $summary, +// expireDays: $expireDays) // { print("deactivateAction") } - } -} +// } +//} #endif diff --git a/TalerWallet1/Views/Peer2peer/SendPurpose.swift b/TalerWallet1/Views/Peer2peer/SendPurpose.swift @@ -103,17 +103,17 @@ struct SendPurpose: View { } // MARK: - #if DEBUG -struct SendPurpose_Previews: PreviewProvider { - static var previews: some View { - @State var summary: String = "" - @State var expireDays: UInt = 0 - let amount = Amount(currency: LONGCURRENCY, integer: 10, fraction: 0) - SendPurpose(amountAvailable: amount, - centsToTransfer: 543, - fee: "0,43", - summary: $summary, - expireDays: $expireDays) +//struct SendPurpose_Previews: PreviewProvider { +// static var previews: some View { +// @State var summary: String = "" +// @State var expireDays: UInt = 0 +// let amount = Amount(currency: LONGCURRENCY, integer: 10, fraction: 0) +// SendPurpose(amountAvailable: amount, +// centsToTransfer: 543, +// fee: "0,43", +// summary: $summary, +// expireDays: $expireDays) // { print("deactivateAction") } - } -} +// } +//} #endif diff --git a/TalerWallet1/Views/Sheets/URLSheet.swift b/TalerWallet1/Views/Sheets/URLSheet.swift @@ -19,7 +19,7 @@ struct URLSheet: View { case .withdraw: WithdrawURIView(url: urlToOpen) case .pay: - PaymentURIView(url: urlToOpen) + PaymentView(url: urlToOpen) case .payPull: P2pPayURIView(url: urlToOpen) case .payPush: