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 }