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 }