commit 185b6c0ad904af203233f8960b1116ebdeb332b1
parent 6d1028c0b4fceeafd3a890850ed4ebc20dde19cf
Author: Marc Stibane <marc@taler.net>
Date: Wed, 21 Jun 2023 15:07:17 +0200
Suspend-Resume
Diffstat:
8 files changed, 118 insertions(+), 49 deletions(-)
diff --git a/TalerWallet1/Backend/Transaction.swift b/TalerWallet1/Backend/Transaction.swift
@@ -102,12 +102,12 @@ struct TransactionCommon: Decodable {
case payment
case refund
case refresh
- case reward // get paid for e.g. survey participation
-// case tip // tip personnel at restaurants
- case peerPushDebit = "peer-push-debit" // send coins to peer, show QR
- case scanPushCredit = "peer-push-credit" // scan QR, receive coins from peer
- case peerPullCredit = "peer-pull-credit" // request payment from peer, show QR
- case scanPullDebit = "peer-pull-debit" // scan QR, pay requested
+ case reward = "tip" // get paid for e.g. survey participation
+// case tip // tip personnel at restaurants
+ case peerPushDebit = "peer-push-debit" // send coins to peer, show QR
+ case scanPushCredit = "peer-push-credit" // scan QR, receive coins from peer
+ case peerPullCredit = "peer-pull-credit" // request payment from peer, show QR
+ case scanPullDebit = "peer-pull-debit" // scan QR, pay requested
}
var type: TransactionType
var txState: TransactionState
diff --git a/TalerWallet1/Model/Model+Exchange.swift b/TalerWallet1/Model/Model+Exchange.swift
@@ -98,7 +98,7 @@ extension WalletModel {
async throws {
symLog?.log("adding exchange: \(url)") // TODO: .notice
let request = AddExchange(exchangeBaseUrl: url)
- logger.info("adding exchange: \(url)")
+ logger.info("adding exchange: \(url, privacy: .public)")
_ = try await sendRequest(request)
}
}
diff --git a/TalerWallet1/Model/Model+Transactions.swift b/TalerWallet1/Model/Model+Transactions.swift
@@ -41,7 +41,6 @@ fileprivate struct GetTransactions: WalletBackendFormattedRequest {
var currency: String?
var search: String?
-
struct Args: Encodable {
var currency: String?
var search: String?
@@ -53,16 +52,14 @@ fileprivate struct GetTransactions: WalletBackendFormattedRequest {
}
/// A request to abort a wallet transaction by ID.
struct AbortTransaction: WalletBackendFormattedRequest {
+ struct Response: Decodable {}
func operation() -> String { return "abortTransaction" }
func args() -> Args { return Args(transactionId: transactionId) }
var transactionId: String
-
struct Args: Encodable {
var transactionId: String
}
-
- struct Response: Decodable {}
}
/// A request to delete a wallet transaction by ID.
struct DeleteTransaction: WalletBackendFormattedRequest {
@@ -71,50 +68,98 @@ struct DeleteTransaction: WalletBackendFormattedRequest {
func args() -> Args { return Args(transactionId: transactionId) }
var transactionId: String
+ struct Args: Encodable {
+ var transactionId: String
+ }
+}
+/// A request to suspend a wallet transaction by ID.
+struct SuspendTransaction: WalletBackendFormattedRequest {
+ struct Response: Decodable {}
+ func operation() -> String { return "suspendTransaction" }
+ func args() -> Args { return Args(transactionId: transactionId) }
+ var transactionId: String
struct Args: Encodable {
var transactionId: String
}
}
+/// A request to suspend a wallet transaction by ID.
+struct ResumeTransaction: WalletBackendFormattedRequest {
+ struct Response: Decodable {}
+ func operation() -> String { return "resumeTransaction" }
+ func args() -> Args { return Args(transactionId: transactionId) }
+ var transactionId: String
+ struct Args: Encodable {
+ var transactionId: String
+ }
+}
// MARK: -
extension WalletModel {
/// ask wallet-core for its list of transactions filtered by searchString
func fetchTransactionsT(currency: String? = nil, searchString: String? = nil)
- async -> [Transaction] { // might be called from a background thread itself
+ async -> [Transaction] { // might be called from a background thread itself
do {
let request = GetTransactions(currency: currency, search: searchString)
+ logger.info("getTransactions")
let response = try await sendRequest(request, ASYNCDELAY)
return response.transactions
} catch {
+ logger.error("getTransactions failed: \(error)")
return []
}
}
/// fetch transactions from Wallet-Core. No networking involved
@MainActor func fetchTransactionsM(currency: String? = nil, searchString: String? = nil)
- async -> [Transaction] { // M for MainActor
- await fetchTransactionsT(currency: currency, searchString: searchString)
+ async -> [Transaction] { // M for MainActor
+ return await fetchTransactionsT(currency: currency, searchString: searchString)
}
func abortTransactionT(transactionId: String)
- async throws { // might be called from a background thread itself
+ async throws { // might be called from a background thread itself
let request = AbortTransaction(transactionId: transactionId)
+ logger.info("abortTransaction: \(transactionId, privacy: .private(mask: .hash))")
let _ = try await sendRequest(request, ASYNCDELAY)
}
/// delete the specified transaction from Wallet-Core. No networking involved
@MainActor func abortTransactionM(transactionId: String)
- async throws { // M for MainActor
- try await abortTransactionT(transactionId: transactionId) // call abortTransaction on main thread
+ async throws { // M for MainActor
+ try await abortTransactionT(transactionId: transactionId)
}
func deleteTransactionT(transactionId: String)
- async throws { // might be called from a background thread itself
+ async throws { // might be called from a background thread itself
let request = DeleteTransaction(transactionId: transactionId)
+ logger.info("deleteTransaction: \(transactionId, privacy: .private(mask: .hash))")
let _ = try await sendRequest(request, ASYNCDELAY)
}
/// delete the specified transaction from Wallet-Core. No networking involved
@MainActor func deleteTransactionM(transactionId: String)
async throws { // M for MainActor
- try await deleteTransactionT(transactionId: transactionId) // call deleteTransaction on main thread
+ try await deleteTransactionT(transactionId: transactionId)
+ }
+
+ func suspendTransactionT(transactionId: String)
+ async throws { // might be called from a background thread itself
+ let request = SuspendTransaction(transactionId: transactionId)
+ logger.info("suspendTransaction: \(transactionId, privacy: .private(mask: .hash))")
+ let _ = try await sendRequest(request, ASYNCDELAY)
+ }
+ /// delete the specified transaction from Wallet-Core. No networking involved
+ @MainActor func suspendTransactionM(transactionId: String)
+ async throws { // M for MainActor
+ try await suspendTransactionT(transactionId: transactionId)
+ }
+
+ func resumeTransactionT(transactionId: String)
+ async throws { // might be called from a background thread itself
+ let request = ResumeTransaction(transactionId: transactionId)
+ logger.info("resumeTransaction: \(transactionId, privacy: .private(mask: .hash))")
+ let _ = try await sendRequest(request, ASYNCDELAY)
+ }
+ /// delete the specified transaction from Wallet-Core. No networking involved
+ @MainActor func resumeTransactionM(transactionId: String)
+ async throws { // M for MainActor
+ try await resumeTransactionT(transactionId: transactionId)
}
}
diff --git a/TalerWallet1/Model/WalletModel.swift b/TalerWallet1/Model/WalletModel.swift
@@ -13,7 +13,7 @@ fileprivate let ASYNCDELAY: UInt = 0 //set e.g to 6 or 9 seconds for debugging
// MARK: -
/// The "virtual" base class for all models
class WalletModel: ObservableObject {
- public static let shared = WalletModel(-1)
+ public static let shared = WalletModel(0)
static func className() -> String {"\(self)"}
var symLog: SymLogC?
let logger = Logger (subsystem: "net.taler.gnu", category: "wallet-core")
diff --git a/TalerWallet1/Views/Balances/BalancesSectionView.swift b/TalerWallet1/Views/Balances/BalancesSectionView.swift
@@ -77,6 +77,8 @@ struct BalancesSectionView: View {
}
let deleteAction = model.deleteTransactionT // dummyTransaction
let abortAction = model.abortTransactionT
+ let suspendAction = model.suspendTransactionT // dummyTransaction
+ let resumeAction = model.resumeTransactionT
Section {
// if "KUDOS" == currency && !balance.available.isZero {
@@ -104,7 +106,9 @@ struct BalancesSectionView: View {
reloadAllAction: reloadCompleted,
reloadOneAction: reloadOneAction,
deleteAction: deleteAction,
- abortAction: abortAction)
+ abortAction: abortAction,
+ suspendAction: suspendAction,
+ resumeAction: resumeAction)
}, tag: 3, selection: $buttonSelected
) { EmptyView() }.frame(width: 0).opacity(0).hidden()
@@ -131,7 +135,9 @@ struct BalancesSectionView: View {
reloadAllAction: reloadPending,
reloadOneAction: reloadOneAction,
deleteAction: deleteAction,
- abortAction: abortAction)
+ abortAction: abortAction,
+ suspendAction: suspendAction,
+ resumeAction: resumeAction)
}
} label: {
VStack(spacing: 6) {
@@ -160,10 +166,12 @@ struct BalancesSectionView: View {
reloadAllAction: reloadUncompleted,
reloadOneAction: reloadOneAction,
deleteAction: deleteAction,
- abortAction: abortAction)
+ abortAction: abortAction,
+ suspendAction: suspendAction,
+ resumeAction: resumeAction)
}
} label: {
- UncompletedRowView(uncompletedTransactions: uncompletedTransactions)
+ UncompletedRowView(uncompletedTransactions: $uncompletedTransactions)
}
}
diff --git a/TalerWallet1/Views/Balances/UncompletedRowView.swift b/TalerWallet1/Views/Balances/UncompletedRowView.swift
@@ -7,7 +7,7 @@ import taler_swift
/// This view shows an uncompleted transaction row in a currency section
struct UncompletedRowView: View {
- var uncompletedTransactions: [Transaction]
+ @Binding var uncompletedTransactions: [Transaction]
var body: some View {
let count = uncompletedTransactions.count
@@ -23,12 +23,12 @@ struct UncompletedRowView: View {
}
// MARK: -
#if DEBUG
-struct UncompletedRowView_Previews: PreviewProvider {
- static var previews: some View {
- let uncompletedTransactions: [Transaction] = []
- List {
- UncompletedRowView(uncompletedTransactions: uncompletedTransactions)
- }
- }
-}
+//struct UncompletedRowView_Previews: PreviewProvider {
+// static var previews: some View {
+// let uncompletedTransactions: [Transaction] = []
+// List {
+// UncompletedRowView(uncompletedTransactions: uncompletedTransactions)
+// }
+// }
+//}
#endif
diff --git a/TalerWallet1/Views/Transactions/TransactionDetailView.swift b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
@@ -32,6 +32,16 @@ struct TransactionDetailView: View {
: localizedType
Group {
List {
+ if developerMode {
+ if transaction.isSuspendable { if let suspendAction {
+ TransactionButton(transactionId: common.transactionId,
+ command: .suspend, action: suspendAction)
+ } }
+ if transaction.isResumable { if let resumeAction {
+ TransactionButton(transactionId: common.transactionId,
+ command: .resume, action: resumeAction)
+ } }
+ }
Text("\(dateString)")
.font(.title2)
// .listRowSeparator(.hidden)
@@ -49,16 +59,6 @@ struct TransactionDetailView: View {
if let doneAction {
DoneButton(doneAction: doneAction)
}
- if developerMode {
- if transaction.isSuspendable { if let suspendAction {
- TransactionButton(transactionId: common.transactionId,
- command: .suspend, action: suspendAction)
- } }
- if transaction.isResumable { if let resumeAction {
- TransactionButton(transactionId: common.transactionId,
- command: .resume, action: resumeAction)
- } }
- }
}.listStyle(myListStyle.style).anyView
}.onNotification(.TransactionStateTransition) { notification in
if let transition = notification.userInfo?[TRANSACTIONTRANSITION] as? TransactionTransition {
@@ -71,6 +71,7 @@ struct TransactionDetailView: View {
symLog.log("newState: \(newState), reloading transaction")
let reloadedTransaction = try await reloadAction(common.transactionId)
transaction = reloadedTransaction // redraw
+ symLog.log("reloaded transaction: \(reloadedTransaction.common.txState.major)")
} catch {
symLog.log(error.localizedDescription)
}}}
@@ -150,7 +151,9 @@ struct TransactionDetailView: View {
baseURL: nil, large: true) // TODO: baseURL
case .peer2peer(let p2pTransaction):
let details = p2pTransaction.details // TODO: details
- QRCodeDetails(transaction: transaction)
+ if pending {
+ QRCodeDetails(transaction: transaction)
+ }
ThreeAmounts(common: common, topTitle: String(localized: "Peer to Peer:"),
baseURL: details.exchangeBaseUrl, large: false)
}
diff --git a/TalerWallet1/Views/Transactions/TransactionsListView.swift b/TalerWallet1/Views/Transactions/TransactionsListView.swift
@@ -6,7 +6,7 @@ import SwiftUI
import SymLog
struct TransactionsListView: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
let navTitle: String
@@ -16,6 +16,8 @@ struct TransactionsListView: View {
let reloadOneAction: ((_ transactionId: String) async throws -> Transaction)
let deleteAction: (_ transactionId: String) async throws -> ()
let abortAction: (_ transactionId: String) async throws -> ()
+ let suspendAction: (_ transactionId: String) async throws -> ()
+ let resumeAction: (_ transactionId: String) async throws -> ()
var body: some View {
#if DEBUG
@@ -26,9 +28,16 @@ struct TransactionsListView: View {
// TODO: Unlock the power of grammatical agreement
// let title = AttributedString(localized: "^[\(count) Ticket](inflect: true)")
let title: String = "\(count) \(navTitle)"
- Content(symLog: symLog, currency: currency, transactions: transactions, myListStyle: $myListStyle,
- reloadAllAction: reloadAllAction, reloadOneAction: reloadOneAction,
- deleteAction: deleteAction, abortAction: abortAction)
+ Content(symLog: symLog,
+ currency: currency,
+ transactions: transactions,
+ myListStyle: $myListStyle,
+ reloadAllAction: reloadAllAction,
+ reloadOneAction: reloadOneAction,
+ deleteAction: deleteAction,
+ abortAction: abortAction,
+ suspendAction: suspendAction,
+ resumeAction: resumeAction)
.navigationTitle(title)
.navigationBarTitleDisplayMode(.large) // .inline
.onAppear {
@@ -51,6 +60,8 @@ extension TransactionsListView {
let reloadOneAction: ((_ transactionId: String) async throws -> Transaction)
let deleteAction: (_ transactionId: String) async throws -> ()
let abortAction: (_ transactionId: String) async throws -> ()
+ let suspendAction: (_ transactionId: String) async throws -> ()
+ let resumeAction: (_ transactionId: String) async throws -> ()
@State private var upAction: () -> Void = {}
@State private var downAction: () -> Void = {}
@@ -92,7 +103,9 @@ extension TransactionsListView {
TransactionDetailView(transaction: transaction,
reloadAction: reloadOneAction,
deleteAction: deleteAction,
- abortAction: abortAction)
+ abortAction: abortAction,
+ suspendAction: suspendAction,
+ resumeAction: resumeAction)
}} label: {
TransactionRowView(transaction: transaction)
}