commit 4ff864731b5ea9ddc5a15f10ba50d217e75fa65b parent 8b2cfb109163dfddf41f92f4601054d01b25416c Author: Marc Stibane <marc@taler.net> Date: Thu, 21 Sep 2023 09:01:27 +0200 Project Structure Diffstat:
10 files changed, 262 insertions(+), 262 deletions(-)
diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj @@ -624,8 +624,6 @@ 4EB095242989CBFE0043A8A1 /* Settings */, 4ECB627E2A0BA4DA004ABBB7 /* Peer2peer */, 4EEC157129F7188B00D46A03 /* Sheets */, - 4EB0953B2989CBFE0043A8A1 /* WithdrawBankIntegrated */, - 4EB0952A2989CBFE0043A8A1 /* Payment */, 4EB095462989CBFE0043A8A1 /* HelperViews */, ); path = Views; @@ -761,6 +759,8 @@ 4EEC157929F9427F00D46A03 /* QRSheet.swift */, 4E753A072A0B6A5F002D9328 /* ShareSheet.swift */, 4EB095332989CBFE0043A8A1 /* URLSheet.swift */, + 4EB0953B2989CBFE0043A8A1 /* WithdrawBankIntegrated */, + 4EB0952A2989CBFE0043A8A1 /* Payment */, 4E3B4BBF2A41E64000CC88B8 /* P2P_Sheets */, ); path = Sheets; diff --git a/TalerWallet1/Views/Payment/PaymentView.swift b/TalerWallet1/Views/Payment/PaymentView.swift @@ -1,160 +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 PaymentView: View { - private let symLog = SymLogV(0) - let stack: CallStack - 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 { // runs on MainActor - 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)") - .accessibilityFont(.body) - 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") - .accessibilityFont(.body) - } - } - .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(stack: CallStack("Preview"), url: url, preparePayResult: details) - } -} diff --git a/TalerWallet1/Views/Payment/PayTemplateView.swift b/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift diff --git a/TalerWallet1/Views/Sheets/Payment/PaymentView.swift b/TalerWallet1/Views/Sheets/Payment/PaymentView.swift @@ -0,0 +1,160 @@ +/* + * 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 in a sheet. +struct PaymentView: View { + private let symLog = SymLogV(0) + let stack: CallStack + 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 { // runs on MainActor + 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)") + .accessibilityFont(.body) + 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") + .accessibilityFont(.body) + } + } + .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(stack: CallStack("Preview"), url: url, preparePayResult: details) + } +} diff --git a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptDone.swift b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift diff --git a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptView.swift b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptView.swift diff --git a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawProgressView.swift b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawProgressView.swift diff --git a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawTOSView.swift b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift diff --git a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift @@ -0,0 +1,100 @@ +/* + * This file is part of GNU Taler, ©2022-23 Taler Systems S.A. + * See LICENSE.md + */ +import SwiftUI +import taler_swift +import SymLog + +// Called either when scanning a QR code or tapping the provided link, both from the bank's website. +// We show the user the withdrawal details in a sheet - but first the ToS must be accepted. +// After the user confirmed the withdrawal, we show a button to return to the bank website to confirm there, too +struct WithdrawURIView: View { + private let symLog = SymLogV(0) + let stack: CallStack + let navTitle = String(localized: "Withdrawal") + + // the URL from the bank website + let url: URL + + @EnvironmentObject private var model: WalletModel + @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic + + // the exchange used for this withdrawal. + @State private var exchangeBaseUrl: String? = nil + @State private var withdrawalAmountDetails: WithdrawalAmountDetails? + + var body: some View { + let badURL = "Error in URL: \(url)" + VStack { + if let withdrawalAmountDetails, let exchangeBaseUrl { + List { + let raw = withdrawalAmountDetails.amountRaw + let effective = withdrawalAmountDetails.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: "Chosen amount to withdraw:"), + topAmount: raw, fee: fee, + bottomTitle: String(localized: "\(currency) to be withdrawn:"), + bottomAmount: effective, + large: false, pending: false, incoming: true, + baseURL: exchangeBaseUrl) + let someCoins = SomeCoins(details: withdrawalAmountDetails) + QuiteSomeCoins(someCoins: someCoins, shouldShowFee: false, + currency: raw.currencyStr, amountEffective: effective) + } + .listStyle(myListStyle.style).anyView + .navigationTitle(navTitle) + let tosAccepted = withdrawalAmountDetails.tosAccepted + if tosAccepted { + NavigationLink(destination: LazyView { + WithdrawAcceptDone(stack: stack.push(), + exchangeBaseUrl: exchangeBaseUrl, + url: url) + }) { + Text("Confirm Withdrawal") // SHEET_WITHDRAW_ACCEPT + }.buttonStyle(TalerButtonStyle(type: .prominent)) + .padding() + } else { + ToSButtonView(exchangeBaseUrl: exchangeBaseUrl, + viewID: SHEET_WITHDRAW_TOS, + p2p: false) + } + } else { + // Yikes no details or no baseURL +// WithdrawProgressView(message: url.host ?? badURL) +// .navigationTitle("Contacting Exchange") + } + } + .onAppear() { + symLog.log("onAppear") + DebugViewC.shared.setSheetID(SHEET_WITHDRAWAL) + } + .task { + do { // TODO: cancelled + symLog.log(".task") + let withdrawUriInfo = try await model.loadWithdrawalDetailsForUriM(url.absoluteString) + let amount = withdrawUriInfo.amount + if let baseURL = withdrawUriInfo.defaultExchangeBaseUrl { + exchangeBaseUrl = baseURL + } else if let first = withdrawUriInfo.possibleExchanges.first { + exchangeBaseUrl = first.exchangeBaseUrl + } + if let exchangeBaseUrl { + let details = try await model.loadWithdrawalDetailsForAmountM(exchangeBaseUrl, amount: amount) + withdrawalAmountDetails = details +// agePicker.setAges(ages: details?.ageRestrictionOptions) + } else { // TODO: error + symLog.log("no exchangeBaseUrl") + withdrawalAmountDetails = nil + } + } catch { // TODO: error + symLog.log(error.localizedDescription) + withdrawalAmountDetails = nil + } + } + } +} diff --git a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawURIView.swift b/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawURIView.swift @@ -1,100 +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 either when scanning a QR code or tapping the provided link, both from the bank's website. -// We show the user the withdrawal details - but first the ToS must be accepted. -// After the user confirmed the withdrawal, we show a button to return to the bank website to confirm there, too -struct WithdrawURIView: View { - private let symLog = SymLogV(0) - let stack: CallStack - let navTitle = String(localized: "Withdrawal") - - // the URL from the bank website - let url: URL - - @EnvironmentObject private var model: WalletModel - @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic - - // the exchange used for this withdrawal. - @State private var exchangeBaseUrl: String? = nil - @State private var withdrawalAmountDetails: WithdrawalAmountDetails? - - var body: some View { - let badURL = "Error in URL: \(url)" - VStack { - if let withdrawalAmountDetails, let exchangeBaseUrl { - List { - let raw = withdrawalAmountDetails.amountRaw - let effective = withdrawalAmountDetails.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: "Chosen amount to withdraw:"), - topAmount: raw, fee: fee, - bottomTitle: String(localized: "\(currency) to be withdrawn:"), - bottomAmount: effective, - large: false, pending: false, incoming: true, - baseURL: exchangeBaseUrl) - let someCoins = SomeCoins(details: withdrawalAmountDetails) - QuiteSomeCoins(someCoins: someCoins, shouldShowFee: false, - currency: raw.currencyStr, amountEffective: effective) - } - .listStyle(myListStyle.style).anyView - .navigationTitle(navTitle) - let tosAccepted = withdrawalAmountDetails.tosAccepted - if tosAccepted { - NavigationLink(destination: LazyView { - WithdrawAcceptDone(stack: stack.push(), - exchangeBaseUrl: exchangeBaseUrl, - url: url) - }) { - Text("Confirm Withdrawal") // SHEET_WITHDRAW_ACCEPT - }.buttonStyle(TalerButtonStyle(type: .prominent)) - .padding() - } else { - ToSButtonView(exchangeBaseUrl: exchangeBaseUrl, - viewID: SHEET_WITHDRAW_TOS, - p2p: false) - } - } else { - // Yikes no details or no baseURL -// WithdrawProgressView(message: url.host ?? badURL) -// .navigationTitle("Contacting Exchange") - } - } - .onAppear() { - symLog.log("onAppear") - DebugViewC.shared.setSheetID(SHEET_WITHDRAWAL) - } - .task { - do { // TODO: cancelled - symLog.log(".task") - let withdrawUriInfo = try await model.loadWithdrawalDetailsForUriM(url.absoluteString) - let amount = withdrawUriInfo.amount - if let baseURL = withdrawUriInfo.defaultExchangeBaseUrl { - exchangeBaseUrl = baseURL - } else if let first = withdrawUriInfo.possibleExchanges.first { - exchangeBaseUrl = first.exchangeBaseUrl - } - if let exchangeBaseUrl { - let details = try await model.loadWithdrawalDetailsForAmountM(exchangeBaseUrl, amount: amount) - withdrawalAmountDetails = details -// agePicker.setAges(ages: details?.ageRestrictionOptions) - } else { // TODO: error - symLog.log("no exchangeBaseUrl") - withdrawalAmountDetails = nil - } - } catch { // TODO: error - symLog.log(error.localizedDescription) - withdrawalAmountDetails = nil - } - } - } -}