commit e1d95f754404192ae906595ccf77e0e2e7d01a34
parent 01cdde738f910580511e586899e6d0c151d08cfc
Author: Marc Stibane <marc@taler.net>
Date: Mon, 25 Nov 2024 21:00:18 +0100
BankListView
Diffstat:
4 files changed, 254 insertions(+), 0 deletions(-)
diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj
@@ -11,6 +11,10 @@
4E0A71152C396D86002485BB /* Error+debugDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0A71132C396D86002485BB /* Error+debugDescription.swift */; };
4E0A71182C3AB099002485BB /* IconBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0A71172C3AB099002485BB /* IconBadge.swift */; };
4E0A71192C3AB099002485BB /* IconBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0A71172C3AB099002485BB /* IconBadge.swift */; };
+ 4E0E8EFA2CEFDB0700C5F914 /* BankListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0E8EF92CEFDB0700C5F914 /* BankListView.swift */; };
+ 4E0E8EFB2CEFDB0700C5F914 /* BankListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0E8EF92CEFDB0700C5F914 /* BankListView.swift */; };
+ 4E0E8EFD2CF0A18800C5F914 /* BankSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0E8EFC2CF0A18800C5F914 /* BankSectionView.swift */; };
+ 4E0E8EFE2CF0A18800C5F914 /* BankSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0E8EFC2CF0A18800C5F914 /* BankSectionView.swift */; };
4E11803D2CD2A6D700023E37 /* SendAmountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E11803C2CD2A6D700023E37 /* SendAmountView.swift */; };
4E11803E2CD2A6D700023E37 /* SendAmountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E11803C2CD2A6D700023E37 /* SendAmountView.swift */; };
4E16E12329F3BB99008B9C86 /* CurrencySpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */; };
@@ -362,6 +366,8 @@
/* Begin PBXFileReference section */
4E0A71132C396D86002485BB /* Error+debugDescription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Error+debugDescription.swift"; sourceTree = "<group>"; };
4E0A71172C3AB099002485BB /* IconBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconBadge.swift; sourceTree = "<group>"; };
+ 4E0E8EF92CEFDB0700C5F914 /* BankListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BankListView.swift; sourceTree = "<group>"; };
+ 4E0E8EFC2CF0A18800C5F914 /* BankSectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BankSectionView.swift; sourceTree = "<group>"; };
4E11803C2CD2A6D700023E37 /* SendAmountView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendAmountView.swift; sourceTree = "<group>"; };
4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrencySpecification.swift; sourceTree = "<group>"; };
4E1A59E02C99C5D700842BBF /* View+Keyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+Keyboard.swift"; sourceTree = "<group>"; };
@@ -571,6 +577,15 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 4E0E8EF82CEFDADF00C5F914 /* Bank */ = {
+ isa = PBXGroup;
+ children = (
+ 4E0E8EF92CEFDB0700C5F914 /* BankListView.swift */,
+ 4E0E8EFC2CF0A18800C5F914 /* BankSectionView.swift */,
+ );
+ path = Bank;
+ sourceTree = "<group>";
+ };
4E2254942A822B8100E41D29 /* Sounds */ = {
isa = PBXGroup;
children = (
@@ -769,6 +784,7 @@
4EB095242989CBFE0043A8A1 /* Settings */ = {
isa = PBXGroup;
children = (
+ 4E0E8EF82CEFDADF00C5F914 /* Bank */,
4EB095252989CBFE0043A8A1 /* SettingsView.swift */,
4EC400882AE3E7E800DF72C7 /* AboutView.swift */,
4EB095262989CBFE0043A8A1 /* SettingsItem.swift */,
@@ -1259,6 +1275,7 @@
4E3EAE3C2A990778009F1BE8 /* WithdrawTOSView.swift in Sources */,
4E3EAE3D2A990778009F1BE8 /* Sheet.swift in Sources */,
4E3EAE3E2A990778009F1BE8 /* ManualDetailsV.swift in Sources */,
+ 4E0E8EFA2CEFDB0700C5F914 /* BankListView.swift in Sources */,
4E3EAE3F2A990778009F1BE8 /* View+dismissTop.swift in Sources */,
4E3EAE402A990778009F1BE8 /* TransactionsListView.swift in Sources */,
4EE9864F2CE26E0F00F75634 /* DepositAmountView.swift in Sources */,
@@ -1328,6 +1345,7 @@
4E3EAE6F2A990778009F1BE8 /* TalerStrings.swift in Sources */,
4E3EAE702A990778009F1BE8 /* CurrencyInputView.swift in Sources */,
4E3EAE712A990778009F1BE8 /* URL+id+iban.swift in Sources */,
+ 4E0E8EFD2CF0A18800C5F914 /* BankSectionView.swift in Sources */,
4ED80E8B2B8F60E7008BD576 /* Atomic.swift in Sources */,
4EEC3A712B2285A200D05F9D /* WithdrawExchangeV.swift in Sources */,
4EDBDCD92AB787CB00925C02 /* CallStack.swift in Sources */,
@@ -1395,6 +1413,7 @@
4EB095652989CBFE0043A8A1 /* WithdrawTOSView.swift in Sources */,
4EEC157829F9032900D46A03 /* Sheet.swift in Sources */,
4E6EDD852A3615BE0031D520 /* ManualDetailsV.swift in Sources */,
+ 4E0E8EFB2CEFDB0700C5F914 /* BankListView.swift in Sources */,
4EB0950B2989CB7C0043A8A1 /* View+dismissTop.swift in Sources */,
4EB095562989CBFE0043A8A1 /* TransactionsListView.swift in Sources */,
4EE986502CE26E0F00F75634 /* DepositAmountView.swift in Sources */,
@@ -1464,6 +1483,7 @@
4EB0950A2989CB7C0043A8A1 /* TalerStrings.swift in Sources */,
4EA551252A2C923600FEC9A8 /* CurrencyInputView.swift in Sources */,
4E363CBC2A237E0900D7E98C /* URL+id+iban.swift in Sources */,
+ 4E0E8EFE2CF0A18800C5F914 /* BankSectionView.swift in Sources */,
4ED80E8C2B8F60E7008BD576 /* Atomic.swift in Sources */,
4EEC3A722B2285A200D05F9D /* WithdrawExchangeV.swift in Sources */,
4EDBDCDA2AB787CB00925C02 /* CallStack.swift in Sources */,
diff --git a/TalerWallet1/Views/Settings/Bank/BankListView.swift b/TalerWallet1/Views/Settings/Bank/BankListView.swift
@@ -0,0 +1,101 @@
+/*
+ * 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 list of exchanges
+struct BankListView: View {
+ private let symLog = SymLogV(0)
+ let stack: CallStack
+ let navTitle: String
+
+ @EnvironmentObject private var model: WalletModel
+ @EnvironmentObject private var controller: Controller
+ @AppStorage("minimalistic") var minimalistic: Bool = false
+ @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
+
+ @State var showAlert: Bool = false
+ @State var newExchange: String = TESTEXCHANGE
+ @State private var bankAccounts: [KnownBankAccountsInfo] = []
+
+ @MainActor
+ func addExchange(_ exchange: String) -> Void {
+ Task { // runs on MainActor
+ symLog.log("adding: \(exchange)")
+ if let _ = try? await model.addExchange(url: exchange) {
+ symLog.log("added: \(exchange)")
+ announce("added: \(exchange)")
+ NotificationCenter.default.post(name: .ExchangeAdded, object: nil, userInfo: nil)
+ }
+ }
+ }
+
+ @MainActor
+ private func viewDidLoad() async {
+ if let accounts = try? await model.listKnownBankAccounts() {
+ withAnimation { bankAccounts = accounts }
+ }
+ }
+
+ 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 a11yLabelStr = String(localized: "Add bank account", comment: "VoiceOver for the + button")
+ let plusButton = PlusButton(accessibilityLabelStr: a11yLabelStr) {
+ showAlert = true
+ }
+ let addTitleStr = String(localized: "Add bank account", comment: "title of the addExchange alert")
+ let addButtonStr = String(localized: "Add", comment: "button in the addExchange alert")
+
+ let sortedList = List(bankAccounts, id: \.self) { account in
+ BankSectionView(stack: stack.push(),
+ account: account)
+ }
+
+ let emptyList = List {
+ Section {
+ Text("There are no bank accounts yet.")
+ .talerFont(.title3)
+ }
+ Section {
+ let plus = Image(systemName: "plus")
+ if !minimalistic {
+ Text("Tap the \(plus) button to add an account.")
+ .listRowSeparator(.hidden)
+ Text("You can also scan a withdrawal QR code from your bank in the Action menu to automatically add a bank account.")
+ } else {
+ Text("Tap \(plus) or scan a withdrawal QR code from your bank to add a bank account.")
+ }
+ }
+ .talerFont(.body)
+ }
+
+ Group {
+ if bankAccounts.isEmpty {
+ emptyList
+ } else {
+ sortedList
+ }
+ }
+ .listStyle(myListStyle.style).anyView
+ .refreshable {
+ controller.hapticNotification(.success)
+ symLog.log("refreshing")
+ await viewDidLoad()
+ }
+ .task { await viewDidLoad() }
+ .navigationTitle(navTitle)
+ .navigationBarItems(trailing: plusButton)
+ .onAppear() {
+ DebugViewC.shared.setViewID(VIEW_PAYMENT_SERVICES, stack: stack.push())
+ }
+ } // body
+}
diff --git a/TalerWallet1/Views/Settings/Bank/BankSectionView.swift b/TalerWallet1/Views/Settings/Bank/BankSectionView.swift
@@ -0,0 +1,124 @@
+/*
+ * 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 BankSectionView: 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
+
+ @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
+
+ @MainActor
+ private func viewDidLoad() async {
+// if let exc = try? await model.listExchanges(scope: balance.scopeInfo) {
+// withAnimation { exchanges = exc }
+// }
+ }
+
+ @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
+ Section {
+ if let alias = account.alias {
+ Text(alias)
+ }
+ let payURL = URL(string: account.paytoUri)
+ if let queryParameters = payURL?.queryParameters {
+ let name = if let rcv = queryParameters["receiver-name"] {
+ rcv.replacingOccurrences(of: "+", with: " ")
+ } else {
+ EMPTYSTRING
+ }
+ Text("Holder: \(name)")
+
+
+// let amountStr = queryParameters["amount"] ?? EMPTYSTRING
+// let messageStr = queryParameters["message"] ?? EMPTYSTRING
+// let senderStr = queryParameters["sender-name"] ?? EMPTYSTRING
+
+ }
+
+ let method = if let iban = payURL?.iban {
+ Text("IBAN: \(iban)")
+ } else if let xTaler = payURL?.xTaler {
+ Text("TALER: \(xTaler)")
+ } else {
+ Text("unknown payment method")
+ }
+ method
+
+ let kyc = account.kycCompleted ? String(localized: "verified")
+ : String(localized: "not yet verified")
+ Text(kyc)
+ .padding(.leading)
+
+ if account.currencies.count == 1 {
+ let currency = account.currencies[0]
+ Text("Currency: \(currency)")
+ } else {
+ Text("Currencies:")
+ ForEach (account.currencies, id: \.self) { currency in
+ Text(currency)
+ .padding(.leading)
+ }
+ }
+ Button {
+ deleteAccount()
+ } label: {
+ Label("Delete Account", systemImage: "trash")
+ }
+ .buttonStyle(TalerButtonStyle(type: .bordered, disabled: disabled))
+ .disabled(disabled)
+ } header: {
+ }
+ .task { await viewDidLoad() }
+// .task(id: controller.currencyTicker) { await currencyTickerChanged(scopeInfo) }
+ .onDisappear() {
+ disabled = false
+ purge = false
+ }
+ }
+}
diff --git a/TalerWallet1/Views/Settings/SettingsView.swift b/TalerWallet1/Views/Settings/SettingsView.swift
@@ -107,6 +107,15 @@ struct SettingsView: View {
SettingsItem(name: exchangesTitle, id1: "exchanges",
description: hideDescriptions ? nil : String(localized: "Manage payment services...")) {}
}
+ let bankAccountsTitle = String(localized: "TitleBankAccounts", defaultValue: "Bank Accounts")
+ let bankAccountsDest = BankListView(stack: stack.push(bankAccountsTitle),
+ navTitle: bankAccountsTitle)
+ NavigationLink { // whole row like in a tableView
+ bankAccountsDest
+ } label: {
+ SettingsItem(name: bankAccountsTitle, id1: "bankAccounts",
+ description: hideDescriptions ? nil : String(localized: "Your accounts for deposit...")) {}
+ }
SettingsToggle(name: String(localized: "Minimalistic"), value: $minimalistic, id1: "minimal",
description: hideDescriptions ? nil : String(localized: "Omit text where possible")) {
hideDescriptions = minimalistic //withAnimation { hideDescriptions = minimalistic }