taler-ios

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

commit 8c1b94805cae9d8533a96fd373478dcb7dea315f
parent a65d74638e890a0042218de9736028d68e684ad5
Author: Marc Stibane <marc@taler.net>
Date:   Thu,  5 Dec 2024 22:55:53 +0100

BankEditView

Diffstat:
MTalerWallet.xcodeproj/project.pbxproj | 6++++++
ATalerWallet1/Views/Settings/Bank/BankEditView.swift | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 204 insertions(+), 0 deletions(-)

diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj @@ -278,6 +278,8 @@ 4EC90C782A1B528B0071DC58 /* ExchangeSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EC90C772A1B528B0071DC58 /* ExchangeSectionView.swift */; }; 4ECB62802A0BA6DF004ABBB7 /* Model+P2P.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECB627F2A0BA6DF004ABBB7 /* Model+P2P.swift */; }; 4ECB62822A0BB01D004ABBB7 /* SelectDays.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */; }; + 4ECF927D2D005E3E006DB14E /* BankEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECF927C2D005E3E006DB14E /* BankEditView.swift */; }; + 4ECF927E2D005E3E006DB14E /* BankEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECF927C2D005E3E006DB14E /* BankEditView.swift */; }; 4ED2F94B2A278F5100453B40 /* ThreeAmountsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED2F94A2A278F5100453B40 /* ThreeAmountsSection.swift */; }; 4ED80E882B8F5FB8008BD576 /* CStringArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED80E872B8F5FB8008BD576 /* CStringArray.swift */; }; 4ED80E892B8F5FB8008BD576 /* CStringArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED80E872B8F5FB8008BD576 /* CStringArray.swift */; }; @@ -500,6 +502,7 @@ 4EC90C772A1B528B0071DC58 /* ExchangeSectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExchangeSectionView.swift; sourceTree = "<group>"; }; 4ECB627F2A0BA6DF004ABBB7 /* Model+P2P.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Model+P2P.swift"; sourceTree = "<group>"; }; 4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectDays.swift; sourceTree = "<group>"; }; + 4ECF927C2D005E3E006DB14E /* BankEditView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BankEditView.swift; sourceTree = "<group>"; }; 4ED2F94A2A278F5100453B40 /* ThreeAmountsSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreeAmountsSection.swift; sourceTree = "<group>"; }; 4ED80E872B8F5FB8008BD576 /* CStringArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CStringArray.swift; sourceTree = "<group>"; }; 4ED80E8A2B8F60E7008BD576 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; }; @@ -583,6 +586,7 @@ children = ( 4E0E8EF92CEFDB0700C5F914 /* BankListView.swift */, 4E0E8EFC2CF0A18800C5F914 /* BankSectionView.swift */, + 4ECF927C2D005E3E006DB14E /* BankEditView.swift */, ); path = Bank; sourceTree = "<group>"; @@ -1279,6 +1283,7 @@ 4E3EAE3E2A990778009F1BE8 /* ManualDetailsV.swift in Sources */, 4E0E8EFA2CEFDB0700C5F914 /* BankListView.swift in Sources */, 4E3EAE3F2A990778009F1BE8 /* View+dismissTop.swift in Sources */, + 4ECF927D2D005E3E006DB14E /* BankEditView.swift in Sources */, 4E3EAE402A990778009F1BE8 /* TransactionsListView.swift in Sources */, 4EE9864F2CE26E0F00F75634 /* DepositAmountView.swift in Sources */, 4E0A71142C396D86002485BB /* Error+debugDescription.swift in Sources */, @@ -1417,6 +1422,7 @@ 4E6EDD852A3615BE0031D520 /* ManualDetailsV.swift in Sources */, 4E0E8EFB2CEFDB0700C5F914 /* BankListView.swift in Sources */, 4EB0950B2989CB7C0043A8A1 /* View+dismissTop.swift in Sources */, + 4ECF927E2D005E3E006DB14E /* BankEditView.swift in Sources */, 4EB095562989CBFE0043A8A1 /* TransactionsListView.swift in Sources */, 4EE986502CE26E0F00F75634 /* DepositAmountView.swift in Sources */, 4E0A71152C396D86002485BB /* Error+debugDescription.swift in Sources */, diff --git a/TalerWallet1/Views/Settings/Bank/BankEditView.swift b/TalerWallet1/Views/Settings/Bank/BankEditView.swift @@ -0,0 +1,198 @@ +/* + * This file is part of GNU Taler, ©2022-24 Taler Systems S.A. + * See LICENSE.md + */ +/** + * @author Marc Stibane + */ +import SwiftUI +import taler_swift +import SymLog + +/// This view shows the currency name in an exchange section +/// currency +struct BankEditView: View { + private let symLog = SymLogV(0) + let stack: CallStack + let account: KnownBankAccountsInfo + + @EnvironmentObject private var model: WalletModel + @EnvironmentObject private var controller: Controller + @AppStorage("minimalistic") var minimalistic: Bool = false + @AppStorage("demoHints") var demoHints: Bool = true + @AppStorage("fakeNoFees") var fakeNoFees: Bool = true + @AppStorage("ownerName") var ownerName: String = EMPTYSTRING + + @State private var shouldReloadBalances: Int = 0 + @State private var currencyInfo: CurrencyInfo = CurrencyInfo.zero(UNKNOWN) + @State private var didDelete: Bool = false + @State private var disabled: Bool = false + @State private var showAlert: Bool = false + @State private var purge: Bool = false + @State private var global: Bool = false + @State private var accountHolder: String = EMPTYSTRING + @State private var iban: String = EMPTYSTRING + @State private var xTaler: String = EMPTYSTRING + @State private var alias: String = EMPTYSTRING + @State private var paytoType: PaytoType = .iban + @State private var selected = 0 + @FocusState private var focus:FocusedField? + + enum FocusedField: Hashable { + case accountHolder, iban, xTaler, alias + } + +// @MainActor +// private func validateIban() async { +// if (try? await model.validateIban(iban)) == true { +// let payto = "payto://iban/\(iban)?receiver-name=\(accountHolder)" +// paytoUri = payto.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! +// } else { +// paytoUri = nil +// } +// } + + @MainActor + private func viewDidLoad() async { + let payURL = URL(string: account.paytoUri) + iban = payURL?.iban ?? EMPTYSTRING + xTaler = payURL?.xTaler ?? EMPTYSTRING + alias = account.alias ?? EMPTYSTRING + if let queryParameters = payURL?.queryParameters { + let name = if let rcv = queryParameters["receiver-name"] { + rcv.replacingOccurrences(of: "+", with: SPACE) + } else { + ownerName + } + accountHolder = name + } + } + + @MainActor + private func deleteAccount() { + disabled = true // don't try this more than once + Task { // runs on MainActor +// if let _ = try? await model.deleteExchange(url: baseUrl, purge: purge, viewHandles: !purge) { +// purge = false +// symLog.log("deleted \(baseUrl.trimURL)") +// didDelete = true // change button text +// NotificationCenter.default.post(name: .ExchangeDeleted, object: nil, userInfo: nil) +// NotificationCenter.default.post(name: .BalanceChange, object: nil, userInfo: nil) +// demoHints = true +// } else { +// purge = true +// showAlert = true +// disabled = false +// } + } + } + + var body: some View { +#if PRINT_CHANGES + let _ = Self._printChanges() +// let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear +#endif + let methods = [PaytoType.iban, PaytoType.xTalerBank] + List { Group { + let accountTitle = String(localized: "Account holder") + let accountColon = String("\(accountTitle):") + if !minimalistic { + Text(accountColon) + .talerFont(.body) + .accessibilityHidden(true) + } + TextField(minimalistic ? accountTitle : EMPTYSTRING, text: $accountHolder) + .accessibilityLabel(accountColon) + .focused($focus, equals: .accountHolder) + .talerFont(.title3) + .foregroundColor(WalletColors().fieldForeground) // text color + .background(WalletColors().fieldBackground) + .textFieldStyle(.roundedBorder) + .padding(.top, -4) + .padding(.bottom) + + Picker(EMPTYSTRING, selection: $selected) { + ForEach(0..<methods.count, id: \.self) { index in + let method = methods[index] + Text(method.rawValue.uppercased()) + .tag(index) + } + } + .pickerStyle(.segmented) + .onChange(of: selected) { newValue in + paytoType = methods[newValue] + } + + Group { + let paytoStr = paytoType.rawValue.uppercased() + let paytoColon = String("\(paytoStr):") + if paytoType == .iban { + TextField(paytoStr, text: $iban) + .accessibilityLabel(paytoColon) + .focused($focus, equals: .iban) + .talerFont(.title3) + .foregroundColor(WalletColors().fieldForeground) // text color + .background(WalletColors().fieldBackground) + .textFieldStyle(.roundedBorder) + } else if paytoType == .xTalerBank { + TextField(paytoStr, text: $xTaler) + .accessibilityLabel(paytoColon) + .focused($focus, equals: .xTaler) + .talerFont(.title3) + .foregroundColor(WalletColors().fieldForeground) // text color + .background(WalletColors().fieldBackground) + .textFieldStyle(.roundedBorder) + } else { + Text("unknown payment method") + } + }.padding(.bottom) + + + if account.currencies.count == 1 { + let currency = account.currencies[0] + Text(minimalistic ? currency + : "Currency: \(currency)") + } else { + if !minimalistic { + Text("Currencies:") + } + ForEach (account.currencies, id: \.self) { currency in + Text(currency) + .padding(.leading) + } + } + + let note = String(localized: "Note") + let noteColon = String("\(note):") + if !minimalistic { + Text(noteColon) + .talerFont(.title3) + .accessibilityHidden(true) + } + TextField(minimalistic ? note : EMPTYSTRING, text: $alias) + .accessibilityLabel(noteColon) + .focused($focus, equals: .alias) + .talerFont(.title2) + .foregroundColor(WalletColors().fieldForeground) // text color + .background(WalletColors().fieldBackground) + .textFieldStyle(.roundedBorder) + Button { + deleteAccount() + } label: { + Label("Delete Account", systemImage: "trash") + } + .buttonStyle(TalerButtonStyle(type: .bordered, disabled: disabled)) + .disabled(disabled) + }.listRowSeparator(.hidden) + } // List + .onChange(of: focus) { + print($0) + } + .task { await viewDidLoad() } +// .task(id: controller.currencyTicker) { await currencyTickerChanged(scopeInfo) } + .onDisappear() { + disabled = false + purge = false + } + } +}