commit c93c70b35ebe749b525fa93ebeb8bdc32b7677c6
parent 7fc8573778758eb930040148518e1316942e1b62
Author: Marc Stibane <marc@taler.net>
Date: Tue, 1 Sep 2026 16:06:37 +0200
-model, update exchange
Diffstat:
10 files changed, 60 insertions(+), 42 deletions(-)
diff --git a/TalerWallet1/Backend/WalletCore.swift b/TalerWallet1/Backend/WalletCore.swift
@@ -85,7 +85,8 @@ class WalletCore: QuickjsMessageHandler {
let isInternal: Bool?
let hintTransactionId: String?
let event: [String: AnyCodable]?
-
+ let exchangeBaseUrl: String?
+ let newExchangeState: ExchangeState?
let operation: String? // indexeddb-to-native-migration
let phase: String? // fixup | copy | verify | complete | failed
let error: TalerErrorDetail? // only when phase is "failed"
@@ -393,6 +394,10 @@ extension WalletCore {
break
case Notification.Name.ExchangeStateTransition.rawValue:
symLog.log(message)
+ if let baseUrl = payload.exchangeBaseUrl,
+ let state = payload.newExchangeState {
+ Controller.shared.updateExchange(for: baseUrl, state: state)
+ }
break
case Notification.Name.RequestProgressError.rawValue:
symLog.log(message)
diff --git a/TalerWallet1/Controllers/Controller.swift b/TalerWallet1/Controllers/Controller.swift
@@ -127,6 +127,7 @@ class Controller: ObservableObject {
private let symLog = SymLogC()
@Published var haveProdBalance: Bool = false
+ @Published var exchanges: [Exchange] = []
@Published var balances: [Balance] = []
@Published var discounts: [TalerToken] = []
@Published var subscriptions: [TalerToken] = []
@@ -163,7 +164,6 @@ class Controller: ObservableObject {
let player = AVQueuePlayer()
let semaphore = AsyncSemaphore(value: 1)
private var currencyInfos: [ScopeInfo : CurrencyInfo]
- var exchanges: [Exchange]
var messageForSheet: String? = nil
var isLoadingChoices: String? = nil
@@ -347,13 +347,14 @@ class Controller: ObservableObject {
// MARK: -
@MainActor
@discardableResult
- func loadBalances(_ stack: CallStack,_ model: WalletModel) async -> Int? {
+ func loadBalances(_ stack: CallStack) async -> Int? {
+ let model = WalletModel.shared
if let response = try? await model.getBalances(stack.push()) {
let reloaded = response.balances
if reloaded != balances {
for balance in reloaded {
let scope = balance.scopeInfo
- checkInfo(for: scope, model: model)
+ checkInfo(for: scope)
}
self.logger.log("••Got new balances, will redraw")
balances = reloaded // redraw
@@ -377,7 +378,8 @@ class Controller: ObservableObject {
// MARK: -
@MainActor
@discardableResult
- func loadDiscounts(_ stack: CallStack,_ model: WalletModel) async -> Int? {
+ func loadDiscounts(_ stack: CallStack) async -> Int? {
+ let model = WalletModel.shared
if let response = try? await model.listDiscounts(stack.push()) {
let reloaded = response.discounts
if reloaded != discounts {
@@ -393,7 +395,8 @@ class Controller: ObservableObject {
// MARK: -
@MainActor
@discardableResult
- func loadSubscriptions(_ stack: CallStack,_ model: WalletModel) async -> Int? {
+ func loadSubscriptions(_ stack: CallStack) async -> Int? {
+ let model = WalletModel.shared
if let response = try? await model.listSubscriptions(stack.push()) {
let reloaded = response.subscriptions
if reloaded != subscriptions {
@@ -410,9 +413,9 @@ class Controller: ObservableObject {
@MainActor
@discardableResult
func loadChoicesForPayment(_ stack: CallStack,
- _ model: WalletModel,
txId: String) async -> Bool {
self.logger.log("getChoicesForPayment: \(txId)")
+ let model = WalletModel.shared
if isLoadingChoices != txId {
isLoadingChoices = txId
if let choiceResponse = try? await model.getChoicesForPayment(txId) {
@@ -429,15 +432,6 @@ class Controller: ObservableObject {
return false
}
// MARK: -
- func exchange(for baseUrl: String) -> Exchange? {
- for exchange in exchanges {
- if exchange.exchangeBaseUrl == baseUrl {
- return exchange
- }
- }
- return nil
- }
-
func info(for scope: ScopeInfo) -> CurrencyInfo? {
// return CurrencyInfo.euro() // Fake EUR instead of the real Currency
// return CurrencyInfo.francs() // Fake CHF instead of the real Currency
@@ -479,11 +473,12 @@ class Controller: ObservableObject {
}
@MainActor
- func exchange(for baseUrl: String?, model: WalletModel) async -> Exchange? {
+ func exchange(for baseUrl: String?) async -> Exchange? {
if let baseUrl {
- if let exchange1 = exchange(for: baseUrl) {
- return exchange1
+ if let exchange = exchanges.first(where: {$0.exchangeBaseUrl == baseUrl}) {
+ return exchange
}
+ let model = WalletModel.shared
if let exchange2 = try? await model.getExchangeByUrl(url: baseUrl) {
// logger.log(" ❗️ will add \(baseUrl)")
exchanges.append(exchange2)
@@ -492,21 +487,40 @@ class Controller: ObservableObject {
}
return nil
}
+ @MainActor
+ func updateExchange(for baseUrl: String, state: ExchangeState) {
+ Task {
+ if let exchange = await exchange(for: baseUrl) {
+ if exchange.tosStatus != state.tosStatus {
+ logger.log(" ❗️updating exchange: \(baseUrl)")
+ let model = WalletModel.shared
+ if let exchange2 = try? await model.getExchangeByUrl(url: baseUrl) {
+ if let index = exchanges.firstIndex(where: {$0.exchangeBaseUrl == baseUrl}) {
+ exchanges[index] = exchange2
+ } else {
+ exchanges.append(exchange2)
+ }
+ }
+ }
+ }
+ }
+ }
@MainActor
- func updateInfo(_ scope: ScopeInfo, model: WalletModel) async {
+ func updateInfo2(_ scope: ScopeInfo) async {
+ let model = WalletModel.shared
if let info = try? await model.getCurrencyInfo(scope: scope) {
await setInfo(info, for: scope)
// logger.log(" ❗️info set for \(scope.currency)")
}
}
- func checkCurrencyInfo(for baseUrl: String, model: WalletModel) async -> Exchange? {
- if let exchange = await exchange(for: baseUrl, model: model) {
+ func checkCurrencyInfo(for baseUrl: String) async -> Exchange? {
+ if let exchange = await exchange(for: baseUrl) {
let scope = exchange.scopeInfo
if currencyInfos[scope] == nil {
logger.log(" ❗️got no info for \(baseUrl.trimURL) \(scope.currency) -> will update")
- await updateInfo(scope, model: model)
+ await updateInfo2(scope)
}
return exchange
} else {
@@ -516,14 +530,14 @@ class Controller: ObservableObject {
}
/// called whenever a new currency pops up - will first load the Exchange and then currencyInfos
- func checkInfo(for scope: ScopeInfo, model: WalletModel) {
+ func checkInfo(for scope: ScopeInfo) {
if currencyInfos[scope] == nil {
Task {
- let exchange = await exchange(for: scope.url, model: model)
+ let exchange = await exchange(for: scope.url)
if let scope2 = exchange?.scopeInfo {
let exchangeName = scope2.url ?? "UNKNOWN"
logger.log(" ❗️got no info for \(scope.currency) -> will update \(exchangeName.trimURL)")
- await updateInfo(scope2, model: model)
+ await updateInfo2(scope2)
} else {
logger.error(" ❗️got no info for \(scope.currency), and couldn't load the exchange info❗️")
}
@@ -553,12 +567,13 @@ class Controller: ObservableObject {
}
// MARK: -
@MainActor
- func initWalletCore(_ model: WalletModel, setTesting: Bool, delay: TimeInterval)
+ func initWalletCore(setTesting: Bool, delay: TimeInterval)
async throws {
if backendState == .instantiated {
backendState = .initing
do {
let walletCore = WalletCore.shared
+ let model = WalletModel.shared
let response = try await model.initWalletCore(setTesting: setTesting)
walletCore.versionInfo = response.versionInfo
walletCore.nativeDB = (response.databaseBackend == "sqlite")
@@ -603,7 +618,7 @@ class Controller: ObservableObject {
// dismiss the launch animation
self.backendState = walletCore.nativeDB ? .ready : .update
}
- await loadBalances(CallStack(), model)
+ await loadBalances(CallStack())
} catch { // rethrows
self.logger.error("\(error.localizedDescription)")
backendState = .error(error.toEquatableError()) // ❗️Yikes app cannot continue
diff --git a/TalerWallet1/Controllers/TalerWallet1App.swift b/TalerWallet1/Controllers/TalerWallet1App.swift
@@ -126,7 +126,7 @@ struct TalerWallet1App: App {
let testing = false
let delay: TimeInterval = 2.25
#endif
- try! await controller.initWalletCore(model, setTesting: testing, delay: delay) // will (and should) crash on failure
+ try! await controller.initWalletCore(setTesting: testing, delay: delay) // will (and should) crash on failure
}
if #available(iOS 16.4, *) {
return WindowGroup {
@@ -173,7 +173,6 @@ struct TalerWallet1App: App {
mainView
}
}
-
}
}
// MARK: -
diff --git a/TalerWallet1/Views/Balances/BalancesListView.swift b/TalerWallet1/Views/Balances/BalancesListView.swift
@@ -38,7 +38,7 @@ struct BalancesListView: View {
func refresh() async {
controller.hapticNotification(.success)
symLog.log("refreshing balances")
- await controller.loadBalances(stack.push("refreshing balances"), model)
+ await controller.loadBalances(stack.push("refreshing balances"))
}
@ViewBuilder
diff --git a/TalerWallet1/Views/Balances/DiscountPasses.swift b/TalerWallet1/Views/Balances/DiscountPasses.swift
@@ -145,9 +145,9 @@ struct DiscountsPassesList: View {
func refresh() async {
// symLog.log("refreshing balances")
if isPass {
- await controller.loadSubscriptions(stack.push("refreshing passes"), model)
+ await controller.loadSubscriptions(stack.push("refreshing passes"))
} else {
- await controller.loadDiscounts(stack.push("refreshing discounts"), model)
+ await controller.loadDiscounts(stack.push("refreshing discounts"))
}
}
diff --git a/TalerWallet1/Views/Main/WalletEmptyView.swift b/TalerWallet1/Views/Main/WalletEmptyView.swift
@@ -129,13 +129,12 @@ struct ProdSectionView: View {
struct WalletEmptyHeader: View {
let stack: CallStack
- @EnvironmentObject private var model: WalletModel
@EnvironmentObject private var controller: Controller
func refresh() async {
controller.hapticNotification(.success)
// symLog.log("refreshing balances")
- await controller.loadBalances(stack.push("refreshing balances"), model)
+ await controller.loadBalances(stack.push("refreshing balances"))
}
var body: some View {
diff --git a/TalerWallet1/Views/Main/WalletMain.swift b/TalerWallet1/Views/Main/WalletMain.swift
@@ -371,9 +371,9 @@ struct WalletMain: View {
}
.task(id: shouldReloadBalances) { // runs once at launch, then on each onNotification(.BalanceChange)
// symLog.log(".task shouldReloadBalances \(shouldReloadBalances)")
- await controller.loadBalances(stack.push("refreshing balances"), model)
- await controller.loadDiscounts(stack.push("refreshing discounts"), model)
- await controller.loadSubscriptions(stack.push("refreshing passes"), model)
+ await controller.loadBalances(stack.push("refreshing balances"))
+ await controller.loadDiscounts(stack.push("refreshing discounts"))
+ await controller.loadSubscriptions(stack.push("refreshing passes"))
} // task
} // body
} // Content
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
@@ -50,7 +50,7 @@ struct P2pReceiveURIView: View {
if let ppResponse = try? await model.preparePeerPushCredit(url.trimmedString) {
let baseUrl = ppResponse.exchangeBaseUrl
exchange = try? await model.getExchangeByUrl(url: baseUrl)
- await controller.checkCurrencyInfo(for: baseUrl, model: model)
+ await controller.checkCurrencyInfo(for: baseUrl)
let oimCurrency = oimCurrency(ppResponse.scopeInfo, oimEuro: oimEuro)
cash.setCurrency(oimCurrency)
controller.removeURL(url) // tx is now saved by wallet-core
diff --git a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawUriScan.swift b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawUriScan.swift
@@ -123,7 +123,7 @@ struct WithdrawUriScan: View {
// await loadExchange(baseUrl) <- checkCurrencyInfo now returns the exchange
// symLog.log("\(baseUrl.trimURL) loaded")
- if let baseUrl, let someExchange = try? await controller.checkCurrencyInfo(for: baseUrl, model: model) {
+ if let baseUrl, let someExchange = try? await controller.checkCurrencyInfo(for: baseUrl) {
symLog.log("Info(for: \(baseUrl.trimURL) loaded")
exchange = someExchange
} else {
diff --git a/TalerWallet1/Views/Transactions/TransactionTypeDetail.swift b/TalerWallet1/Views/Transactions/TransactionTypeDetail.swift
@@ -146,8 +146,8 @@ struct TransactionTypeDetail: View {
let choicesForPayment = choicesTuple.1
if choicesTuple.0 != common.transactionId || choicesForPayment == nil {
symLog.log("❗️❗️ task: \(common.txState)")
- await controller.loadChoicesForPayment(stack.push(),model,
- txId: common.transactionId)
+ await controller.loadChoicesForPayment(stack.push(),
+ txId: common.transactionId)
} else {
symLog.log("❗️❗️ choicesForPayment exists, no need for task: \(state)")
}