taler-ios

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

AmountInputV.swift (5116B)


      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 struct ComputeFeeResult {
     13     let insufficient: Bool
     14     let feeAmount: Amount?
     15     let feeStr: (String, String)
     16     let numCoins: Int?
     17 
     18     static func zero() -> ComputeFeeResult {
     19         ComputeFeeResult(insufficient: false,
     20                             feeAmount: nil,
     21                                feeStr: (EMPTYSTRING, EMPTYSTRING),
     22                              numCoins: 0)
     23     }
     24     static func insufficient() -> ComputeFeeResult {
     25         ComputeFeeResult(insufficient: true,
     26                             feeAmount: nil,
     27                                feeStr: (EMPTYSTRING, EMPTYSTRING),
     28                              numCoins: -1)
     29     }
     30 }
     31 // MARK: -
     32 struct AmountInputV: View {
     33     private let symLog = SymLogV(0)
     34     let stack: CallStack
     35     let scope: ScopeInfo?
     36     @Binding var amountAvailable: Amount
     37     let amountLabel: String?
     38     let a11yLabel: String
     39     @Binding var amountToTransfer: Amount
     40     let amountLastUsed: Amount
     41     let wireFee: Amount?
     42     @Binding var summary: String
     43 //    @Binding var insufficient: Bool
     44 //    @Binding var feeAmount: Amount?
     45     let shortcutAction: ((_ amount: Amount) -> Void)?
     46     let buttonAction: () -> Void
     47 //    let feeIsNegative: Bool
     48     let isIncoming: Bool
     49     let computeFee: ((_ amount: Amount) async -> ComputeFeeResult?)?
     50 
     51     @EnvironmentObject private var controller: Controller
     52     @Environment(\.colorScheme) private var colorScheme
     53     @Environment(\.colorSchemeContrast) private var colorSchemeContrast
     54 
     55     @State private var feeAmount: Amount? = nil
     56     @State private var feeStr = (EMPTYSTRING, EMPTYSTRING)
     57     @State private var numCoins: Int?
     58 
     59     struct Flags {
     60         let insufficient: Bool
     61         let disabled: Bool
     62     }
     63 
     64     func checkAvailable(_ coinData: CoinData) -> Flags {
     65         let isZero = amountToTransfer.isZero
     66         if !amountAvailable.isZero {
     67             do {
     68                 let insufficient: Bool
     69 //                if let feeAmount {
     70 //                    if feeIsNegative {
     71 //                        insufficient = try amountToTransfer > amountAvailable
     72 //                    } else {
     73 //                        insufficient = try (amountToTransfer + feeAmount) > amountAvailable
     74 //                    }
     75 //                } else {
     76                     insufficient = try amountToTransfer > amountAvailable
     77 //                }
     78                 let disabled = insufficient || isZero || coinData.invalid || coinData.tooMany
     79                 return Flags(insufficient: insufficient, disabled: disabled)
     80             } catch {
     81                 // TODO: error Amounts don't match
     82                 symLog.log("❗️Cannot compare amountAvailable.\(amountAvailable.currencyStr) to amountToTransfer.\(amountToTransfer.currencyStr))")
     83             }
     84         }
     85         return Flags(insufficient: false, disabled: isZero)
     86     }
     87 
     88     var body: some View {
     89         let currency = amountToTransfer.currencyStr
     90         VStack(alignment: .trailing) {
     91             CurrencyInputView(scope: scope,
     92                              amount: $amountToTransfer,
     93                      amountLastUsed: amountLastUsed,
     94                           available: isIncoming ? nil : amountAvailable,
     95                               title: amountLabel,
     96                           a11yTitle: a11yLabel,
     97                      shortcutAction: shortcutAction)
     98 //                .accessibility(sortPriority: 2)
     99 
    100             let coinData = CoinData(coins: numCoins, fee: feeAmount)
    101             QuiteSomeCoins(scope: scope,
    102                         coinData: coinData,
    103                    shouldShowFee: true,       // TODO: set to false if we never charge withdrawal fees
    104                    feeIsNegative: isIncoming)
    105             let flags = checkAvailable(coinData)
    106             let hint = String(localized: "enabled when amount is non-zero", comment: "a11y")
    107             Button("Next") { buttonAction() }
    108                 .buttonStyle(TalerButtonStyle(type: .prominent, disabled: flags.disabled))
    109                 .disabled(flags.disabled)
    110                 .accessibilityHint(flags.disabled ? hint : EMPTYSTRING)
    111         }.padding(.horizontal)
    112         .frame(maxWidth: .infinity, alignment: .leading)
    113         .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
    114         .task(id: amountToTransfer.value) {
    115             // re-compute the fees on every tapped digit or backspace
    116             if let computeFee {
    117                 symLog.log(".task \(amountToTransfer.value)")
    118                 if let result: ComputeFeeResult = await computeFee(amountToTransfer) {
    119                     symLog.log("computeFee() finished")
    120                     feeStr = result.feeStr
    121 //                    insufficient = result.insufficient    // TODO: insufficient
    122                     feeAmount = result.feeAmount
    123                     numCoins = result.numCoins
    124                 } else {
    125                     symLog.log("computeFee() failed ❗️") //  \(error)")
    126                 }
    127             }
    128         }
    129     }
    130 }