taler-ios

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

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 }