taler-ios

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

commit 31682e6c11debc4b2575d95bcf4809a11e274325
parent 085b089f4fd3d63c162e5d3b6f1d0bb675406a81
Author: Marc Stibane <marc@taler.net>
Date:   Sat, 19 Oct 2024 17:20:02 +0200

iterate over balances

Diffstat:
MTalerWallet1/Model/Model+Exchange.swift | 63+++++++++++++++++----------------------------------------------
MTalerWallet1/Views/Settings/Exchange/ExchangeListView.swift | 59++++++++++++++++++++---------------------------------------
MTalerWallet1/Views/Settings/Exchange/ExchangeRowView.swift | 39+++++++++++++++------------------------
MTalerWallet1/Views/Settings/Exchange/ExchangeSectionView.swift | 41++++++++++++++++++++++++-----------------
4 files changed, 76 insertions(+), 126 deletions(-)

diff --git a/TalerWallet1/Model/Model+Exchange.swift b/TalerWallet1/Model/Model+Exchange.swift @@ -87,34 +87,18 @@ struct Exchange: Codable, Hashable, Identifiable { return nil } } - -struct ExchangeUrlItem: Codable, Hashable, Identifiable { - var exchangeBaseUrl: String - var id: String { exchangeBaseUrl } -} // MARK: - /// A request to list exchanges names for a currency -fileprivate struct ListExchangesForScopedCurrency: WalletBackendFormattedRequest { - func operation() -> String { "listExchangesForScopedCurrency" } - func args() -> Args { Args(scope: scope) } - - var scope: ScopeInfo - - struct Args: Encodable { - var scope: ScopeInfo - } - struct Response: Decodable, Sendable { - var exchanges: [ExchangeUrlItem] // list of Exchange names - } -} - -/// A request to list exchanges. fileprivate struct ListExchanges: WalletBackendFormattedRequest { func operation() -> String { "listExchanges" } - func args() -> Args { Args() } - - struct Args: Encodable {} // no arguments needed + func args() -> Args { Args(filterByScope: scope, filterByExchangeEntryStatus: filterByStatus) } + var scope: ScopeInfo? + var filterByStatus: ExchangeEntryStatus? + struct Args: Encodable { + var filterByScope: ScopeInfo? + var filterByExchangeEntryStatus: ExchangeEntryStatus? + } struct Response: Decodable { // list of known exchanges var exchanges: [Exchange] } @@ -122,7 +106,6 @@ fileprivate struct ListExchanges: WalletBackendFormattedRequest { /// A request to get info for one exchange. fileprivate struct GetExchangeByUrl: WalletBackendFormattedRequest { - typealias Response = Exchange func operation() -> String { "getExchangeEntryByUrl" } func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl) } @@ -131,72 +114,69 @@ fileprivate struct GetExchangeByUrl: WalletBackendFormattedRequest { struct Args: Encodable { var exchangeBaseUrl: String } + typealias Response = Exchange } /// A request to update a single exchange. fileprivate struct UpdateExchange: WalletBackendFormattedRequest { - struct Response: Decodable {} // no result - getting no error back means success func operation() -> String { "updateExchangeEntry" } // func args() -> Args { Args(scopeInfo: scopeInfo) } func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl) } // var scopeInfo: ScopeInfo var exchangeBaseUrl: String - struct Args: Encodable { var exchangeBaseUrl: String } + struct Response: Decodable {} // no result - getting no error back means success } /// A request to add an exchange. fileprivate struct AddExchange: WalletBackendFormattedRequest { - struct Response: Decodable {} // no result - getting no error back means success func operation() -> String { "addExchange" } // addExchangeEntry func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl) } var exchangeBaseUrl: String - struct Args: Encodable { var exchangeBaseUrl: String } + struct Response: Decodable {} // no result - getting no error back means success } /// A request to delete an exchange. fileprivate struct DeleteExchange: WalletBackendFormattedRequest { - struct Response: Decodable {} // no result - getting no error back means success func operation() -> String { "deleteExchange" } func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl, purge: purge) } var exchangeBaseUrl: String var purge: Bool - struct Args: Encodable { var exchangeBaseUrl: String var purge: Bool } + struct Response: Decodable {} // no result - getting no error back means success } /// A request to get info about a currency fileprivate struct GetCurrencySpecification: WalletBackendFormattedRequest { - struct Response: Codable, Sendable { - let currencySpecification: CurrencySpecification - } func operation() -> String { "getCurrencySpecification" } func args() -> Args { Args(scope: scope) } var scope: ScopeInfo - struct Args: Encodable { var scope: ScopeInfo } + struct Response: Codable, Sendable { + let currencySpecification: CurrencySpecification + } } // MARK: - extension WalletModel { /// ask wallet-core for its list of known exchanges - @MainActor func listExchangesForScopedCurrencyM(scope: ScopeInfo, viewHandles: Bool = false) - async -> [ExchangeUrlItem] { // M for MainActor + @MainActor func listExchangesM(scope: ScopeInfo?, filterByStatus: ExchangeEntryStatus? = nil, viewHandles: Bool = false) + async -> [Exchange] { // M for MainActor do { - let request = ListExchangesForScopedCurrency(scope: scope) + let request = ListExchanges(scope: scope, filterByStatus: filterByStatus) // .used, .preset let response = try await sendRequest(request, ASYNCDELAY, viewHandles: viewHandles) return response.exchanges } catch { @@ -204,14 +184,6 @@ extension WalletModel { } } - /// ask wallet-core for its list of known exchanges - @MainActor func listExchangesM(devMode: Bool = false, viewHandles: Bool = false) - async throws -> [Exchange] { // M for MainActor - let request = ListExchanges() - let response = try await sendRequest(request, ASYNCDELAY, viewHandles: viewHandles) - return response.exchanges - } - /// add a new exchange with URL to the wallet's list of known exchanges func getExchangeByUrl(url: String, viewHandles: Bool = false) async throws -> Exchange { @@ -258,5 +230,4 @@ extension WalletModel { formatter: CurrencyFormatter.formatter(scope: scope, specs: response.currencySpecification)) } - } diff --git a/TalerWallet1/Views/Settings/Exchange/ExchangeListView.swift b/TalerWallet1/Views/Settings/Exchange/ExchangeListView.swift @@ -66,41 +66,22 @@ struct ExchangeListView: View { } } // MARK: - -struct ExchangeListCommonV { +struct ExchangeListCommonV: View { let symLog: SymLogV? let stack: CallStack @EnvironmentObject private var model: WalletModel @EnvironmentObject private var controller: Controller @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic -// @AppStorage("depositIBAN") var depositIBAN = EMPTYSTRING -// @AppStorage("accountHolder") var accountHolder = EMPTYSTRING - @State private var exchanges: [Exchange] = [] - - // source of truth for the value the user enters in currencyField for exchange withdrawals - @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING) // TODO: Hold different values for different currencies? - - func reloadExchanges() async -> Void { - if let exc = try? await model.listExchangesM() { - withAnimation { exchanges = exc } - } - } -} -// MARK: - -extension ExchangeListCommonV: View { 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 sortedExchanges = exchanges.sorted { $0 < $1 } - let sortedList = List(sortedExchanges, id: \.self) { exchange in + let sortedList = List(controller.balances, id: \.self) { balance in ExchangeSectionView(stack: stack.push(), - exchange: exchange, -// depositIBAN: $depositIBAN, -// accountHolder: $accountHolder, - amountToTransfer: $amountToTransfer) // does still have the wrong currency + balance: balance) } let emptyList = List { @@ -118,9 +99,8 @@ extension ExchangeListCommonV: View { } } - // TODO: Balances for amountAvailable for Deposit Group { - if exchanges.isEmpty { + if controller.balances.isEmpty { emptyList } else { sortedList @@ -131,25 +111,26 @@ extension ExchangeListCommonV: View { .refreshable { controller.hapticNotification(.success) symLog?.log("refreshing") - await reloadExchanges() +// await reloadExchanges() + NotificationCenter.default.post(name: .BalanceChange, object: nil, userInfo: nil) } //#endif .onAppear() { DebugViewC.shared.setViewID(VIEW_BANKING, stack: stack.push()) } - .onNotification(.ExchangeAdded) { notification in - // doesn't need to be received on main thread because we just reload in the background anyway - symLog?.log(".onNotification(.ExchangeAdded) ==> reloading exchanges") - Task { await reloadExchanges() } // runs on MainActor - } - .onNotification(.ExchangeDeleted) { notification in - // doesn't need to be received on main thread because we just reload in the background anyway - symLog?.log(".onNotification(.ExchangeDeleted) ==> reloading exchanges") - Task { await reloadExchanges() } // runs on MainActor - } - .task { - symLog?.log(".task") - await reloadExchanges() - } +// .onNotification(.ExchangeAdded) { notification in +// // doesn't need to be received on main thread because we just reload in the background anyway +// symLog?.log(".onNotification(.ExchangeAdded) ==> reloading exchanges") +// Task { await reloadExchanges() } // runs on MainActor +// } +// .onNotification(.ExchangeDeleted) { notification in +// // doesn't need to be received on main thread because we just reload in the background anyway +// symLog?.log(".onNotification(.ExchangeDeleted) ==> reloading exchanges") +// Task { await reloadExchanges() } // runs on MainActor +// } +// .task { +// symLog?.log(".task") +// await reloadExchanges() +// } } // body } diff --git a/TalerWallet1/Views/Settings/Exchange/ExchangeRowView.swift b/TalerWallet1/Views/Settings/Exchange/ExchangeRowView.swift @@ -12,9 +12,8 @@ import SymLog struct ExchangeRowView: View { private let symLog = SymLogV(0) let stack: CallStack - @Binding var currencyInfo: CurrencyInfo + @Binding var currencyInfo: CurrencyInfo // this is the currency to be used let exchange: Exchange - @Binding var amountToTransfer: Amount // does still have the wrong currency @Environment(\.sizeCategory) var sizeCategory @EnvironmentObject private var controller: Controller @@ -30,26 +29,21 @@ struct ExchangeRowView: View { let delay: UInt = 0 #endif let baseURL = exchange.exchangeBaseUrl - let showToS = LazyView { - WithdrawTOSView(stack: stack.push(), - exchangeBaseUrl: baseURL, - viewID: VIEW_WITHDRAW_TOS, - acceptAction: nil) // pop back to here - } - Group { - NavigationLink(destination: showToS) { - VStack(alignment: .leading) { - Text(baseURL.trimURL) - .talerFont(.headline) - if !minimalistic { - Text("Terms of Service") // VIEW_WITHDRAW_TOS - .talerFont(.body) - } - } + let rowView = VStack(alignment: .leading) { + Text(baseURL.trimURL) + .talerFont(.headline) + if !minimalistic { + Text("Terms of Service") // VIEW_WITHDRAW_TOS + .talerFont(.body) } - .listRowSeparator(.hidden) - } + let showToS = WithdrawTOSView(stack: stack.push(), + exchangeBaseUrl: baseURL, + viewID: VIEW_WITHDRAW_TOS, + acceptAction: nil) // pop back to here + Group { + NavigationLink(destination: showToS) { rowView } + }.listRowSeparator(.hidden) } } // MARK: - @@ -83,10 +77,7 @@ fileprivate struct ExchangeRow_Previews: PreviewProvider { ageRestrictionOptions: []) ExchangeRowView(stack: CallStack("Preview"), currencyInfo: $currencyInfoL, - exchange: exchange1, -// depositIBAN: $depositIBAN, -// accountHolder: $accountHolder, - amountToTransfer: $amountToPreview) + exchange: exchange1) } } diff --git a/TalerWallet1/Views/Settings/Exchange/ExchangeSectionView.swift b/TalerWallet1/Views/Settings/Exchange/ExchangeSectionView.swift @@ -11,20 +11,17 @@ import SymLog /// This view shows the currency name in an exchange section /// currency -/// [Deposit Coins] [Withdraw Coins] struct ExchangeSectionView: View { private let symLog = SymLogV(0) let stack: CallStack - let exchange: Exchange -// let exchanges: [Exchange] -// @Binding var depositIBAN: String -// @Binding var accountHolder: String - @Binding var amountToTransfer: Amount // does still have the wrong currency + let balance: Balance + @EnvironmentObject private var model: WalletModel @EnvironmentObject private var controller: Controller @AppStorage("minimalistic") var minimalistic: Bool = false @AppStorage("hideSpendingHint") var hideSpendingHint: Bool = false + @State private var exchanges: [Exchange] = [] @State private var shouldReloadBalances: Int = 0 @State private var currencyInfo: CurrencyInfo = CurrencyInfo.zero(UNKNOWN) @State private var didDelete: Bool = false @@ -49,9 +46,20 @@ struct ExchangeSectionView: View { // return nil // } + private func viewDidLoad() async { + if let exc = try? await model.listExchangesM(scope: balance.scopeInfo) { + withAnimation { exchanges = exc } + } + } + private func currencyTickerChanged(_ scopeInfo: ScopeInfo) async { + symLog.log("task \(didDelete ? 1 : 0)") + currencyInfo = controller.info(for: scopeInfo, controller.currencyTicker) + } private func deleteExchange() { disabled = true // don't try this more than once Task { // runs on MainActor + if exchanges.count > 0 { + let exchange = exchanges[0] let baseUrl = exchange.exchangeBaseUrl if let _ = try? await model.deleteExchange(url: baseUrl, purge: purge, viewHandles: !purge) { purge = false @@ -65,6 +73,7 @@ struct ExchangeSectionView: View { showAlert = true disabled = false } + } } } @@ -73,17 +82,16 @@ struct ExchangeSectionView: View { let _ = Self._printChanges() // let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear #endif - let scopeInfo = exchange.scopeInfo + let scopeInfo = balance.scopeInfo let currency = scopeInfo.currency // let currencyInfo = controller.info(for: currency, controller.currencyTicker) Section { -// ForEach(exchanges) { exchange in + ForEach(exchanges) { exchange in ExchangeRowView(stack: stack.push(), currencyInfo: $currencyInfo, - exchange: exchange, - amountToTransfer: $amountToTransfer) // does still have the wrong currency + exchange: exchange) .listRowSeparator(.hidden) -// } + } if DEMOCURRENCY == currency { let bankingHint = String(localized: "Since the demo bank supports the Taler integration, you can start a withdrawal directly on the") let linkTitle = String(localized: "LinkTitle_DEMOBANK", defaultValue: "Demo Bank Website") @@ -118,18 +126,17 @@ struct ExchangeSectionView: View { deleteExchange() showAlert = false } - }, message: { Text("You will loose all \(currency) of this payment provider") } - ) + }, message: { + Text("You will loose all \(currency) of this payment provider") + }) } header: { BarGraphHeader(stack: stack.push(), scopeInfo: scopeInfo, currencyInfo: $currencyInfo, shouldReloadBalances: $shouldReloadBalances) } - .task(id: controller.currencyTicker) { - symLog.log("task \(didDelete ? 1 : 0)") - currencyInfo = controller.info2(for: currency, controller.currencyTicker) - } + .task { await viewDidLoad() } + .task(id: controller.currencyTicker) { await currencyTickerChanged(scopeInfo) } .onDisappear() { disabled = false purge = false