taler-ios

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

commit a914a934b9268d65b8db53ee4d2d1723b47b5f3d
parent f26e277802ecf62ad042cb485c4cfc981c773368
Author: Marc Stibane <marc@taler.net>
Date:   Fri, 17 Nov 2023 16:46:58 +0100

Notifications

Diffstat:
MTalerWallet1/Backend/WalletCore.swift | 52++++++++++++++++++++++++++++++++++++----------------
MTalerWallet1/Controllers/PublicConstants.swift | 10+++++++++-
MTalerWallet1/Views/Transactions/TransactionDetailView.swift | 92++++++++++++++++++++++++++++++++++++++++---------------------------------------
3 files changed, 92 insertions(+), 62 deletions(-)

diff --git a/TalerWallet1/Backend/WalletCore.swift b/TalerWallet1/Backend/WalletCore.swift @@ -184,26 +184,46 @@ extension WalletCore { @MainActor private func handleStateTransition(_ jsonData: Data) throws { do { let decoded = try JSONDecoder().decode(TransactionTransition.self, from: jsonData) - if decoded.newTxState != decoded.oldTxState { - let components = decoded.transactionId.components(separatedBy: ":") - if components.count >= 3 { // txn:$txtype:$uid - if let type = TransactionType(rawValue: components[1]) { - guard type != .refresh else { return } - if decoded.newTxState.major == .done { + guard decoded.newTxState != decoded.oldTxState else { + // TODO: Same state usually means that an error is transmitted + logger.info("No State change: \(decoded.transactionId, privacy: .private(mask: .hash))") + return + } + let components = decoded.transactionId.components(separatedBy: ":") + if components.count >= 3 { // txn:$txtype:$uid + if let type = TransactionType(rawValue: components[1]) { + guard type != .refresh else { return } + switch decoded.newTxState.major { + case .done: logger.info("Done: \(decoded.transactionId, privacy: .private(mask: .hash))") Controller.shared.playSound(type.isIncoming ? 2 : 1) - } else if decoded.newTxState.major == .expired { + postNotification(.TransactionDone, + userInfo: [TRANSACTIONTRANSITION: decoded]) + case .expired: logger.log("Expired: \(decoded.transactionId, privacy: .private(mask: .hash))") Controller.shared.playSound(0) - } - postNotification(.TransactionStateTransition, - userInfo: [TRANSACTIONTRANSITION: decoded]) - } - } - } else { - // TODO: Same state usually means that an error is transmitted - logger.info("No State change: \(decoded.transactionId, privacy: .private(mask: .hash))") - } + postNotification(.TransactionExpired, + userInfo: [TRANSACTIONTRANSITION: decoded]) + case .pending: + if let newMinor = decoded.newTxState.minor { + if newMinor == .ready { + postNotification(.PendingReady, + userInfo: [TRANSACTIONTRANSITION: decoded]) + } else if newMinor == .exchangeWaitReserve // user did confirm on bank website + || newMinor == .withdrawCoins { // coin-withdrawal has started + postNotification(.DismissSheet, + userInfo: [TRANSACTIONTRANSITION: decoded]) + } else if newMinor == .kyc { // user did confirm on bank website, but KYC is needed + postNotification(.KYCrequired, + userInfo: [TRANSACTIONTRANSITION: decoded]) + } + } + default: break + } // switch + postNotification(.TransactionStateTransition, + userInfo: [TRANSACTIONTRANSITION: decoded]) + } // type + } // 3 components } catch { // rethrows symLog.log(jsonData) // TODO: .error throw WalletBackendError.deserializationError diff --git a/TalerWallet1/Controllers/PublicConstants.swift b/TalerWallet1/Controllers/PublicConstants.swift @@ -46,12 +46,20 @@ public let EXCHANGEBASEURL = "exchangeBaseUrl" public let TALERURI = "talerUri" public let TRANSACTIONTRANSITION = "transactionTransition" +public let TRANSACTIONID = "transactionID" /// Notifications sent by wallet-core extension Notification.Name { static let BalanceChange = Notification.Name("balance-change") - static let TransactionStateTransition = Notification.Name(TransactionTransition.TransitionType.transition.rawValue) static let ExchangeAdded = Notification.Name("exchange-added") + static let TransactionStateTransition = Notification.Name(TransactionTransition.TransitionType.transition.rawValue) + static let TransactionDone = Notification.Name("transaction-done") + static let TransactionExpired = Notification.Name("transaction-expired") + static let PendingReady = Notification.Name("pending-ready") + static let WaitReserve = Notification.Name("wait-reserve") + static let WithdrawCoins = Notification.Name("withdraw-coins") + static let KYCrequired = Notification.Name("kyc-required") + static let DismissSheet = Notification.Name("dismiss-sheet") static let PendingOperationProcessed = Notification.Name("pending-operation-processed") static let ReserveNotYetFound = Notification.Name("reserve-not-yet-found") // static let WithdrawalGroupBankConfirmed = Notification.Name("withdrawal-group-bank-confirmed") diff --git a/TalerWallet1/Views/Transactions/TransactionDetailView.swift b/TalerWallet1/Views/Transactions/TransactionDetailView.swift @@ -56,6 +56,31 @@ struct TransactionDetailView: View { return nil } + func loadTransaction(_ txId: String) async { + do { + let reloadedTransaction = try await reloadAction(txId) + symLog.log("reloaded transaction: \(reloadedTransaction.common.txState.major)") + withAnimation() { transaction = reloadedTransaction; viewId = UUID() } // redraw + } catch { + symLog.log(error.localizedDescription) + withAnimation() { transaction = Transaction(dummyCurrency: DEMOCURRENCY); viewId = UUID() } + } + } + + @discardableResult + func checkDismiss(_ notification: Notification, _ logStr: String = "") -> Bool { + if let doneAction { + if let transition = notification.userInfo?[TRANSACTIONTRANSITION] as? TransactionTransition { + if transition.transactionId == transaction.common.transactionId { // is the transition for THIS transaction? + symLog.log(logStr) + doneAction() // if this view is in a sheet then dissmiss the sheet + return true + } + } + } + return false + } + var body: some View { #if DEBUG let _ = Self._printChanges() @@ -116,45 +141,28 @@ struct TransactionDetailView: View { .padding(.horizontal) } } - }.onNotification(.TransactionStateTransition) { notification in + } + .onNotification(.TransactionExpired) { notification in + if checkDismiss(notification, "newTxState.major == expired => dismiss sheet") { + // TODO: logger.info("newTxState.major == expired => dismiss sheet") + } + } + .onNotification(.TransactionDone) { notification in + checkDismiss(notification, "newTxState.major == done => dismiss sheet") + } + .onNotification(.DismissSheet) { notification in + checkDismiss(notification, "exchangeWaitReserve or withdrawCoins => dismiss sheet") + } + .onNotification(.TransactionStateTransition) { notification in if let transition = notification.userInfo?[TRANSACTIONTRANSITION] as? TransactionTransition { if transition.transactionId == common.transactionId { // is the transition for THIS transaction? let newMajor = transition.newTxState.major - let newMinor = transition.newTxState.minor - if let doneAction { - if newMajor == .done { - symLog.log("newTxState.major == done => dismiss sheet") -// TODO: logger.info("newTxState.major == done => dismiss sheet") - doneAction() // if this view is in a sheet this action will dissmiss it - } else if newMajor == .expired { - symLog.log("newTxState.major == expired => dismiss sheet") -// TODO: logger.info("newTxState.major == expired => dismiss sheet") - doneAction() // if this view is in a sheet this action will dissmiss it - } else if newMajor == .pending { - if let newMinor { - if newMinor == .exchangeWaitReserve { // user did confirm on bank website - symLog.log("newTxState.minor == exchangeWaitReserve => dismiss sheet") - doneAction() // if this view is in a sheet this action will dissmiss it - } else if newMinor == .withdrawCoins { // coin-withdrawal has started - symLog.log("newTxState.minor == withdrawCoins => dismiss sheet") - doneAction() // if this view is in a sheet this action will dissmiss it - } else { - symLog.log("ignoring newTxState: \(newMajor):\(newMinor)") - } - } - } else { - symLog.log("ignoring newTxState.major: \(newMajor)") - } - } else { Task { // runs on MainActor - do { - symLog.log("newState: \(newMajor), reloading transaction") - withAnimation() { transaction = Transaction(dummyCurrency: DEMOCURRENCY); viewId = UUID() } - let reloadedTransaction = try await reloadAction(common.transactionId) - symLog.log("reloaded transaction: \(reloadedTransaction.common.txState.major)") - withAnimation() { transaction = reloadedTransaction; viewId = UUID() } // redraw - } catch { - symLog.log(error.localizedDescription) - }}} + Task { // runs on MainActor + // flush the screen first, then reload + withAnimation() { transaction = Transaction(dummyCurrency: DEMOCURRENCY); viewId = UUID() } + symLog.log("newState: \(newMajor), reloading transaction") + await loadTransaction(common.transactionId) + } } } else { // Yikes - should never happen // TODO: logger.warning("Can't get notification.userInfo as TransactionTransition") @@ -163,14 +171,8 @@ struct TransactionDetailView: View { } .navigationTitle(navTitle ?? navTitle2) .task { - do { - symLog.log("task - load transaction") - let reloadedTransaction = try await reloadAction(transactionId) - withAnimation() { transaction = reloadedTransaction; viewId = UUID() } // redraw - } catch { - symLog.log(error) - withAnimation() { transaction = Transaction(dummyCurrency: DEMOCURRENCY); viewId = UUID() } - } + symLog.log("task - load transaction") + await loadTransaction(transactionId) } .onAppear { symLog.log("onAppear") @@ -224,7 +226,7 @@ struct TransactionDetailView: View { } } } - } + } // switch } // ManualDetails or Confirm with bank ThreeAmountsSheet(common: common, topAbbrev: String(localized: "Chosen:"), topTitle: String(localized: "Chosen amount to withdraw:"),