taler-ios

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

PayTemplateV.swift (12294B)


      1 /*
      2  * This file is part of GNU Taler, ©2022-25 Taler Systems S.A.
      3  * See LICENSE.md
      4  */
      5 /**
      6  * @author Marc Stibane
      7  */
      8 import SwiftUI
      9 import taler_swift
     10 import SymLog
     11 
     12 // Will be called either by the user scanning a <pay-template> QR code or tapping the provided link,
     13 // both from the shop's website - or even from a printed QR code.
     14 // We check whether amount and/or summary is editable, and finally go to PaymentView
     15 struct PayTemplateV: View {
     16     private let symLog = SymLogV(0)
     17     let stack: CallStack
     18 
     19     // the scanned URL
     20     let url: URL
     21 
     22     @EnvironmentObject private var controller: Controller
     23     @EnvironmentObject private var model: WalletModel
     24     @AppStorage("minimalistic") var minimalistic: Bool = false
     25     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
     26 
     27     let navTitle = String(localized: "Custom Amount", comment:"pay merchant")
     28 
     29 //    @State private var insufficient = false
     30 //    @State private var preparePayResult: PreparePayResult? = nil
     31     @State private var templateContract: TemplateContractDetails? = nil
     32     @State private var amountIsEditable = false
     33     @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING)    // Update currency when used
     34     @State private var amountShortcut = Amount.zero(currency: EMPTYSTRING)      // Update currency when used
     35     @State private var amountLastUsed = Amount.zero(currency: EMPTYSTRING)      // Update currency when used
     36     @State private var amountAvailable = Amount.zero(currency: EMPTYSTRING)     // TODO: set correct available amount (like in SendAmountV)
     37     @State private var shortcutSelected = false
     38     @State private var buttonSelected1 = false
     39     @State private var buttonSelected2 = false
     40     @State private var summaryIsEditable = false
     41     @State private var summary: String = EMPTYSTRING       // templateParam
     42     @State private var scope: ScopeInfo? = nil
     43 
     44 //    @State private var feeAmount: Amount? = nil
     45 //    @State private var feeStr: String = EMPTYSTRING
     46 
     47     private func shortcutAction(_ shortcut: Amount) {
     48         amountShortcut = shortcut
     49         shortcutSelected = true
     50     }
     51     private func buttonAction1() {
     52         buttonSelected1 = true
     53     }
     54     private func buttonAction2() {
     55         buttonSelected2 = true
     56     }
     57 
     58     @MainActor
     59     func acceptAction(preparePayResult: PreparePayResult) {
     60         Task { // runs on MainActor
     61             if let confirmPayResult = try? await model.confirmPay(preparePayResult.transactionId) {
     62 //                symLog.log(confirmPayResult as Any)
     63                 if confirmPayResult.type == "done" {
     64                     dismissTop(stack.push())
     65                 } else if confirmPayResult.type == "error" {
     66                     controller.playSound(0)
     67                     // TODO: show error
     68                 }
     69             }
     70         }
     71     }
     72 
     73     private func computeFeePayTemplate(_ amount: Amount) async -> ComputeFeeResult? {
     74 //        if let result = await preparePayForTemplate(model: model,
     75 //                                                      url: url,
     76 //                                                   amount: amountToTransfer,
     77 //                                                  summary: summaryIsEditable ? summary ?? SPACE
     78 //                                                                             : nil,
     79 //                                                 announce: announce)
     80 //        {
     81 //            preparePayResult = result.ppCheck
     82 //        }
     83         return nil
     84     }
     85 
     86     @MainActor
     87     private func viewDidLoad() async {
     88         if let response = try? await model.checkPayForTemplate(url.absoluteString) {
     89             let details = response.templateDetails
     90             let defaults = details.editableDefaults     // might be nil, or its fields might be nil
     91                                                         // TODO: let the user choose a currency from supportedCurrencies[]
     92             let supportedCurrencies = response.supportedCurrencies
     93 
     94             /// checkPayForTemplate does not provide fees (yet)
     95             let contract = details.templateContract     // specifies fixed amount/summary
     96             if let amount = contract.amount {
     97                 controller.updateAmount(amount, forSaved: url)
     98             }
     99             amountIsEditable = contract.amount == nil
    100             summaryIsEditable = contract.summary == nil
    101 
    102             let prepCurrency = contract.currency ?? defaults?.currency ??
    103                              (supportedCurrencies.count > 0 ? supportedCurrencies[0]
    104                                                             : UNKNOWN)
    105             let zeroAmount = Amount(currency: prepCurrency, cent: 0)
    106             let prepAmount = contract.amount ?? defaults?.amount        // might be nil
    107             let prepSummary = contract.summary ?? defaults?.summary     // might be nil
    108 //          symLog.log("LoadingView.task preparePayForTemplate")
    109             /// preparePayForTemplate will make a network call to the merchant and create a TX
    110             ///  -> we only want to do this after the user entered amount and subject - but before confirmation of course
    111 //          if let result = await preparePayForTemplate(model: model,
    112 //                                                        url: url,
    113 //                                                     amount: amountIsEditable ? prepAmount ?? zeroAmount
    114 //                                                                              : nil,
    115 //                                                    summary: summaryIsEditable ? prepSummary ?? SPACE
    116 //                                                                               : nil,
    117 //                                                   announce: announce)
    118 //          {   symLog.log("preparePayForTemplate finished")
    119                 amountToTransfer = prepAmount ?? zeroAmount
    120                 summary = prepSummary ?? EMPTYSTRING
    121                 templateContract = contract
    122 //              insufficient = result.insufficient
    123 //              feeAmount = result.feeAmount
    124 //              feeStr = result.feeStr
    125 //              preparePayResult = result.ppCheck
    126 //          } else {
    127 //              symLog.log("preparePayForTemplateM failed")
    128 //          }
    129         }
    130 
    131     }
    132     var body: some View {
    133         if let templateContract {       // preparePayResult
    134 //            let currency = templateContract.currency ?? templateContract.amount?.currencyStr ?? UNKNOWN
    135             let a11yLabel = String(localized: "Amount to pay:", comment: "a11y, no abbreviations")
    136             let amountLabel = minimalistic ? String(localized: "Amount:")
    137                                            : a11yLabel
    138             // final destination with amountToTransfer, after user input of amount
    139             let finalDestinationI = PaymentView(stack: stack.push(),
    140                                                   url: url,
    141                                              template: true,
    142                                      amountToTransfer: $amountToTransfer,
    143                                               summary: $summary,
    144                                      amountIsEditable: amountIsEditable,
    145                                     summaryIsEditable: summaryIsEditable)
    146 
    147             // final destination with amountShortcut, when user tapped a shortcut
    148             let finalDestinationS = PaymentView(stack: stack.push(),
    149                                                   url: url,
    150                                              template: true,
    151                                      amountToTransfer: $amountShortcut,
    152                                               summary: $summary,
    153                                      amountIsEditable: amountIsEditable,
    154                                     summaryIsEditable: summaryIsEditable)
    155 
    156             // destination to subject input
    157             let inputDestination = SubjectInputV(stack: stack.push(),
    158                                                    url: url,
    159                                        amountAvailable: nil,
    160                                       amountToTransfer: $amountToTransfer,
    161                                            amountLabel: amountLabel,
    162                                                summary: $summary,
    163 //                                        insufficient: $insufficient,
    164 //                                           feeAmount: $feeAmount,
    165                                           feeIsNotZero: true, // TODO: feeIsNotZero()
    166                                             targetView: finalDestinationI)
    167 
    168             // destination to subject input, when user tapped an amount shortcut
    169             let shortcutDestination = SubjectInputV(stack: stack.push(),
    170                                                       url: url,
    171                                           amountAvailable: nil,
    172                                          amountToTransfer: $amountShortcut,
    173                                               amountLabel: amountLabel,
    174                                                   summary: $summary,
    175 //                                           insufficient: $insufficient,
    176 //                                              feeAmount: $feeAmount,
    177                                              feeIsNotZero: true, // TODO: feeIsNotZero()
    178                                                targetView: finalDestinationS)
    179           Group {
    180             if amountIsEditable {           // template contract amount is not fixed => let the user input an amount first
    181                 let amountInput = AmountInputV(stack: stack.push(),
    182                                                scope: scope,
    183                                      amountAvailable: $amountAvailable,
    184                                          amountLabel: amountLabel,
    185                                            a11yLabel: a11yLabel,
    186                                     amountToTransfer: $amountToTransfer,
    187                                       amountLastUsed: amountLastUsed,
    188                                              wireFee: nil,
    189                                              summary: $summary,
    190                                       shortcutAction: shortcutAction,
    191                                         buttonAction: buttonAction1,
    192                                           isIncoming: false,
    193                                           computeFee: computeFeePayTemplate)
    194                 ScrollView {
    195                     if summaryIsEditable {  // after amount input,
    196                         amountInput
    197                             .background( NavLink($shortcutSelected) { shortcutDestination } )
    198                             .background( NavLink($buttonSelected1) { inputDestination} )
    199 
    200                     } else {
    201                         amountInput
    202                             .background( NavLink($shortcutSelected) { finalDestinationS } )
    203                             .background( NavLink($buttonSelected1) { finalDestinationI } )
    204                     }
    205                 } // amountInput
    206             } else if summaryIsEditable {   // template contract summary is not fixed => let the user input a summary
    207                 ScrollView {
    208                     inputDestination
    209                         .background( NavLink($buttonSelected2) { finalDestinationI } )
    210                 } // inputDestination
    211             } else {    // both template contract amount and summary are fixed => directly show the payment
    212                 // Attention: contains a List, thus mustn't be included in a ScrollView
    213                 PaymentView(stack: stack.push(),
    214                               url: url,
    215                          template: true,
    216                  amountToTransfer: $amountToTransfer,
    217                           summary: $summary,
    218                  amountIsEditable: amountIsEditable,
    219                 summaryIsEditable: summaryIsEditable)
    220             }
    221           } .navigationTitle(navTitle)
    222             .frame(maxWidth: .infinity, alignment: .leading)
    223             .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
    224             .onAppear() {
    225                 symLog.log("onAppear")
    226                 DebugViewC.shared.setSheetID(SHEET_PAY_TEMPLATE)
    227             }
    228         } else {
    229             LoadingView(stack: stack.push(), scopeInfo: nil, message: url.host)
    230                 .task { await viewDidLoad() }
    231         }
    232     }
    233 }