taler-ios

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

commit a75849a9b23042a12d4c0f28615f0e4bd1c1747d
parent 185b6c0ad904af203233f8960b1116ebdeb332b1
Author: Marc Stibane <marc@taler.net>
Date:   Wed, 21 Jun 2023 20:29:03 +0200

Dummy

Diffstat:
MTalerWallet1/Backend/Transaction.swift | 54+++++++++++++++++++++++++++++++++++++++++++++++-------
MTalerWallet1/Views/Exchange/ManualWithdrawDone.swift | 13++++++-------
MTalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift | 12++++++------
MTalerWallet1/Views/Transactions/TransactionDetailView.swift | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
MTalerWallet1/Views/Transactions/TransactionRowView.swift | 5+++--
MTalerWallet1/Views/Transactions/TransactionsListView.swift | 2+-
MTalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptDone.swift | 12++++++------
7 files changed, 125 insertions(+), 51 deletions(-)

diff --git a/TalerWallet1/Backend/Transaction.swift b/TalerWallet1/Backend/Transaction.swift @@ -97,6 +97,7 @@ enum TxAction: String, Codable { struct TransactionCommon: Decodable { enum TransactionType: String, Codable { + case dummy case withdrawal case deposit case payment @@ -165,6 +166,7 @@ struct WithdrawalTransactionDetails: Decodable { struct WithdrawalTransaction { var common: TransactionCommon var details: WithdrawalTransactionDetails + var loaded: Timestamp } struct PaymentTransactionDetails: Decodable { @@ -180,6 +182,7 @@ struct PaymentTransactionDetails: Decodable { struct PaymentTransaction { var common: TransactionCommon var details: PaymentTransactionDetails + var loaded: Timestamp } struct RefundTransactionDetails: Decodable { @@ -193,6 +196,7 @@ struct RefundTransactionDetails: Decodable { struct RefundTransaction { var common: TransactionCommon var details: RefundTransactionDetails + var loaded: Timestamp } struct RewardTransactionDetails: Decodable { @@ -203,6 +207,7 @@ struct RewardTransactionDetails: Decodable { struct RewardTransaction { var common: TransactionCommon var details: RewardTransactionDetails + var loaded: Timestamp } //struct TipTransactionDetails: Decodable { @@ -213,6 +218,7 @@ struct RewardTransaction { //struct TipTransaction { // var common: TransactionCommon // var details: TipTransactionDetails +// var loaded: Timestamp //} enum RefreshReason: String, Decodable { @@ -238,6 +244,7 @@ struct RefreshTransactionDetails: Decodable { struct RefreshTransaction { var common: TransactionCommon var details: RefreshTransactionDetails + var loaded: Timestamp } struct P2pShortInfo: Codable { @@ -254,9 +261,15 @@ struct P2PTransactionDetails: Codable { struct P2PTransaction { var common: TransactionCommon var details: P2PTransactionDetails + var loaded: Timestamp +} +struct DummyTransaction { + var common: TransactionCommon + var loaded: Timestamp } enum Transaction: Decodable, Hashable, Identifiable { + case dummy (DummyTransaction) case withdrawal (WithdrawalTransaction) case payment (PaymentTransaction) case refund (RefundTransaction) @@ -268,29 +281,30 @@ enum Transaction: Decodable, Hashable, Identifiable { init(from decoder: Decoder) throws { do { let common = try TransactionCommon.init(from: decoder) + let now = Timestamp.now() switch (common.type) { case .withdrawal: let details = try WithdrawalTransactionDetails.init(from: decoder) - self = .withdrawal(WithdrawalTransaction(common: common, details: details)) + self = .withdrawal(WithdrawalTransaction(common: common, details: details, loaded: now)) case .payment: let details = try PaymentTransactionDetails.init(from: decoder) - self = .payment(PaymentTransaction(common: common, details: details)) + self = .payment(PaymentTransaction(common: common, details: details, loaded: now)) case .refund: let details = try RefundTransactionDetails.init(from: decoder) - self = .refund(RefundTransaction(common: common, details: details)) + self = .refund(RefundTransaction(common: common, details: details, loaded: now)) case .reward: let details = try RewardTransactionDetails.init(from: decoder) - self = .reward(RewardTransaction(common: common, details: details)) + self = .reward(RewardTransaction(common: common, details: details, loaded: now)) // case .tip: // let details = try TipTransactionDetails.init(from: decoder) -// self = .tip(TipTransaction(common: common, details: details)) +// self = .tip(TipTransaction(common: common, details: details, loaded: now)) case .refresh: let details = try RefreshTransactionDetails.init(from: decoder) - self = .refresh(RefreshTransaction(common: common, details: details)) + self = .refresh(RefreshTransaction(common: common, details: details, loaded: now)) case .peerPushDebit, .peerPullCredit, .scanPullDebit, .scanPushCredit: let details = try P2PTransactionDetails.init(from: decoder) - self = .peer2peer(P2PTransaction(common: common, details: details)) + self = .peer2peer(P2PTransaction(common: common, details: details, loaded: now)) default: let context = DecodingError.Context( codingPath: decoder.codingPath, @@ -323,6 +337,7 @@ enum Transaction: Decodable, Hashable, Identifiable { var localizedType: String { switch common.type { + case .dummy: return String("Dummy") case .withdrawal: return String(localized: "Withdrawal") case .deposit: return String(localized: "Deposit") case .payment: return String(localized: "Payment") @@ -379,6 +394,8 @@ enum Transaction: Decodable, Hashable, Identifiable { var common: TransactionCommon { switch self { + case .dummy(let dummyTransaction): + return dummyTransaction.common case .withdrawal(let withdrawalTransaction): return withdrawalTransaction.common case .payment(let paymentTransaction): @@ -396,9 +413,32 @@ enum Transaction: Decodable, Hashable, Identifiable { } } + var loaded: Timestamp { + switch self { + case .dummy(let dummyTransaction): + return dummyTransaction.loaded + case .withdrawal(let withdrawalTransaction): + return withdrawalTransaction.loaded + case .payment(let paymentTransaction): + return paymentTransaction.loaded + case .refund(let refundTransaction): + return refundTransaction.loaded + case .reward(let rewardTransaction): + return rewardTransaction.loaded +// case .tip(let tipTransaction): +// return tipTransaction.loaded + case .refresh(let refreshTransaction): + return refreshTransaction.loaded + case .peer2peer(let p2pTransaction): + return p2pTransaction.loaded + } + } + func detailsToShow() -> Dictionary<String, String> { var result: [String:String] = [:] switch self { + case .dummy(let dummyTransaction): + break case .withdrawal(let withdrawalTransaction): result[EXCHANGEBASEURL] = withdrawalTransaction.details.exchangeBaseUrl case .payment(let paymentTransaction): diff --git a/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift b/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift @@ -17,7 +17,7 @@ struct ManualWithdrawDone: View { @EnvironmentObject private var model: WalletModel @State var acceptManualWithdrawalResult: AcceptManualWithdrawalResult? - @State var withdrawalTransaction: Transaction? + @State var transactionId: String? func reloadOneAction(_ transactionId: String) async throws -> Transaction { return try await model.getTransactionByIdT(transactionId) @@ -29,10 +29,10 @@ struct ManualWithdrawDone: View { let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear #endif VStack { - if let transaction = withdrawalTransaction { - TransactionDetailView(transaction: transaction, - reloadAction: reloadOneAction, - doneAction: ViewState.shared.popToRootView) + if let transactionId { + TransactionDetailView(transactionId: transactionId, + reloadAction: reloadOneAction, + doneAction: ViewState.shared.popToRootView) .navigationBarBackButtonHidden(true) // exit only by Done-Button .navigationTitle(navTitle) } else { @@ -48,8 +48,7 @@ struct ManualWithdrawDone: View { let result = try await model.sendAcceptManualWithdrawalM(exchange.exchangeBaseUrl, amount: amount, restrictAge: 0) print(result as Any) - let transaction = try await model.getTransactionByIdT(result!.transactionId) - withdrawalTransaction = transaction + transactionId = result!.transactionId } catch { // TODO: error symLog.log(error.localizedDescription) } diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift @@ -16,7 +16,7 @@ struct P2pAcceptDone: View { @EnvironmentObject private var model: WalletModel @State private var confirmTransferUrl: String? = nil - @State private var transaction: Transaction? = nil + @State private var transactionId: String? = nil func reloadOneAction(_ transactionId: String) async throws -> Transaction { return try await model.getTransactionByIdT(transactionId) @@ -28,10 +28,10 @@ struct P2pAcceptDone: View { let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear #endif VStack { - if let transaction { - TransactionDetailView(transaction: transaction, - reloadAction: reloadOneAction, - doneAction: { dismissTop() }) + if let transactionId { + TransactionDetailView(transactionId: transactionId, + reloadAction: reloadOneAction, + doneAction: { dismissTop() }) .navigationBarBackButtonHidden(true) .interactiveDismissDisabled() // can only use "Done" button to dismiss .navigationTitle(navTitle) @@ -47,7 +47,7 @@ struct P2pAcceptDone: View { if let exchangeBaseUrl { let result = try await model.sendAcceptIntWithdrawalM(exchangeBaseUrl, withdrawURL: url.absoluteString) confirmTransferUrl = result!.confirmTransferUrl - transaction = try await model.getTransactionByIdT(result!.transactionId) + transactionId = result!.transactionId } } catch { // TODO: error symLog.log(error.localizedDescription) diff --git a/TalerWallet1/Views/Transactions/TransactionDetailView.swift b/TalerWallet1/Views/Transactions/TransactionDetailView.swift @@ -6,12 +6,30 @@ import SwiftUI import taler_swift import SymLog +extension Transaction { // for Dummys + init(dummy: Bool) { + let amount = try! Amount(fromString: "KUDOS:0") + let now = Timestamp.now() + let common = TransactionCommon(type: .dummy, + txState: TransactionState(major: .pending), + amountEffective: amount, + amountRaw: amount, + transactionId: "", + timestamp: now, + txActions: []) + self = .dummy(DummyTransaction(common: common, loaded: now)) + } +} +// MARK: - struct TransactionDetailView: View { private let symLog = SymLogV() @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic @AppStorage("developerMode") var developerMode: Bool = false - @State var transaction: Transaction + var transactionId: String + @State var transaction: Transaction = Transaction(dummy: true) + @State var viewId: Int = 0 + var reloadAction: ((_ transactionId: String) async throws -> Transaction) var deleteAction: ((_ transactionId: String) async throws -> Void)? var abortAction: ((_ transactionId: String) async throws -> Void)? @@ -26,12 +44,15 @@ struct TransactionDetailView: View { #endif let common = transaction.common let pending = transaction.isPending + let loaded = TalerDater.dateString(from: transaction.loaded) let dateString = TalerDater.dateString(from: common.timestamp) let localizedType = transaction.localizedType let navTitle = pending ? String(localized: "Pending \(localizedType)") : localizedType Group { List { + Text("Loaded: \(loaded)") + .font(.title2) if developerMode { if transaction.isSuspendable { if let suspendAction { TransactionButton(transactionId: common.transactionId, @@ -59,7 +80,8 @@ struct TransactionDetailView: View { if let doneAction { DoneButton(doneAction: doneAction) } - }.listStyle(myListStyle.style).anyView + }.id(viewId) + .listStyle(myListStyle.style).anyView }.onNotification(.TransactionStateTransition) { notification in if let transition = notification.userInfo?[TRANSACTIONTRANSITION] as? TransactionTransition { if transition.transactionId == common.transactionId { @@ -69,9 +91,10 @@ struct TransactionDetailView: View { doneAction() }} else { Task { do { symLog.log("newState: \(newState), reloading transaction") + withAnimation() { transaction = Transaction(dummy: true); viewId += 1 } let reloadedTransaction = try await reloadAction(common.transactionId) - transaction = reloadedTransaction // redraw symLog.log("reloaded transaction: \(reloadedTransaction.common.txState.major)") + withAnimation() { transaction = reloadedTransaction; viewId += 1 } // redraw } catch { symLog.log(error.localizedDescription) }}} @@ -81,6 +104,15 @@ struct TransactionDetailView: View { } } .navigationTitle(navTitle) + .task { + do { + symLog.log("task") + let reloadedTransaction = try await reloadAction(transactionId) + withAnimation() { transaction = reloadedTransaction } // redraw + } catch { + withAnimation() { transaction = Transaction(dummy: true) } + } + } .onAppear { symLog.log("onAppear") DebugViewC.shared.setViewID(VIEW_TRANSACTIONDETAIL) @@ -99,6 +131,8 @@ struct TransactionDetailView: View { let common = transaction.common let pending = transaction.isPending switch transaction { + case .dummy(let dummyTransaction): + Text("Dummy") case .withdrawal(let withdrawalTransaction): let details = withdrawalTransaction.details if pending { @@ -197,23 +231,23 @@ struct TransactionDetailView: View { } // MARK: - #if DEBUG -struct TransactionDetail_Previews: PreviewProvider { - static func deleteTransactionDummy(transactionId: String) async throws {} - static func doneActionDummy() {} - static var withdrawal = Transaction(incoming: true, - pending: true, - id: "some withdrawal ID", - time: Timestamp(from: 1_666_000_000_000)) - static var payment = Transaction(incoming: false, - pending: false, - id: "some payment ID", - time: Timestamp(from: 1_666_666_000_000)) - static func reloadActionDummy(transactionId: String) async -> Transaction { return withdrawal } - static var previews: some View { - Group { - TransactionDetailView(transaction: withdrawal, reloadAction: reloadActionDummy, doneAction: doneActionDummy) - TransactionDetailView(transaction: payment, reloadAction: reloadActionDummy, deleteAction: deleteTransactionDummy) - } - } -} +//struct TransactionDetail_Previews: PreviewProvider { +// static func deleteTransactionDummy(transactionId: String) async throws {} +// static func doneActionDummy() {} +// static var withdrawal = Transaction(incoming: true, +// pending: true, +// id: "some withdrawal ID", +// time: Timestamp(from: 1_666_000_000_000)) +// static var payment = Transaction(incoming: false, +// pending: false, +// id: "some payment ID", +// time: Timestamp(from: 1_666_666_000_000)) +// static func reloadActionDummy(transactionId: String) async -> Transaction { return withdrawal } +// static var previews: some View { +// Group { +// TransactionDetailView(transaction: withdrawal, reloadAction: reloadActionDummy, doneAction: doneActionDummy) +// TransactionDetailView(transaction: payment, reloadAction: reloadActionDummy, deleteAction: deleteTransactionDummy) +// } +// } +//} #endif diff --git a/TalerWallet1/Views/Transactions/TransactionRowView.swift b/TalerWallet1/Views/Transactions/TransactionRowView.swift @@ -93,6 +93,7 @@ extension Transaction { // for PreViews transactionId: id, timestamp: time, txActions: [.abort]) + let now = Timestamp.now() if incoming { // if pending then manual else bank-integrated let payto = "payto://iban/SANDBOXX/DE159593?receiver-name=Exchange+Company&amount=KUDOS%3A9.99&message=Taler+Withdrawal+J41FQPJGAP1BED1SFSXHC989EN8HRDYAHK688MQ228H6SKBMV0AG" @@ -103,7 +104,7 @@ extension Transaction { // for PreViews exchangePaytoUris: pending ? [payto] : nil) let wDetails = WithdrawalTransactionDetails(exchangeBaseUrl: DEMOEXCHANGE, withdrawalDetails: withdrawalDetails) - self = .withdrawal(WithdrawalTransaction(common: common, details: wDetails)) + self = .withdrawal(WithdrawalTransaction(common: common, details: wDetails, loaded: now)) } else { let merchant = Merchant(name: "some random shop") let info = OrderShortInfo(orderId: "some order ID", @@ -114,7 +115,7 @@ extension Transaction { // for PreViews totalRefundRaw: try! Amount(fromString: refRaw), totalRefundEffective: try! Amount(fromString: refEff), info: info) - self = .payment(PaymentTransaction(common: common, details: pDetails)) + self = .payment(PaymentTransaction(common: common, details: pDetails, loaded: now)) } } } diff --git a/TalerWallet1/Views/Transactions/TransactionsListView.swift b/TalerWallet1/Views/Transactions/TransactionsListView.swift @@ -100,7 +100,7 @@ extension TransactionsListView { // let common = transaction.common NavigationLink { LazyView { // whole row like in a tableView // pending may not be deleted, but only aborted - TransactionDetailView(transaction: transaction, + TransactionDetailView(transactionId: transaction.id, reloadAction: reloadOneAction, deleteAction: deleteAction, abortAction: abortAction, diff --git a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptDone.swift b/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptDone.swift @@ -16,7 +16,7 @@ struct WithdrawAcceptDone: View { @EnvironmentObject private var model: WalletModel @State private var confirmTransferUrl: String? = nil - @State private var transaction: Transaction? = nil + @State private var transactionId: String? = nil func reloadOneAction(_ transactionId: String) async throws -> Transaction { return try await model.getTransactionByIdT(transactionId) @@ -28,10 +28,10 @@ struct WithdrawAcceptDone: View { let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear #endif VStack { - if let transaction { - TransactionDetailView(transaction: transaction, - reloadAction: reloadOneAction, - doneAction: { dismissTop() }) + if let transactionId { + TransactionDetailView(transactionId: transactionId, + reloadAction: reloadOneAction, + doneAction: { dismissTop() }) .navigationBarBackButtonHidden(true) .interactiveDismissDisabled() // can only use "Done" button to dismiss .navigationTitle(navTitle) @@ -47,7 +47,7 @@ struct WithdrawAcceptDone: View { if let exchangeBaseUrl { let result = try await model.sendAcceptIntWithdrawalM(exchangeBaseUrl, withdrawURL: url.absoluteString) confirmTransferUrl = result!.confirmTransferUrl - transaction = try await model.getTransactionByIdT(result!.transactionId) + transactionId = result!.transactionId } } catch { // TODO: error symLog.log(error.localizedDescription)