commit a06aff28aee937ca5a5806bcf589c9713f91a4e9
parent 883f4b0237e6269587a4e982aede5b03e83d5ed0
Author: Marc Stibane <marc@taler.net>
Date: Fri, 7 Aug 2026 08:52:07 +0200
PayTemplateScan
Diffstat:
3 files changed, 215 insertions(+), 215 deletions(-)
diff --git a/TalerWallet1/Views/Sheets/Payment/PayTemplateScan.swift b/TalerWallet1/Views/Sheets/Payment/PayTemplateScan.swift
@@ -0,0 +1,214 @@
+/*
+ * This file is part of GNU Taler, ©2022-26 Taler Systems S.A.
+ * See LICENSE.md
+ */
+/**
+ * @author Marc Stibane
+ */
+import SwiftUI
+import taler_swift
+import SymLog
+
+// Will be called either by the user scanning a <pay-template> QR code or tapping the provided link,
+// both from the shop's website - or even from a printed QR code.
+// We check whether amount and/or summary is editable, and finally go to PaymentView
+struct PayTemplateScan: View {
+ private let symLog = SymLogV(0)
+ let stack: CallStack
+
+ // the scanned URL
+ let url: URL
+
+ @EnvironmentObject private var controller: Controller
+ @EnvironmentObject private var model: WalletModel
+ @AppStorage("minimalistic") var minimalistic: Bool = false
+ @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
+
+ let navTitle = String(localized: "Custom Amount", comment:"pay merchant")
+
+// @State private var insufficient = false
+// @State private var preparePayResult: PreparePayResult? = nil
+ @State private var templateContract: TemplateContractDetails? = nil
+ @State private var amountIsEditable = false
+ @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING) // Update currency when used
+ @State private var amountShortcut = Amount.zero(currency: EMPTYSTRING) // Update currency when used
+ @State private var amountLastUsed = Amount.zero(currency: EMPTYSTRING) // Update currency when used
+ @State private var amountAvailable = Amount.zero(currency: EMPTYSTRING) // TODO: set correct available amount (like in SendAmountV)
+ @State private var shortcutSelected = false
+ @State private var buttonSelected1 = false
+ @State private var buttonSelected2 = false
+ @State private var summaryIsEditable = false
+ @State private var isPaivana = false
+ @State private var summary: String = EMPTYSTRING // templateParam
+ @State private var scope: ScopeInfo? = nil
+
+// @State private var feeAmount: Amount? = nil
+// @State private var feeStr: String = EMPTYSTRING
+
+ private func shortcutAction(_ shortcut: Amount) {
+ amountShortcut = shortcut
+ shortcutSelected = true
+ }
+ private func buttonAction1() {
+ buttonSelected1 = true
+ }
+ private func buttonAction2() {
+ buttonSelected2 = true
+ }
+
+ private func computeFeePayTemplate(_ amount: Amount) async -> ComputeFeeResult? {
+ return nil
+ }
+
+ @MainActor
+ private func viewDidLoad() async {
+ if let response = try? await model.checkPayForTemplate(url.trimmedString) {
+ let details = response.templateDetails
+ let defaults = details.editableDefaults // might be nil, or its fields might be nil
+ // TODO: let the user choose a currency from supportedCurrencies[]
+ let supportedCurrencies = response.supportedCurrencies
+
+ /// checkPayForTemplate does not provide fees (yet)
+ let contract = details.templateContract // specifies fixed amount/summary
+ if let amount = contract.amount {
+ controller.updateAmount(amount, forSaved: url)
+ }
+ if contract.templateType == "paivana" {
+ // TODO: ???
+ isPaivana = true
+ } else {
+ amountIsEditable = contract.amount == nil
+ summaryIsEditable = contract.summary == nil
+ }
+
+ let prepCurrency = contract.currency ?? defaults?.currency ?? supportedCurrencies.first ?? UNKNOWN
+ let zeroAmount = Amount(currency: prepCurrency, cent: 0)
+ let prepAmount = contract.amount ?? defaults?.amount // might be nil
+ let prepSummary = contract.summary ?? defaults?.summary // might be nil
+// symLog.log("LoadingView.task preparePayForTemplate")
+ /// preparePayForTemplate will make a network call to the merchant and create a TX
+ /// -> we only want to do this after the user entered amount and subject - but before confirmation of course
+// if let result = await preparePayForTemplate(model: model,
+// url: url,
+// amount: amountIsEditable ? prepAmount ?? zeroAmount
+// : nil,
+// summary: summaryIsEditable ? prepSummary ?? SPACE
+// : nil,
+// announce: announce)
+// { symLog.log("preparePayForTemplate finished")
+ amountToTransfer = prepAmount ?? zeroAmount
+ summary = prepSummary ?? EMPTYSTRING
+ templateContract = contract
+// insufficient = result.insufficient
+// feeAmount = result.feeAmount
+// feeStr = result.feeStr
+// preparePayResult = result.ppCheck
+// } else {
+// symLog.log("preparePayForTemplateM failed")
+// }
+ }
+
+ }
+ var body: some View {
+ if let templateContract { // preparePayResult
+// let currency = templateContract.currency ?? templateContract.amount?.currencyStr ?? UNKNOWN
+ let a11yLabel = String(localized: "Amount to pay:", comment: "a11y, no abbreviations")
+ let amountLabel = minimalistic ? String(localized: "Amount:")
+ : a11yLabel
+ // final destination with amountToTransfer, after user input of amount
+ let finalDestinationI = PaymentView(stack: stack.push(),
+ url: url,
+ template: true,
+ amountToTransfer: $amountToTransfer,
+ summary: $summary,
+ amountIsEditable: amountIsEditable,
+ summaryIsEditable: summaryIsEditable)
+
+ // final destination with amountShortcut, when user tapped a shortcut
+ let finalDestinationS = PaymentView(stack: stack.push(),
+ url: url,
+ template: true,
+ amountToTransfer: $amountShortcut,
+ summary: $summary,
+ amountIsEditable: amountIsEditable,
+ summaryIsEditable: summaryIsEditable)
+
+ // destination to subject input
+ let inputDestination = SubjectInputV(stack: stack.push(),
+ url: url,
+ amountAvailable: nil,
+ amountToTransfer: $amountToTransfer,
+ amountLabel: amountLabel,
+ summary: $summary,
+// insufficient: $insufficient,
+// feeAmount: $feeAmount,
+ feeIsNotZero: true, // TODO: feeIsNotZero()
+ targetView: finalDestinationI)
+
+ // destination to subject input, when user tapped an amount shortcut
+ let shortcutDestination = SubjectInputV(stack: stack.push(),
+ url: url,
+ amountAvailable: nil,
+ amountToTransfer: $amountShortcut,
+ amountLabel: amountLabel,
+ summary: $summary,
+// insufficient: $insufficient,
+// feeAmount: $feeAmount,
+ feeIsNotZero: true, // TODO: feeIsNotZero()
+ targetView: finalDestinationS)
+ Group {
+ if amountIsEditable { // template contract amount is not fixed => let the user input an amount first
+ let amountInput = AmountInputV(stack: stack.push(),
+ scope: scope,
+ amountAvailable: $amountAvailable,
+ amountLabel: amountLabel,
+ a11yLabel: a11yLabel,
+ amountToTransfer: $amountToTransfer,
+ amountLastUsed: amountLastUsed,
+ wireFee: nil,
+ summary: $summary,
+ shortcutAction: shortcutAction,
+ buttonAction: buttonAction1,
+ isIncoming: false,
+ computeFee: computeFeePayTemplate)
+ ScrollView {
+ if summaryIsEditable { // after amount input,
+ amountInput
+ .background( NavLink($shortcutSelected) { shortcutDestination } )
+ .background( NavLink($buttonSelected1) { inputDestination} )
+
+ } else {
+ amountInput
+ .background( NavLink($shortcutSelected) { finalDestinationS } )
+ .background( NavLink($buttonSelected1) { finalDestinationI } )
+ }
+ } // amountInput
+ } else if summaryIsEditable { // template contract summary is not fixed => let the user input a summary
+ ScrollView {
+ inputDestination
+ .background( NavLink($buttonSelected2) { finalDestinationI } )
+ } // inputDestination
+ } else { // both template contract amount and summary are fixed => directly show the payment
+ // Attention: contains a List, thus mustn't be included in a ScrollView
+ PaymentView(stack: stack.push(),
+ url: url,
+ template: true,
+ amountToTransfer: $amountToTransfer,
+ summary: $summary,
+ amountIsEditable: amountIsEditable,
+ summaryIsEditable: summaryIsEditable)
+ }
+ } .navigationTitle(navTitle)
+// .ignoresSafeArea(.keyboard, edges: .bottom)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .background(FullBackground())
+ .onAppear() {
+ symLog.log("onAppear")
+ DebugViewC.shared.setSheetID(SHEET_PAY_TEMPLATE, stack: stack.push())
+ }
+ } else {
+ LoadingView(stack: stack.push(), scopeInfo: nil, message: url.host)
+ .task { await viewDidLoad() }
+ }
+ }
+}
diff --git a/TalerWallet1/Views/Sheets/Payment/PayTemplateV.swift b/TalerWallet1/Views/Sheets/Payment/PayTemplateV.swift
@@ -1,214 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-26 Taler Systems S.A.
- * See LICENSE.md
- */
-/**
- * @author Marc Stibane
- */
-import SwiftUI
-import taler_swift
-import SymLog
-
-// Will be called either by the user scanning a <pay-template> QR code or tapping the provided link,
-// both from the shop's website - or even from a printed QR code.
-// We check whether amount and/or summary is editable, and finally go to PaymentView
-struct PayTemplateV: View {
- private let symLog = SymLogV(0)
- let stack: CallStack
-
- // the scanned URL
- let url: URL
-
- @EnvironmentObject private var controller: Controller
- @EnvironmentObject private var model: WalletModel
- @AppStorage("minimalistic") var minimalistic: Bool = false
- @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
-
- let navTitle = String(localized: "Custom Amount", comment:"pay merchant")
-
-// @State private var insufficient = false
-// @State private var preparePayResult: PreparePayResult? = nil
- @State private var templateContract: TemplateContractDetails? = nil
- @State private var amountIsEditable = false
- @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING) // Update currency when used
- @State private var amountShortcut = Amount.zero(currency: EMPTYSTRING) // Update currency when used
- @State private var amountLastUsed = Amount.zero(currency: EMPTYSTRING) // Update currency when used
- @State private var amountAvailable = Amount.zero(currency: EMPTYSTRING) // TODO: set correct available amount (like in SendAmountV)
- @State private var shortcutSelected = false
- @State private var buttonSelected1 = false
- @State private var buttonSelected2 = false
- @State private var summaryIsEditable = false
- @State private var isPaivana = false
- @State private var summary: String = EMPTYSTRING // templateParam
- @State private var scope: ScopeInfo? = nil
-
-// @State private var feeAmount: Amount? = nil
-// @State private var feeStr: String = EMPTYSTRING
-
- private func shortcutAction(_ shortcut: Amount) {
- amountShortcut = shortcut
- shortcutSelected = true
- }
- private func buttonAction1() {
- buttonSelected1 = true
- }
- private func buttonAction2() {
- buttonSelected2 = true
- }
-
- private func computeFeePayTemplate(_ amount: Amount) async -> ComputeFeeResult? {
- return nil
- }
-
- @MainActor
- private func viewDidLoad() async {
- if let response = try? await model.checkPayForTemplate(url.trimmedString) {
- let details = response.templateDetails
- let defaults = details.editableDefaults // might be nil, or its fields might be nil
- // TODO: let the user choose a currency from supportedCurrencies[]
- let supportedCurrencies = response.supportedCurrencies
-
- /// checkPayForTemplate does not provide fees (yet)
- let contract = details.templateContract // specifies fixed amount/summary
- if let amount = contract.amount {
- controller.updateAmount(amount, forSaved: url)
- }
- if contract.templateType == "paivana" {
- // TODO: ???
- isPaivana = true
- } else {
- amountIsEditable = contract.amount == nil
- summaryIsEditable = contract.summary == nil
- }
-
- let prepCurrency = contract.currency ?? defaults?.currency ?? supportedCurrencies.first ?? UNKNOWN
- let zeroAmount = Amount(currency: prepCurrency, cent: 0)
- let prepAmount = contract.amount ?? defaults?.amount // might be nil
- let prepSummary = contract.summary ?? defaults?.summary // might be nil
-// symLog.log("LoadingView.task preparePayForTemplate")
- /// preparePayForTemplate will make a network call to the merchant and create a TX
- /// -> we only want to do this after the user entered amount and subject - but before confirmation of course
-// if let result = await preparePayForTemplate(model: model,
-// url: url,
-// amount: amountIsEditable ? prepAmount ?? zeroAmount
-// : nil,
-// summary: summaryIsEditable ? prepSummary ?? SPACE
-// : nil,
-// announce: announce)
-// { symLog.log("preparePayForTemplate finished")
- amountToTransfer = prepAmount ?? zeroAmount
- summary = prepSummary ?? EMPTYSTRING
- templateContract = contract
-// insufficient = result.insufficient
-// feeAmount = result.feeAmount
-// feeStr = result.feeStr
-// preparePayResult = result.ppCheck
-// } else {
-// symLog.log("preparePayForTemplateM failed")
-// }
- }
-
- }
- var body: some View {
- if let templateContract { // preparePayResult
-// let currency = templateContract.currency ?? templateContract.amount?.currencyStr ?? UNKNOWN
- let a11yLabel = String(localized: "Amount to pay:", comment: "a11y, no abbreviations")
- let amountLabel = minimalistic ? String(localized: "Amount:")
- : a11yLabel
- // final destination with amountToTransfer, after user input of amount
- let finalDestinationI = PaymentView(stack: stack.push(),
- url: url,
- template: true,
- amountToTransfer: $amountToTransfer,
- summary: $summary,
- amountIsEditable: amountIsEditable,
- summaryIsEditable: summaryIsEditable)
-
- // final destination with amountShortcut, when user tapped a shortcut
- let finalDestinationS = PaymentView(stack: stack.push(),
- url: url,
- template: true,
- amountToTransfer: $amountShortcut,
- summary: $summary,
- amountIsEditable: amountIsEditable,
- summaryIsEditable: summaryIsEditable)
-
- // destination to subject input
- let inputDestination = SubjectInputV(stack: stack.push(),
- url: url,
- amountAvailable: nil,
- amountToTransfer: $amountToTransfer,
- amountLabel: amountLabel,
- summary: $summary,
-// insufficient: $insufficient,
-// feeAmount: $feeAmount,
- feeIsNotZero: true, // TODO: feeIsNotZero()
- targetView: finalDestinationI)
-
- // destination to subject input, when user tapped an amount shortcut
- let shortcutDestination = SubjectInputV(stack: stack.push(),
- url: url,
- amountAvailable: nil,
- amountToTransfer: $amountShortcut,
- amountLabel: amountLabel,
- summary: $summary,
-// insufficient: $insufficient,
-// feeAmount: $feeAmount,
- feeIsNotZero: true, // TODO: feeIsNotZero()
- targetView: finalDestinationS)
- Group {
- if amountIsEditable { // template contract amount is not fixed => let the user input an amount first
- let amountInput = AmountInputV(stack: stack.push(),
- scope: scope,
- amountAvailable: $amountAvailable,
- amountLabel: amountLabel,
- a11yLabel: a11yLabel,
- amountToTransfer: $amountToTransfer,
- amountLastUsed: amountLastUsed,
- wireFee: nil,
- summary: $summary,
- shortcutAction: shortcutAction,
- buttonAction: buttonAction1,
- isIncoming: false,
- computeFee: computeFeePayTemplate)
- ScrollView {
- if summaryIsEditable { // after amount input,
- amountInput
- .background( NavLink($shortcutSelected) { shortcutDestination } )
- .background( NavLink($buttonSelected1) { inputDestination} )
-
- } else {
- amountInput
- .background( NavLink($shortcutSelected) { finalDestinationS } )
- .background( NavLink($buttonSelected1) { finalDestinationI } )
- }
- } // amountInput
- } else if summaryIsEditable { // template contract summary is not fixed => let the user input a summary
- ScrollView {
- inputDestination
- .background( NavLink($buttonSelected2) { finalDestinationI } )
- } // inputDestination
- } else { // both template contract amount and summary are fixed => directly show the payment
- // Attention: contains a List, thus mustn't be included in a ScrollView
- PaymentView(stack: stack.push(),
- url: url,
- template: true,
- amountToTransfer: $amountToTransfer,
- summary: $summary,
- amountIsEditable: amountIsEditable,
- summaryIsEditable: summaryIsEditable)
- }
- } .navigationTitle(navTitle)
-// .ignoresSafeArea(.keyboard, edges: .bottom)
- .frame(maxWidth: .infinity, alignment: .leading)
- .background(FullBackground())
- .onAppear() {
- symLog.log("onAppear")
- DebugViewC.shared.setSheetID(SHEET_PAY_TEMPLATE, stack: stack.push())
- }
- } else {
- LoadingView(stack: stack.push(), scopeInfo: nil, message: url.host)
- .task { await viewDidLoad() }
- }
- }
-}
diff --git a/TalerWallet1/Views/Sheets/URLSheet.swift b/TalerWallet1/Views/Sheets/URLSheet.swift
@@ -78,7 +78,7 @@ struct URLSheet: View {
case .payPush:
P2pReceiveURIView(stack: stack.push(), url: passedURL, oimEuro: oimEuro)
case .payTemplate:
- PayTemplateV(stack: stack.push(), url: passedURL)
+ PayTemplateScan(stack: stack.push(), url: passedURL)
case .refund:
RefundURIView(stack: stack.push(), url: passedURL)
#if GNU_TALER || TALER_NIGHTLY