taler-ios

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

BankEditView.swift (9325B)


      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 /// This view shows the currency name in an exchange section
     13 ///         currency
     14 struct BankEditView: View {
     15     private let symLog = SymLogV(0)
     16     let stack: CallStack
     17     let accountID: String?
     18 
     19     @EnvironmentObject private var model: WalletModel
     20     @EnvironmentObject private var controller: Controller
     21     @AppStorage("minimalistic") var minimalistic: Bool = false
     22     @AppStorage("demoHints") var demoHints: Bool = true
     23 //    @AppStorage("fakeNoFees") var fakeNoFees: Bool = true     TODO: use this flag!
     24     @AppStorage("ownerName") var ownerName: String = EMPTYSTRING
     25 
     26     @State private var shouldReloadBalances: Int = 0
     27     @State private var currencyInfo: CurrencyInfo = CurrencyInfo.zero(UNKNOWN)
     28     @State private var didDelete: Bool = false
     29     @State private var disabled: Bool = false
     30     @State private var showAlert: Bool = false
     31 
     32     @State private var myAccount: String? = nil
     33     @State private var accountHolder: String = EMPTYSTRING
     34     @State private var iban: String = EMPTYSTRING
     35     @State private var xTaler: String = EMPTYSTRING
     36     @State private var accountLabel: String = EMPTYSTRING
     37     @State private var paytoType: PaytoType = .iban
     38     @State private var selected = 0
     39     @State private var kycCompleted = false
     40 
     41     @FocusState private var focus:FocusedField?
     42 
     43     enum FocusedField: Hashable {
     44         case accountLabel, accountHolder, iban, xTaler
     45     }
     46 
     47 //    @MainActor
     48 //    private func validateIban() async {
     49 //        if (try? await model.validateIban(iban)) == true {
     50 //            let payto = "payto://iban/\(iban)?receiver-name=\(accountHolder)"
     51 //            paytoUri = payto.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
     52 //        } else {
     53 //            paytoUri = nil
     54 //        }
     55 //    }
     56 
     57     @MainActor
     58     private func viewDidLoad() async {
     59         if let accountID {
     60             if let account = try? await model.getBankAccountById(accountID) {
     61                 let payTo = PayTo(account.paytoUri)
     62                 iban = payTo.iban ?? EMPTYSTRING
     63                 xTaler = payTo.xTaler ?? EMPTYSTRING
     64                 if iban.count < 1 && xTaler.count > 1 {
     65                     paytoType = .xTalerBank
     66                 }
     67                 accountLabel = account.label ?? EMPTYSTRING
     68                 kycCompleted = account.kycCompleted
     69                 accountHolder = payTo.receiver ?? ownerName
     70             }
     71         } else {
     72 
     73         }
     74     }
     75 
     76     @MainActor
     77     private func updateAccount() async {
     78         let payto = "payto://iban/\(iban)?receiver-name=\(accountHolder)"
     79         let paytoUri = payto.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
     80 
     81         symLog.log(paytoUri)
     82         if let account = try? await model.addBankAccount(paytoUri,
     83                                                    label: accountLabel,
     84                                                  replace: myAccount ?? accountID
     85         ) {
     86             symLog.log(account)
     87             myAccount = account
     88         } else {
     89             symLog.log("error addBankAccount")
     90         }
     91     }
     92 
     93     @MainActor
     94     private func deleteAccount() {
     95         disabled = true     // don't try this more than once
     96         Task { // runs on MainActor
     97             if let id = myAccount ?? accountID {
     98                 if let _ = try? await model.forgetBankAccount(id) {
     99                     symLog.log("forgot \(id)")
    100                     didDelete = true             // change button text
    101 //                NotificationCenter.default.post(name: .ExchangeDeleted, object: nil, userInfo: nil)
    102 //                NotificationCenter.default.post(name: .BalanceChange, object: nil, userInfo: nil)
    103                     dismissTop(stack.push())
    104                 } else {
    105                     showAlert = true
    106                     disabled = false
    107                 }
    108             }
    109         }
    110     }
    111 
    112     var body: some View {
    113 #if PRINT_CHANGES
    114         let _ = Self._printChanges()
    115 //        let _ = symLog.vlog()       // just to get the # to compare it with .onAppear & onDisappear
    116 #endif
    117         let methods = [PaytoType.iban, PaytoType.xTalerBank]
    118         List {
    119             if kycCompleted // || true
    120             {   Section {
    121                     Text("If you change the account holder name or the IBAN, you may have to perform the legitimization procedure again.")
    122                         .talerFont(.body)
    123                 }
    124             }
    125           Section {
    126             let labelTitle = String(localized: "Label")
    127             let labelColon = String("\(labelTitle):")
    128             if !minimalistic {
    129                 Text(labelColon)
    130                     .talerFont(.picker)
    131                     .accessibilityHidden(true)
    132                     .padding(.bottom, -12)
    133             }
    134             TextField(minimalistic ? labelTitle : EMPTYSTRING, text: $accountLabel)
    135                 .accessibilityLabel(labelColon)
    136                 .focused($focus, equals: .accountLabel)
    137                 .talerFont(.title3)
    138                 .foregroundColor(WalletColors().fieldForeground)     // text color
    139                 .background(WalletColors().fieldBackground)
    140                 .textFieldStyle(.roundedBorder)
    141                 .padding(.bottom)
    142 
    143             let holderTitle = String(localized: "Account holder")
    144             let holderColon = String("\(holderTitle):")
    145             if !minimalistic {
    146                 Text(holderColon)
    147                     .talerFont(.picker)
    148                     .accessibilityHidden(true)
    149                     .padding(.bottom, -12)
    150             }
    151             TextField(minimalistic ? holderTitle : EMPTYSTRING, text: $accountHolder)
    152                 .accessibilityLabel(holderColon)
    153                 .focused($focus, equals: .accountHolder)
    154                 .talerFont(.title3)
    155                 .foregroundColor(WalletColors().fieldForeground)     // text color
    156                 .background(WalletColors().fieldBackground)
    157                 .textFieldStyle(.roundedBorder)
    158                 .padding(.bottom)
    159 
    160             let paytoStr = paytoType.rawValue.uppercased()
    161             let paytoColon = String("\(paytoStr):")
    162             if let accountID {
    163                 // we are editing an existing bank account
    164                 Text(paytoColon)
    165                     .talerFont(.picker)
    166                     .accessibilityHidden(true)
    167                     .padding(.bottom, -12)
    168             } else {
    169                 // this is a NEW bank account - choose a payment method
    170                 Picker(EMPTYSTRING, selection: $selected) {
    171                     ForEach(0..<methods.count, id: \.self) { index in
    172                         let method = methods[index]
    173                         Text(method.rawValue.uppercased())
    174                             .tag(index)
    175                     }
    176                 }
    177                 .pickerStyle(.segmented)
    178                 .onChange(of: selected) { newValue in
    179                     paytoType = methods[newValue]
    180                 }
    181             }
    182                 if paytoType == .iban {
    183                     TextField(paytoStr, text: $iban)
    184                         .accessibilityLabel(paytoColon)
    185                         .focused($focus, equals: .iban)
    186                         .talerFont(.title3)
    187                         .foregroundColor(WalletColors().fieldForeground)     // text color
    188                         .background(WalletColors().fieldBackground)
    189                         .textFieldStyle(.roundedBorder)
    190                         .padding(.bottom)
    191                 } else if paytoType == .xTalerBank {
    192                     TextField(paytoStr, text: $xTaler)
    193                         .accessibilityLabel(paytoColon)
    194                         .focused($focus, equals: .xTaler)
    195                         .talerFont(.title3)
    196                         .foregroundColor(WalletColors().fieldForeground)     // text color
    197                         .background(WalletColors().fieldBackground)
    198                         .textFieldStyle(.roundedBorder)
    199                         .padding(.bottom)
    200                 } else {
    201                     Text("unknown payment method")
    202                         .talerFont(.title3)
    203                         .padding(.bottom)
    204                 }
    205 
    206             let buttonTitle = String(localized: "Account.Delete", defaultValue: "Forget bank account", comment: "Action button")
    207             let warningText1 = String(localized: "Are you sure you want to forget this bank account?")
    208             WarningButton(warningText: warningText1,
    209                           buttonTitle: buttonTitle,
    210                            buttonIcon: "trash",
    211                                  role: .destructive,                            // TODO: WalletColors().errorColor
    212                              disabled: $disabled,
    213                                action: deleteAccount)
    214             .padding(.top)
    215           }.listRowSeparator(.hidden)
    216         } // List
    217         .onChange(of: focus) { [focus] newState in
    218             switch focus {
    219                 case .none:
    220                     break
    221                 case .some(_): Task {
    222                     await updateAccount()
    223                 }
    224             }
    225         }
    226         .task { await viewDidLoad() }
    227         .onDisappear() {
    228             disabled = false
    229         }
    230     }
    231 }