commit 1f0c84ae3a5d01e8127f84a8156d5b2fb2e982cf
parent e522d4385f5f88747a813ab0138395a0769b6fa7
Author: Marc Stibane <marc@taler.net>
Date: Sat, 25 May 2024 21:51:21 +0200
checkPayForTemplate
Diffstat:
8 files changed, 319 insertions(+), 249 deletions(-)
diff --git a/TalerWallet1/Model/Model+Payment.swift b/TalerWallet1/Model/Model+Payment.swift
@@ -1,7 +1,10 @@
/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * This file is part of GNU Taler, ©2022-24 Taler Systems S.A.
* See LICENSE.md
*/
+/**
+ * @author Marc Stibane
+ */
import Foundation
import taler_swift
import AnyCodable
@@ -163,7 +166,7 @@ struct TemplateParams: Codable {
let summary: String? // Human-readable short summary of the contract
}
/// A request to get an exchange's payment contract terms.
-fileprivate struct PreparePayForTemplate: WalletBackendFormattedRequest {
+fileprivate struct PreparePayForTemplateRequest: WalletBackendFormattedRequest {
typealias Response = PreparePayResult
func operation() -> String { "preparePayForTemplate" }
func args() -> Args { Args(talerPayTemplateUri: talerPayTemplateUri, templateParams: templateParams) }
@@ -177,45 +180,39 @@ fileprivate struct PreparePayForTemplate: WalletBackendFormattedRequest {
}
// MARK: -
struct TemplateContractDetails: Codable {
- let summary: String? // Human-readable short summary of the contract
+ let summary: String? // Human-readable short summary of the contract. Fixed if this field exists, editable if nil
let currency: String?
- let amount: Amount? // Total amount payable
+ let amount: Amount? // Total amount payable. Fixed if this field exists, editable if nil
let minimumAge: Int
- let payDuration: String? // RelativeTime
+ let payDuration: RelativeTime
enum CodingKeys: String, CodingKey {
- case summary
- case currency
- case amount
+ case summary, currency, amount
case minimumAge = "minimum_age"
case payDuration = "pay_duration"
}
}
struct TemplateContractDetailsDefaults: Codable {
- let summary: String? // Human-readable short summary of the contract
+ let summary: String? // Default 'Human-readable short summary' if editable, or empty if nil.
let currency: String?
- let amount: Amount? // Total amount payable
- let minimumAge: Int?
-
- enum CodingKeys: String, CodingKey {
- case summary
- case currency
- case amount
- case minimumAge = "minimum_age"
- }
+ let amount: Amount? // Default amount if editable, or unspecified if nil.
}
-/// The result from checkPayForTemplate
-struct WalletTemplateDetails: Codable {
+struct TalerMerchantTemplateDetails: Codable {
let templateContract: TemplateContractDetails
let editableDefaults: TemplateContractDetailsDefaults?
- let requiredCurrency: String?
-
+// let requiredCurrency: String?
enum CodingKeys: String, CodingKey {
case templateContract = "template_contract"
case editableDefaults = "editable_defaults"
- case requiredCurrency = "required_currency"
+// case requiredCurrency = "required_currency"
}
}
+
+/// The result from checkPayForTemplate
+struct WalletTemplateDetails: Codable {
+ let templateDetails: TalerMerchantTemplateDetails
+ let supportedCurrencies: [String]
+}
/// A request to get an exchange's payment contract terms.
fileprivate struct CheckPayForTemplate: WalletBackendFormattedRequest {
typealias Response = WalletTemplateDetails
@@ -266,7 +263,7 @@ extension WalletModel {
func preparePayForTemplateM(_ talerPayTemplateUri: String, amount: Amount?, summary: String?, viewHandles: Bool = false) // M for MainActor
async throws -> PreparePayResult {
let templateParams = TemplateParams(amount: amount, summary: summary)
- let request = PreparePayForTemplate(talerPayTemplateUri: talerPayTemplateUri, templateParams: templateParams)
+ let request = PreparePayForTemplateRequest(talerPayTemplateUri: talerPayTemplateUri, templateParams: templateParams)
let response = try await sendRequest(request, ASYNCDELAY, viewHandles: viewHandles)
return response
}
diff --git a/TalerWallet1/Views/Banking/DepositAmountV.swift b/TalerWallet1/Views/Banking/DepositAmountV.swift
@@ -1,7 +1,10 @@
/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * This file is part of GNU Taler, ©2022-24 Taler Systems S.A.
* See LICENSE.md
*/
+/**
+ * @author Marc Stibane
+ */
import SwiftUI
import taler_swift
import SymLog
@@ -44,8 +47,6 @@ struct DepositAmountV: View {
feeAmount = nil
return feeAmount
}
-
- var feeLabel: String { feeStr.count > 0 ? String(localized: "+ \(feeStr) fee") : EMPTYSTRING }
private func feeIsNotZero() -> Bool? {
if let hasNoFees = exchange?.noFees {
@@ -109,9 +110,9 @@ struct DepositAmountV: View {
title: minimalistic ? String(localized: "Amount:")
: String(localized: "Amount to deposit:"),
shortcutAction: nil)
-// .padding(.top)
+
Text(insufficient ? insufficientLabel
- : feeLabel)
+ : feeLabel(feeStr))
.talerFont(.body)
.foregroundColor(insufficient ? .red
: (feeAmount?.isZero ?? true) ? WalletColors().secondary(colorScheme, colorSchemeContrast)
@@ -167,11 +168,15 @@ struct DepositAmountV: View {
prepareDepositResult = nil
} else if let paytoUri {
if let ppCheck = try? await model.prepareDepositM(paytoUri, amount: amountToTransfer) {
- prepareDepositResult = ppCheck
- if let feeAmount = fee(ppCheck: prepareDepositResult) {
+ if let feeAmount = fee(ppCheck: ppCheck) {
feeStr = feeAmount.string(currencyInfo)
- } else { feeStr = EMPTYSTRING }
- announce("\(amountVoiceOver), \(feeLabel)")
+ let feeLabel = feeLabel(feeStr)
+ announce("\(amountVoiceOver), \(feeLabel)")
+ } else {
+ feeStr = EMPTYSTRING
+ announce(amountVoiceOver)
+ }
+ prepareDepositResult = ppCheck
} else {
prepareDepositResult = nil
}
diff --git a/TalerWallet1/Views/HelperViews/AmountInputV.swift b/TalerWallet1/Views/HelperViews/AmountInputV.swift
@@ -1,7 +1,10 @@
/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * This file is part of GNU Taler, ©2022-24 Taler Systems S.A.
* See LICENSE.md
*/
+/**
+ * @author Marc Stibane
+ */
import SwiftUI
import taler_swift
import SymLog
@@ -14,10 +17,10 @@ struct AmountInputV: View {
let amountAvailable: Amount? // TODO: GetMaxPeerPushAmount
@Binding var amountToTransfer: Amount
let amountLabel: String
- let wantsSummary: Bool // if true we call SubjectInputV next
+ let summaryIsEditable: Bool // if true we call SubjectInputV next
@Binding var summary: String
- @Binding var insufficient: Bool
- @Binding var feeAmount: Amount?
+// @Binding var insufficient: Bool
+// @Binding var feeAmount: Amount?
let shortcutAction: ((_ amount: Amount) -> Void)?
let buttonAction: () -> Void
@@ -29,24 +32,22 @@ struct AmountInputV: View {
@State private var preparePayResult: PreparePayResult? = nil
@State private var feeStr: String = EMPTYSTRING
- var feeLabel: String { feeStr.count > 0 ? String(localized: "+ \(feeStr) fee") : EMPTYSTRING }
+ struct Flags {
+ let insufficient: Bool
+ let disabled: Bool
+ }
- func preparePayForTemplate() async {
- if let url {
- if let ppCheck = try? await model.preparePayForTemplateM(url.absoluteString, amount: amountToTransfer, summary: summary) {
- let amount = ppCheck.amountRaw
- let currency = amount.currencyStr
- let currencyInfo = controller.info(for: currency, controller.currencyTicker)
- insufficient = ppCheck.status == .insufficientBalance
- feeAmount = templateFee(ppCheck: ppCheck)
- if let feeAmount {
- feeStr = feeAmount.string(currencyInfo)
- } else { feeStr = EMPTYSTRING }
- let amountVoiceOver = amount.string(currencyInfo)
- announce(this: "\(amountVoiceOver), \(feeLabel)")
- preparePayResult = ppCheck
+ func checkAvailable(amount: Amount) -> Flags {
+ if let amountAvailable {
+ do {
+ let insufficient = try amountAvailable > amount
+ let disabled = insufficient || amount.isZero
+ return Flags(insufficient: insufficient, disabled: disabled)
+ } catch {
+ // TODO: error Amounts don't match
}
}
+ return Flags(insufficient: false, disabled: amount.isZero)
}
var body: some View {
@@ -54,58 +55,60 @@ struct AmountInputV: View {
let currencyInfo = controller.info(for: currency, controller.currencyTicker)
let insufficientLabel = String(localized: "You don't have enough \(currency).")
let available = amountAvailable?.string(currencyInfo) ?? nil
- let disabled = insufficient || amountToTransfer.isZero
- VStack(alignment: .trailing) {
+ let flags = checkAvailable(amount: amountToTransfer)
+ ScrollView { VStack(alignment: .trailing) {
if let available {
Text("Available:\t\(available)")
.talerFont(.title3)
.padding(.bottom, 2)
- // .accessibility(sortPriority: 3)
+// .accessibility(sortPriority: 3)
}
CurrencyInputView(amount: $amountToTransfer,
available: amountAvailable,
title: amountLabel,
shortcutAction: shortcutAction)
// .accessibility(sortPriority: 2)
- Text(insufficient ? insufficientLabel
- : feeLabel)
- .talerFont(.body)
- .foregroundColor(insufficient ? .red
- : (feeAmount?.isZero ?? true) ? WalletColors().secondary(colorScheme, colorSchemeContrast)
- : .red)
-// .accessibility(sortPriority: 1)
- .padding(4)
- Group {
- if let url {
- let destination = LazyView {
- PaymentView(stack: stack.push(),
- url: url,
- template: true,
- amountToTransfer: $amountToTransfer,
- summary: $summary)
- }
- NavigationLink(destination: destination) {
- Text("Next")
- }
- .buttonStyle(TalerButtonStyle(type: .prominent, disabled: disabled))
- .disabled(disabled)
- } else {
- Button("Next") {
- buttonAction()
- }
- .buttonStyle(TalerButtonStyle(type: .prominent, disabled: disabled))
- .disabled(disabled)
- }
- }
-// .accessibility(sortPriority: 0)
- .task(id: amountToTransfer.value) {
- symLog.log(".task")
- await preparePayForTemplate()
- }
- }.padding(.horizontal)
- .onAppear() {
- // symLog.log("onAppear")
- DebugViewC.shared.setSheetID(SHEET_PAY_TEMPL_AMOUNT)
+ if flags.insufficient {
+ Text(insufficientLabel)
+ .talerFont(.body)
+ .foregroundColor(.red)
+ .padding(4)
+// } else {
+// Text(feeLabel(feeStr))
+// .talerFont(.body)
+// .foregroundColor((feeAmount?.isZero ?? true) ? WalletColors().secondary(colorScheme, colorSchemeContrast)
+// : .red)
+// .padding(4)
+// .accessibility(sortPriority: 1)
}
+ Button("Next") { buttonAction() }
+ .buttonStyle(TalerButtonStyle(type: .prominent, disabled: flags.disabled))
+ .disabled(flags.disabled)
+ }.padding(.horizontal) } // ScrollVStack
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
+ .onAppear() {
+// symLog.log("onAppear")
+ DebugViewC.shared.setSheetID(SHEET_PAY_TEMPL_AMOUNT)
+ }
+// .task(id: amountToTransfer.value) {
+// if let url {
+// symLog.log(".task preparePayForTemplate")
+// if let result = await preparePayForTemplate(model: model,
+// url: url,
+// amount: amountToTransfer,
+// summary: summaryIsEditable ? summary ?? " "
+// : nil,
+// announce: announce)
+// { symLog.log("preparePayForTemplate finished")
+// insufficient = result.insufficient
+// feeAmount = result.feeAmount
+// feeStr = result.feeStr
+// preparePayResult = result.ppCheck
+// } else {
+// symLog.log("preparePayForTemplateM failed")
+// }
+// }
+// }
}
}
diff --git a/TalerWallet1/Views/HelperViews/SubjectInputV.swift b/TalerWallet1/Views/HelperViews/SubjectInputV.swift
@@ -1,7 +1,10 @@
/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * This file is part of GNU Taler, ©2022-24 Taler Systems S.A.
* See LICENSE.md
*/
+/**
+ * @author Marc Stibane
+ */
import SwiftUI
import taler_swift
import SymLog
@@ -15,8 +18,8 @@ struct SubjectInputV<TargetView: View>: View {
@Binding var amountToTransfer: Amount
let amountLabel: String
@Binding var summary: String
- @Binding var insufficient: Bool
- @Binding var feeAmount: Amount?
+// @Binding var insufficient: Bool
+// @Binding var feeAmount: Amount?
let feeIsNotZero: Bool? // nil = no fees at all, false = no fee for this tx
let currencyInfo: CurrencyInfo
@@ -30,37 +33,21 @@ struct SubjectInputV<TargetView: View>: View {
@Environment(\.colorSchemeContrast) private var colorSchemeContrast
@AppStorage("minimalistic") var minimalistic: Bool = false
+ let navTitle = String(localized: "Custom Summary", comment:"pay merchant")
+
@State private var preparePayResult: PreparePayResult? = nil
- @State private var feeStr: String = EMPTYSTRING
@FocusState private var isFocused: Bool
- var feeLabel: String { feeStr.count > 0 ? String(localized: "+ \(feeStr) fee") : EMPTYSTRING }
-
- func preparePayForTemplate() async {
- if let url {
- if let ppCheck = try? await model.preparePayForTemplateM(url.absoluteString, amount: amountToTransfer, summary: summary) {
- let amount = ppCheck.amountRaw
- let currency = amount.currencyStr
- let currencyInfo = controller.info(for: currency, controller.currencyTicker)
- insufficient = ppCheck.status == .insufficientBalance
- feeAmount = templateFee(ppCheck: ppCheck)
- if let feeAmount {
- feeStr = feeAmount.string(currencyInfo)
- } else { feeStr = EMPTYSTRING }
- let amountVoiceOver = amount.string(currencyInfo)
- announce("\(amountVoiceOver), \(feeLabel)")
- preparePayResult = ppCheck
- }
- }
- }
-
var body: some View {
let currency = amountToTransfer.currencyStr
- let currencyInfo = controller.info(for: currency, controller.currencyTicker)
- let insufficientLabel = String(localized: "You don't have enough \(currency).")
+// let feeStr = feeAmount?.string(currencyInfo) ?? EMPTYSTRING
+// let insufficientLabel = String(localized: "You don't have enough \(currency).")
+// let feeLabel = insufficient ? insufficientLabel
+// : feeLabel(feeStr)
let available = amountAvailable?.string(currencyInfo) ?? nil
- let disabled = insufficient || summary.count == 0
- VStack(alignment: .leading) {
+// let disabled = insufficient || summary.count == 0
+ let disabled = summary.count == 0
+ ScrollView { VStack(alignment: .leading) {
if let available {
Text("Available:\t\(available)")
.talerFont(.title3)
@@ -97,22 +84,31 @@ struct SubjectInputV<TargetView: View>: View {
}
}
}
-
- let subLabel = insufficient ? insufficientLabel
- : feeLabel
- Text(subLabel)
+ HStack {
+ Text(amountToTransfer.string(currencyInfo))
+ // TODO: hasFees?
+// Text(feeLabel)
+ }
.talerFont(.body)
- .foregroundColor(insufficient ? .red
- : (feeAmount?.isZero ?? true) ? WalletColors().secondary(colorScheme, colorSchemeContrast)
- : .red)
- // .accessibility(sortPriority: 1)
+// .foregroundColor(insufficient ? .red : WalletColors().secondary(colorScheme, colorSchemeContrast))
+ .foregroundColor(WalletColors().secondary(colorScheme, colorSchemeContrast))
+// .accessibility(sortPriority: 1)
.padding(4)
+// if insufficient {
+// Text(insufficientLabel)
+// .talerFont(.body)
+// .foregroundColor(.red)
+// .padding(4)
+// }
NavigationLink("Next", destination: targetView)
.buttonStyle(TalerButtonStyle(type: .prominent, disabled: disabled))
.disabled(disabled)
// .accessibility(sortPriority: 0)
- }.padding(.horizontal)
+ }.padding(.horizontal) } // ScrollVStack
+ .navigationTitle(navTitle)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
.onAppear() {
// symLog.log("onAppear")
DebugViewC.shared.setSheetID(SHEET_PAY_TEMPL_SUBJECT)
diff --git a/TalerWallet1/Views/Peer2peer/SendAmount.swift b/TalerWallet1/Views/Peer2peer/SendAmount.swift
@@ -1,7 +1,10 @@
/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * This file is part of GNU Taler, ©2022-24 Taler Systems S.A.
* See LICENSE.md
*/
+/**
+ * @author Marc Stibane
+ */
import SwiftUI
import taler_swift
import SymLog
@@ -43,8 +46,6 @@ struct SendAmount: View {
feeAmount = nil
return feeAmount
}
-
- var feeLabel: String { feeStr.count > 0 ? String(localized: "+ \(feeStr) fee") : EMPTYSTRING }
private func shortcutAction(_ shortcut: Amount) {
amountShortcut = shortcut
@@ -82,11 +83,11 @@ struct SendAmount: View {
let inputDestination = LazyView {
P2PSubjectV(stack: stack.push(),
- feeLabel: feeLabel,
+ feeLabel: feeLabel(feeStr),
feeIsNotZero: feeIsNotZero(),
currencyInfo: currencyInfo,
amountToSend: true,
- amountToTransfer: $amountToTransfer,
+ amountToTransfer: $amountToTransfer, // from the textedit
summary: $summary,
expireDays: $expireDays)
}
@@ -96,20 +97,22 @@ struct SendAmount: View {
feeIsNotZero: feeIsNotZero(),
currencyInfo: currencyInfo,
amountToSend: true,
- amountToTransfer: $amountShortcut,
+ amountToTransfer: $amountShortcut, // from the tapped shortcut button
summary: $summary,
expireDays: $expireDays)
}
ScrollView {
+ let amountLabel = minimalistic ? String(localized: "Amount:")
+ : String(localized: "Amount to send:")
+
AmountInputV(stack: stack.push(), url: nil,
amountAvailable: amountAvailable,
amountToTransfer: $amountToTransfer,
- amountLabel: minimalistic ? String(localized: "Amount:")
- : String(localized: "Amount to send:"),
- wantsSummary: true,
+ amountLabel: amountLabel,
+ summaryIsEditable: true,
summary: $summary,
- insufficient: $insufficient,
- feeAmount: $feeAmount,
+// insufficient: $insufficient,
+// feeAmount: $feeAmount,
shortcutAction: shortcutAction,
buttonAction: buttonAction)
.background(NavigationLink(destination: shortcutDestination, isActive: $shortcutSelected)
@@ -149,13 +152,17 @@ struct SendAmount: View {
feeStr = EMPTYSTRING
} else {
if let ppCheck = try? await model.checkPeerPushDebitM(amountToTransfer) {
- peerPushCheck = ppCheck
// TODO: set from exchange
// agePicker.setAges(ages: peerPushCheck?.ageRestrictionOptions)
- if let feeAmount = fee(ppCheck: peerPushCheck) {
+ if let feeAmount = fee(ppCheck: ppCheck) {
feeStr = feeAmount.string(currencyInfo)
- } else { feeStr = EMPTYSTRING }
- announce("\(amountVoiceOver), \(feeLabel)")
+ let feeLabel = feeLabel(feeStr)
+ announce("\(amountVoiceOver), \(feeLabel)")
+ } else {
+ feeStr = EMPTYSTRING
+ announce(amountVoiceOver)
+ }
+ peerPushCheck = ppCheck
} else {
peerPushCheck = nil
}
diff --git a/TalerWallet1/Views/Sheets/Payment/PayTemplateV.swift b/TalerWallet1/Views/Sheets/Payment/PayTemplateV.swift
@@ -1,26 +1,17 @@
/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * This file is part of GNU Taler, ©2022-24 Taler Systems S.A.
* See LICENSE.md
*/
+/**
+ * @author Marc Stibane
+ */
import SwiftUI
import taler_swift
import SymLog
-func templateFee(ppCheck: PreparePayResult?) -> Amount? {
- do {
- if let ppCheck {
- // Outgoing: fee = effective - raw
- if let effective = ppCheck.amountEffective {
- let fee = try effective - ppCheck.amountRaw
- return fee
- }
- }
- } catch {}
- return nil
-}
-
-// 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
+// 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
@@ -33,22 +24,23 @@ struct PayTemplateV: View {
@AppStorage("minimalistic") var minimalistic: Bool = false
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
- let navTitle = String(localized: "Customize Order", comment:"pay merchant")
+ let navTitle = String(localized: "Custom Amount", comment:"pay merchant")
- @State private var insufficient = false
- @State private var preparePayResult: PreparePayResult? = nil
- @State private var currencyName: String? = nil
+// @State private var insufficient = false
+// @State private var preparePayResult: PreparePayResult? = nil
+ @State private var templateContract: TemplateContractDetails? = nil
+ @State private var currencyName = EMPTYSTRING
+ @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 shortcutSelected = false
@State private var buttonSelected1 = false
@State private var buttonSelected2 = false
+ @State private var summaryIsEditable = false
@State private var summary: String = EMPTYSTRING // templateParam
- @State private var wantsSummary: Bool = false
- @State private var feeAmount: Amount? = nil
- @State private var feeStr: String = EMPTYSTRING
- var feeLabel: String { feeStr.count > 0 ? String(localized: "+ \(feeStr) fee") : EMPTYSTRING }
+// @State private var feeAmount: Amount? = nil
+// @State private var feeStr: String = EMPTYSTRING
private func shortcutAction(_ shortcut: Amount) {
amountShortcut = shortcut
@@ -73,46 +65,13 @@ struct PayTemplateV: View {
}
}
- func queryURL() -> Bool {
- if let queryParameters = url.queryParameters {
- if let amountStr = queryParameters["amount"] {
- currencyName = amountStr
- amountToTransfer = Amount.zero(currency: amountStr)
- }
- if let summaryStr = queryParameters["summary"] {
- summary = summaryStr
- wantsSummary = true
- }
- return true
- }
- return false
- }
-
- func preparePayForTemplate() async {
- if let ppCheck = try? await model.preparePayForTemplateM(url.absoluteString, amount: amountToTransfer, summary: summary) {
- let amount = ppCheck.amountRaw
- let currency = amount.currencyStr
- let currencyInfo = controller.info(for: currency, controller.currencyTicker)
- insufficient = ppCheck.status == .insufficientBalance
- feeAmount = templateFee(ppCheck: ppCheck)
- if let feeAmount {
- feeStr = feeAmount.string(currencyInfo)
- } else { feeStr = EMPTYSTRING }
- let amountVoiceOver = amount.string(currencyInfo)
- announce(this: "\(amountVoiceOver), \(feeLabel)")
- preparePayResult = ppCheck
- } else {
- symLog.log("preparePayForTemplateM failed")
- }
- }
-
var body: some View {
- if let preparePayResult {
- let effective = preparePayResult.amountEffective
- let baseURL = preparePayResult.contractTerms.exchanges.first?.url
- let raw = preparePayResult.amountRaw
- let currency = raw.currencyStr
- let currencyInfo = controller.info(for: currency, controller.currencyTicker)
+ if let templateContract { // preparePayResult
+// let effective = preparePayResult.amountEffective
+// let baseURL = preparePayResult.contractTerms.exchanges.first?.url
+// let raw = preparePayResult.amountRaw
+// let currency = raw.currencyStr
+ let currencyInfo = controller.info(for: currencyName, controller.currencyTicker)
let amountLabel = minimalistic ? String(localized: "Amount:")
: String(localized: "Amount to pay:")
let finalDestinationI = LazyView {
@@ -120,53 +79,58 @@ struct PayTemplateV: View {
url: url,
template: true,
amountToTransfer: $amountToTransfer,
- summary: $summary)
- }
+ summary: $summary,
+ amountIsEditable: amountIsEditable,
+ summaryIsEditable: summaryIsEditable)
+ } // final destination with amountToTransfer, after user input of amount
+ let finalDestinationS = LazyView {
+ PaymentView(stack: stack.push(),
+ url: url,
+ template: true,
+ amountToTransfer: $amountShortcut,
+ summary: $summary,
+ amountIsEditable: amountIsEditable,
+ summaryIsEditable: summaryIsEditable)
+ } // final destination with amountShortcut, when user tapped a shortcut
+
let inputDestination = LazyView {
SubjectInputV(stack: stack.push(), url: url,
amountAvailable: nil,
amountToTransfer: $amountToTransfer,
amountLabel: amountLabel,
summary: $summary,
- insufficient: $insufficient,
- feeAmount: $feeAmount,
- feeIsNotZero: true, // feeIsNotZero(),
+// insufficient: $insufficient,
+// feeAmount: $feeAmount,
+ feeIsNotZero: true, // TODO: feeIsNotZero()
currencyInfo: currencyInfo,
targetView: finalDestinationI)
- }
- let finalDestinationS = LazyView {
- PaymentView(stack: stack.push(),
- url: url,
- template: true,
- amountToTransfer: $amountShortcut,
- summary: $summary)
- }
+ } // destination to subject input
let shortcutDestination = LazyView {
SubjectInputV(stack: stack.push(), url: url,
amountAvailable: nil,
amountToTransfer: $amountShortcut,
amountLabel: amountLabel,
summary: $summary,
- insufficient: $insufficient,
- feeAmount: $feeAmount,
- feeIsNotZero: true, // feeIsNotZero(),
+// insufficient: $insufficient,
+// feeAmount: $feeAmount,
+ feeIsNotZero: true, // TODO: feeIsNotZero()
currencyInfo: currencyInfo,
targetView: finalDestinationS)
- }
+ }// destination to subject input, when user tapped an amount shortcut
Group {
- if let currencyName { // template included a currency name => let the user input an amount
+ if amountIsEditable { // template contract amount is not fixed => let the user input an amount first
let amountInput = AmountInputV(stack: stack.push(), url: url,
amountAvailable: nil,
amountToTransfer: $amountToTransfer,
amountLabel: amountLabel,
- wantsSummary: wantsSummary,
+ summaryIsEditable: summaryIsEditable,
summary: $summary,
- insufficient: $insufficient,
- feeAmount: $feeAmount,
+// insufficient: $insufficient,
+// feeAmount: $feeAmount,
shortcutAction: shortcutAction,
buttonAction: buttonAction1)
ScrollView {
- if wantsSummary {
+ if summaryIsEditable { // after amount input,
amountInput
.background(NavigationLink(destination: shortcutDestination, isActive: $shortcutSelected)
{ EmptyView() }.frame(width: 0).opacity(0).hidden())
@@ -180,32 +144,64 @@ struct PayTemplateV: View {
.background(NavigationLink(destination: finalDestinationI, isActive: $buttonSelected1)
{ EmptyView() }.frame(width: 0).opacity(0).hidden())
}
- } // ScrollVStack
- } else if wantsSummary { // check summary
+ }
+ } else if summaryIsEditable { // template contract summary is not fixed => let the user input a summary
ScrollView {
inputDestination
.background(NavigationLink(destination: finalDestinationI, isActive: $buttonSelected2)
{ EmptyView() }.frame(width: 0).opacity(0).hidden()
)
}
- } else {
+ } else { // both template contract amount and summary are fixed => directly show the payment
PaymentView(stack: stack.push(),
url: url,
template: true,
amountToTransfer: $amountToTransfer,
- summary: $summary)
+ summary: $summary,
+ amountIsEditable: amountIsEditable,
+ summaryIsEditable: summaryIsEditable)
}
- }.onAppear() {
+ }.navigationTitle(navTitle)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
+ .onAppear() {
symLog.log("onAppear")
DebugViewC.shared.setSheetID(SHEET_PAY_TEMPLATE)
- }.navigationTitle(navTitle)
+ }
} else {
LoadingView(scopeInfo: nil, message: url.host)
.task {
- symLog.log("LoadingView.task preparePayForTemplate")
- let hasParams = queryURL()
- await preparePayForTemplate()
- symLog.log("preparePayForTemplate finished")
+ if let details = try? await model.checkPayForTemplateM(url.absoluteString) {
+ let supportedCurrency0 = details.supportedCurrencies[0]
+ let contract = details.templateDetails.templateContract // specifies fixed amount/summary
+ amountIsEditable = contract.amount == nil
+ summaryIsEditable = contract.summary == nil
+ let defaults = details.templateDetails.editableDefaults // might be nil, or its fields might be nil
+ let prepCurrency = contract.currency ?? defaults?.currency ?? supportedCurrency0
+ 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")
+// if let result = await preparePayForTemplate(model: model,
+// url: url,
+// amount: amountIsEditable ? prepAmount ?? zeroAmount
+// : nil,
+// summary: summaryIsEditable ? prepSummary ?? " "
+// : nil,
+// announce: announce)
+// { symLog.log("preparePayForTemplate finished")
+ amountToTransfer = prepAmount ?? zeroAmount
+ currencyName = prepCurrency
+ summary = prepSummary ?? EMPTYSTRING
+ templateContract = contract
+// insufficient = result.insufficient
+// feeAmount = result.feeAmount
+// feeStr = result.feeStr
+// preparePayResult = result.ppCheck
+// } else {
+// symLog.log("preparePayForTemplateM failed")
+// }
+ }
}
}
}
diff --git a/TalerWallet1/Views/Sheets/Payment/PaymentView.swift b/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
@@ -1,11 +1,69 @@
/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * This file is part of GNU Taler, ©2022-24 Taler Systems S.A.
* See LICENSE.md
*/
+/**
+ * @author Marc Stibane
+ */
import SwiftUI
import taler_swift
import SymLog
+typealias Announce = (_ this: String) -> ()
+
+func feeLabel(_ feeString: String) -> String {
+ feeString.count > 0 ? String(localized: "+ \(feeString) fee")
+ : EMPTYSTRING
+}
+
+func templateFee(ppCheck: PreparePayResult?) -> Amount? {
+ do {
+ if let ppCheck {
+ // Outgoing: fee = effective - raw
+ if let effective = ppCheck.amountEffective {
+ let fee = try effective - ppCheck.amountRaw
+ return fee
+ }
+ }
+ } catch {}
+ return nil
+}
+
+struct PayForTemplateResult {
+ let ppCheck: PreparePayResult
+ let insufficient: Bool
+ let feeAmount: Amount?
+ let feeStr: String
+}
+
+func preparePayForTemplate(model: WalletModel,
+ url: URL,
+ amount: Amount?,
+ summary: String?,
+ announce: Announce)
+ async -> PayForTemplateResult? {
+ if let ppCheck = try? await model.preparePayForTemplateM(url.absoluteString, amount: amount, summary: summary) {
+ let controller = Controller.shared
+ let amountRaw = ppCheck.amountRaw
+ let currency = amountRaw.currencyStr
+ let currencyInfo = controller.info(for: currency, controller.currencyTicker)
+ let amountVoiceOver = amountRaw.string(currencyInfo)
+ let insufficient = ppCheck.status == .insufficientBalance
+ if let feeAmount = templateFee(ppCheck: ppCheck) {
+ let feeStr = feeAmount.string(currencyInfo)
+ let feeLabel = feeLabel(feeStr)
+ announce("\(amountVoiceOver), \(feeLabel)")
+ return PayForTemplateResult(ppCheck: ppCheck, insufficient: insufficient,
+ feeAmount: feeAmount, feeStr: feeStr)
+ }
+ announce(amountVoiceOver)
+ return PayForTemplateResult(ppCheck: ppCheck, insufficient: insufficient,
+ feeAmount: nil, feeStr: EMPTYSTRING)
+ }
+ return nil
+}
+
+// MARK: -
// 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 {
@@ -18,6 +76,8 @@ struct PaymentView: View {
let template: Bool
@Binding var amountToTransfer: Amount
@Binding var summary: String
+ let amountIsEditable: Bool //
+ let summaryIsEditable: Bool //
@EnvironmentObject private var model: WalletModel
@EnvironmentObject private var controller: Controller
@@ -34,6 +94,7 @@ struct PaymentView: View {
// TODO: show balanceDetails.balanceAvailable
let baseURL = preparePayResult.contractTerms.exchanges.first?.url
let raw = preparePayResult.amountRaw
+ let status = preparePayResult.status
let currency = raw.currencyStr
let topTitle = String(localized: "Amount to pay:")
let topAbbrev = String(localized: "Pay:", comment: "mini")
@@ -102,8 +163,8 @@ struct PaymentView: View {
symLog.log(".task")
if template {
if let result = try? await model.preparePayForTemplateM(url.absoluteString,
- amount: amountToTransfer,
- summary: summary) {
+ amount: amountIsEditable ? amountToTransfer : nil,
+ summary: summaryIsEditable ? summary : nil) {
preparePayResult = result
}
} else {
@@ -170,9 +231,10 @@ struct PaymentURIView_Previews: PreviewProvider {
// @State private var amount: Amount? = nil // templateParam
// @State private var summary: String? = nil // templateParam
- PaymentView(stack: CallStack("Preview"),
- url: url, template: false, amountToTransfer: nil, summary: nil,
- preparePayResult: details)
+ PaymentView(stack: CallStack("Preview"), url: url,
+ template: false, amountToTransfer: nil, summary: nil,
+ amountIsEditable: false, summaryIsEditable: false,
+ preparePayResult: details)
}
}
#endif
diff --git a/TalerWallet1/Views/Sheets/URLSheet.swift b/TalerWallet1/Views/Sheets/URLSheet.swift
@@ -1,7 +1,10 @@
/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * This file is part of GNU Taler, ©2022-24 Taler Systems S.A.
* See LICENSE.md
*/
+/**
+ * @author Marc Stibane
+ */
import SwiftUI
import taler_swift
import SymLog
@@ -32,7 +35,8 @@ struct URLSheet: View {
WithdrawExchangeV(stack: stack.push(), url: urlToOpen) // TODO: just check the ToS
case .pay:
PaymentView(stack: stack.push(), url: urlToOpen,
- template: false, amountToTransfer: $amountToTransfer, summary: $summary)
+ template: false, amountToTransfer: $amountToTransfer, summary: $summary,
+ amountIsEditable: false, summaryIsEditable: false)
case .payPull:
P2pPayURIView(stack: stack.push(), url: urlToOpen)
case .payPush: