taler-ios

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

BankEditView.swift (9765B)


      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 /// 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)"       // TODO: convert BBAN to IBAN
     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 titleStr = String(localized: "Done", comment: "Done button")
    118         let a11yLabelStr = String(localized: "Done", comment: "a11y for the Done button")
    119         let doneButton = DoneButton(titleStr: titleStr, accessibilityLabelStr: a11yLabelStr) {
    120             dismissTop(stack.push())
    121         }
    122 
    123         let methods = [PaytoType.iban, PaytoType.xTalerBank]
    124         List {
    125             if kycCompleted // || true
    126             {   Section {
    127                     Text("If you change the account holder name or the IBAN, you may have to perform the legitimization procedure again.")          // TODO: BBAN
    128                         .talerFont(.body)
    129                 }
    130             }
    131           Section {
    132             let labelTitle = String(localized: "Label")
    133             let labelColon = String("\(labelTitle):")
    134             if !minimalistic {
    135                 Text(labelColon)
    136                     .talerFont(.picker)
    137                     .accessibilityHidden(true)
    138                     .padding(.bottom, -12)
    139             }
    140             TextField(minimalistic ? labelTitle : EMPTYSTRING, text: $accountLabel)
    141                 .accessibilityLabel(labelColon)
    142                 .focused($focus, equals: .accountLabel)
    143                 .talerFont(.title3)
    144                 .foregroundColor(WalletColors().fieldForeground)     // text color
    145                 .background(WalletColors().fieldBackground)
    146                 .textFieldStyle(.roundedBorder)
    147                 .padding(.bottom)
    148 
    149             let holderTitle = String(localized: "Account holder")
    150             let holderColon = String("\(holderTitle):")
    151             if !minimalistic {
    152                 Text(holderColon)
    153                     .talerFont(.picker)
    154                     .accessibilityHidden(true)
    155                     .padding(.bottom, -12)
    156             }
    157             TextField(minimalistic ? holderTitle : EMPTYSTRING, text: $accountHolder)
    158                 .accessibilityLabel(holderColon)
    159                 .focused($focus, equals: .accountHolder)
    160                 .talerFont(.title3)
    161                 .foregroundColor(WalletColors().fieldForeground)     // text color
    162                 .background(WalletColors().fieldBackground)
    163                 .textFieldStyle(.roundedBorder)
    164                 .padding(.bottom)
    165 
    166             let paytoStr = paytoType.rawValue.uppercased()
    167             let paytoColon = String("\(paytoStr):")
    168             if let accountID {
    169                 // we are editing an existing bank account
    170                 Text(paytoColon)
    171                     .talerFont(.picker)
    172                     .accessibilityHidden(true)
    173                     .padding(.bottom, -12)
    174             } else {
    175                 // this is a NEW bank account - choose a payment method
    176                 Picker(EMPTYSTRING, selection: $selected) {
    177                     ForEach(0..<methods.count, id: \.self) { index in
    178                         let method = methods[index]
    179                         Text(method.rawValue.uppercased())
    180                             .tag(index)
    181                     }
    182                 }
    183                 .pickerStyle(.segmented)
    184                 .onChange(of: selected) { newValue in
    185                     paytoType = methods[newValue]
    186                 }
    187             }
    188                 if paytoType == .iban {
    189                     TextField(paytoStr, text: $iban)            // TODO: BBAN
    190                         .accessibilityLabel(paytoColon)
    191                         .focused($focus, equals: .iban)
    192                         .talerFont(.title3)
    193                         .foregroundColor(WalletColors().fieldForeground)     // text color
    194                         .background(WalletColors().fieldBackground)
    195                         .textFieldStyle(.roundedBorder)
    196                         .padding(.bottom)
    197                 } else if paytoType == .xTalerBank {
    198                     TextField(paytoStr, text: $xTaler)
    199                         .accessibilityLabel(paytoColon)
    200                         .focused($focus, equals: .xTaler)
    201                         .talerFont(.title3)
    202                         .foregroundColor(WalletColors().fieldForeground)     // text color
    203                         .background(WalletColors().fieldBackground)
    204                         .textFieldStyle(.roundedBorder)
    205                         .padding(.bottom)
    206                 } else {
    207                     Text("unknown payment method")
    208                         .talerFont(.title3)
    209                         .padding(.bottom)
    210                 }
    211 
    212             let buttonTitle = String(localized: "Account.Delete", defaultValue: "Forget bank account", comment: "Action button")
    213             let warningText1 = String(localized: "Are you sure you want to forget this bank account?")
    214             WarningButton(warningText: warningText1,
    215                           buttonTitle: buttonTitle,
    216                            buttonIcon: "trash",
    217                                  role: .destructive,                            // TODO: WalletColors().errorColor
    218                              disabled: $disabled,
    219                                action: deleteAccount)
    220             .padding(.top)
    221           }.listRowSeparator(.hidden)
    222         } // List
    223         .navigationBarItems(trailing: doneButton)
    224         .onChange(of: focus) { [focus] newState in
    225             switch focus {
    226                 case .none:
    227                     break
    228                 case .some(_): Task {
    229                     await updateAccount()
    230                 }
    231             }
    232         }
    233         .task { await viewDidLoad() }
    234         .onDisappear() {
    235             disabled = false
    236         }
    237     }
    238 }