DepositAmountView.swift (6385B)
1 /* 2 * This file is part of GNU Taler, ©2022-26 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 // Called from DepositAmountAction 13 struct DepositAmountView: View { 14 private let symLog = SymLogV(0) 15 let stack: CallStack 16 @Binding var balance: Balance? 17 @Binding var balanceIndex: Int 18 @Binding var amountLastUsed: Amount 19 @Binding var amountToTransfer: Amount 20 let amountAvailable: Amount 21 let depositStarted: Bool 22 let action: () -> Void 23 24 @EnvironmentObject private var controller: Controller 25 @EnvironmentObject private var model: WalletModel 26 @Environment(\.colorScheme) private var colorScheme 27 @Environment(\.colorSchemeContrast) private var colorSchemeContrast 28 @AppStorage("minimalistic") var minimalistic: Bool = false 29 30 @State var checkDepositResult: CheckDepositResponse? = nil 31 @State private var feeAmount: Amount? = nil 32 @State private var feeStr: String = EMPTYSTRING 33 @State private var exchange: Exchange? = nil // wg. noFees 34 35 public static func navTitle(_ currency: String = EMPTYSTRING, 36 _ condition: Bool = false 37 ) -> String { 38 condition ? String(localized: "NavTitle_Deposit_Currency", 39 defaultValue: "Deposit \(currency)", 40 comment: "currencySymbol") 41 : String(localized: "NavTitle_Deposit", 42 defaultValue: "Deposit") 43 } 44 45 private func feeLabel(_ feeString: String) -> String { 46 feeString.isEmpty ? EMPTYSTRING : String(localized: "+ \(feeString) fee") 47 } 48 49 private func feeIsNotZero() -> Bool? { 50 if let hasNoFees = exchange?.noFees { 51 if hasNoFees { 52 return nil // this exchange never has fees 53 } 54 } 55 return checkDepositResult == nil ? false 56 : true // TODO: !(feeAmount?.isZero ?? false) 57 } 58 59 @MainActor 60 private func computeFeeDeposit(_ amount: Amount) async -> ComputeFeeResult? { 61 if amount.isZero { 62 return ComputeFeeResult.zero() 63 } 64 let insufficient = (try? amount > amountAvailable) ?? true 65 if insufficient { 66 return ComputeFeeResult.insufficient() 67 } 68 // private func fee(ppCheck: CheckDepositResponse?) -> Amount? { 69 do { 70 // if let ppCheck { 71 // // Outgoing: fee = effective - raw 72 // feeAmount = try ppCheck.fees.coin + ppCheck.fees.wire + ppCheck.fees.refresh 73 // return feeAmount 74 // } 75 } catch { 76 77 } 78 return nil 79 } 80 81 private func buttonTitle(_ amount: Amount) -> String { 82 if let balance { 83 let amountWithCurrency = amount.formatted(balance.scopeInfo, isNegative: false, useISO: true) 84 return DepositAmountView.navTitle(amountWithCurrency.0, true) 85 } 86 return DepositAmountView.navTitle() // should never happen 87 } 88 89 var body: some View { 90 #if PRINT_CHANGES 91 let _ = Self._printChanges() 92 let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear 93 #endif 94 if let balance { 95 let scopeInfo = balance.scopeInfo 96 let availableStr = amountAvailable.formatted(scopeInfo, isNegative: false) 97 98 let currencyInfo = controller.info(for: scopeInfo, controller.currencyTicker) 99 // let amountVoiceOver = amountToTransfer.formatted(scopeInfo, isNegative: false) 100 let insufficientLabel = String(localized: "You don't have enough \(currencyInfo.specs.name).") 101 // let insufficientLabel2 = String(localized: "but you only have \(available) to deposit.") 102 103 // amountAvailable is wallet-core's getMaxDepositAmount() verdict for this scope. 104 // Fail closed when the two amounts cannot be compared - this happens while the 105 // scope (and thus the currency) is being switched. 106 let insufficient = !amountToTransfer.isZero 107 && ((try? amountToTransfer > amountAvailable) ?? true) 108 let disabled = insufficient || amountToTransfer.isZero 109 110 Text("Available: \(availableStr.0)") 111 .accessibilityLabel(Text("Available: \(availableStr.1)", comment: "a11y")) 112 .talerFont(.title3) 113 .frame(maxWidth: .infinity, alignment: .trailing) 114 .padding(.horizontal) 115 // .padding(.bottom, 2) 116 let a11yLabel = String(localized: "Amount to deposit:", comment: "a11y, no abbreviations") 117 let amountLabel = minimalistic ? String(localized: "Amount:") 118 : a11yLabel 119 CurrencyInputView(scope: scopeInfo, 120 amount: $amountToTransfer, 121 amountLastUsed: amountLastUsed, 122 available: amountAvailable, // enable shortcuts if available >= value 123 title: amountLabel, 124 a11yTitle: a11yLabel, 125 shortcutAction: nil) 126 .padding(.horizontal) 127 Text(insufficient ? insufficientLabel 128 : feeLabel(feeStr)) 129 .talerFont(.body) 130 .foregroundColor(insufficient ? WalletColors().attention 131 : (feeAmount?.isZero ?? true) ? WalletColors().secondary(colorScheme, colorSchemeContrast) 132 : WalletColors().negative) 133 .padding(4) 134 let hint = String(localized: "enabled when amount is non-zero, but not higher than your available nt", 135 comment: "a11y") 136 Button(buttonTitle(amountToTransfer)) { action() } 137 .buttonStyle(TalerButtonStyle(type: .prominent, disabled: disabled || depositStarted)) 138 .disabled(disabled || depositStarted) 139 .accessibilityHint(disabled ? hint : EMPTYSTRING) 140 .padding(.horizontal) 141 } else { // no balance - Yikes 142 Text("No balance. There seems to be a problem with the database...") 143 } 144 } // body 145 }