[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-taler-ios] 29/54: Model cleanup
From: |
gnunet |
Subject: |
[taler-taler-ios] 29/54: Model cleanup |
Date: |
Fri, 30 Jun 2023 22:34:01 +0200 |
This is an automated email from the git hooks/post-receive script.
marc-stibane pushed a commit to branch master
in repository taler-ios.
commit 99d5f1ac7ddb4bc76ae9ed398a6af463bdfef4d1
Author: Marc Stibane <marc@taler.net>
AuthorDate: Tue Jun 27 14:49:53 2023 +0200
Model cleanup
---
TalerWallet1/Model/Model+Settings.swift | 56 +++++-----
TalerWallet1/Model/Model+Transactions.swift | 57 +++++-----
TalerWallet1/Model/WalletModel.swift | 7 +-
TalerWallet1/Views/Balances/BalancesListView.swift | 6 +
.../Views/Balances/BalancesSectionView.swift | 16 +--
TalerWallet1/Views/Payment/PaymentURIView.swift | 124 +++++++++++++++------
6 files changed, 160 insertions(+), 106 deletions(-)
diff --git a/TalerWallet1/Model/Model+Settings.swift
b/TalerWallet1/Model/Model+Settings.swift
index 188d4a0..a085cb5 100644
--- a/TalerWallet1/Model/Model+Settings.swift
+++ b/TalerWallet1/Model/Model+Settings.swift
@@ -13,35 +13,6 @@ fileprivate let DEMO_BANKAPIBASEURL = DEMOBANK +
"/demobanks/default/access-api
fileprivate let DEMO_MERCHANTBASEURL = DEMOBACKEND
fileprivate let DEMO_MERCHANTAUTHTOKEN = "secret-token:sandbox"
-// MARK: -
-extension WalletModel {
- @MainActor func loadTestKudosM()
- async throws { // M for MainActor
- let amount = Amount(currency: DEMOCURRENCY, integer: 11, fraction: 0)
- let request = WalletBackendWithdrawTestBalance(amount: amount,
- bankBaseUrl:
DEMO_BANKBASEURL,
- exchangeBaseUrl:
DEMO_EXCHANGEBASEURL,
- bankAccessApiBaseUrl:
DEMO_BANKAPIBASEURL)
- let response = try await sendRequest(request, ASYNCDELAY)
- symLog?.log("received: \(response)")
- }
-
- @MainActor func runIntegrationTestM(newVersion: Bool)
- async throws { // M for MainActor
- let amountW = Amount(currency: DEMOCURRENCY, integer: 3, fraction: 0)
- let amountS = Amount(currency: DEMOCURRENCY, integer: 1, fraction: 0)
- let request = WalletBackendRunIntegration(newVersion: newVersion,
- amountToWithdraw: amountW,
- amountToSpend: amountS,
- bankBaseUrl:
DEMO_BANKAPIBASEURL,
- bankAccessApiBaseUrl:
DEMO_BANKAPIBASEURL,
- exchangeBaseUrl:
DEMO_EXCHANGEBASEURL,
- merchantBaseUrl:
DEMO_MERCHANTBASEURL,
- merchantAuthToken:
DEMO_MERCHANTAUTHTOKEN)
- let _ = try await sendRequest(request, ASYNCDELAY)
- symLog?.log("runIntegrationTest finished")
- }
-}
// MARK: -
/// A request to add a test balance to the wallet.
fileprivate struct WalletBackendWithdrawTestBalance:
WalletBackendFormattedRequest {
@@ -64,6 +35,17 @@ fileprivate struct WalletBackendWithdrawTestBalance:
WalletBackendFormattedReque
var bankAccessApiBaseUrl: String
}
}
+extension WalletModel {
+ @MainActor func loadTestKudosM()
+ async throws { // M for MainActor
+ let amount = Amount(currency: DEMOCURRENCY, integer: 11, fraction: 0)
+ let request = WalletBackendWithdrawTestBalance(amount: amount,
+ bankBaseUrl:
DEMO_BANKBASEURL,
+ exchangeBaseUrl:
DEMO_EXCHANGEBASEURL,
+ bankAccessApiBaseUrl:
DEMO_BANKAPIBASEURL)
+ let response = try await sendRequest(request, ASYNCDELAY)
+ }
+} // loadTestKudosM()
// MARK: -
/// A request to add a test balance to the wallet.
fileprivate struct WalletBackendRunIntegration: WalletBackendFormattedRequest {
@@ -100,3 +82,19 @@ fileprivate struct WalletBackendRunIntegration:
WalletBackendFormattedRequest {
var merchantAuthToken: String
}
}
+extension WalletModel {
+ @MainActor func runIntegrationTestM(newVersion: Bool)
+ async throws { // M for MainActor
+ let amountW = Amount(currency: DEMOCURRENCY, integer: 3, fraction: 0)
+ let amountS = Amount(currency: DEMOCURRENCY, integer: 1, fraction: 0)
+ let request = WalletBackendRunIntegration(newVersion: newVersion,
+ amountToWithdraw: amountW,
+ amountToSpend: amountS,
+ bankBaseUrl:
DEMO_BANKAPIBASEURL,
+ bankAccessApiBaseUrl:
DEMO_BANKAPIBASEURL,
+ exchangeBaseUrl:
DEMO_EXCHANGEBASEURL,
+ merchantBaseUrl:
DEMO_MERCHANTBASEURL,
+ merchantAuthToken:
DEMO_MERCHANTAUTHTOKEN)
+ let _ = try await sendRequest(request, ASYNCDELAY)
+ }
+} // runIntegrationTestM()
diff --git a/TalerWallet1/Model/Model+Transactions.swift
b/TalerWallet1/Model/Model+Transactions.swift
index f6bb23c..39e9842 100644
--- a/TalerWallet1/Model/Model+Transactions.swift
+++ b/TalerWallet1/Model/Model+Transactions.swift
@@ -72,6 +72,17 @@ struct DeleteTransaction: WalletBackendFormattedRequest {
var transactionId: String
}
}
+/// A request to delete a wallet transaction by ID.
+struct FailTransaction: WalletBackendFormattedRequest {
+ struct Response: Decodable {}
+ func operation() -> String { return "failTransaction" }
+ 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 {}
@@ -97,69 +108,51 @@ struct ResumeTransaction: WalletBackendFormattedRequest {
// MARK: -
extension WalletModel {
/// ask wallet-core for its list of transactions filtered by searchString
- func fetchTransactionsT(currency: String? = nil, searchString: String? =
nil)
+ func transactionsT(currency: String? = nil, searchString: String? = nil)
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)
+ @MainActor func transactionsMA(currency: String? = nil, searchString:
String? = nil)
async -> [Transaction] { // M for MainActor
- return await fetchTransactionsT(currency: currency, searchString:
searchString)
+ return await transactionsT(currency: currency, searchString:
searchString)
}
- func abortTransactionT(transactionId: String)
- async throws { // might be called from a background thread
itself
+ /// abort the specified transaction from Wallet-Core. No networking
involved
+ func abortTransaction(transactionId: String) async throws {
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)
- }
- func deleteTransactionT(transactionId: String)
- async throws { // might be called from a background thread
itself
+ /// delete the specified transaction from Wallet-Core. No networking
involved
+ func deleteTransaction(transactionId: String) async throws {
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)
+
+ func failTransaction(transactionId: String) async throws {
+ let request = FailTransaction(transactionId: transactionId)
+ logger.info("failTransaction: \(transactionId, privacy: .private(mask:
.hash))")
+ let _ = try await sendRequest(request, ASYNCDELAY)
}
- func suspendTransactionT(transactionId: String)
- async throws { // might be called from a background thread
itself
+ func suspendTransaction(transactionId: String) async throws {
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
+ func resumeTransaction(transactionId: String) async throws {
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
index d0ce810..525bf11 100644
--- a/TalerWallet1/Model/WalletModel.swift
+++ b/TalerWallet1/Model/WalletModel.swift
@@ -13,13 +13,8 @@ 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(0)
+ public static let shared = WalletModel()
static func className() -> String {"\(self)"}
- var symLog: SymLogC?
-
- init(_ symbol: Int) { // init with 0 to
disable logging for this class
- self.symLog = SymLogC(symbol == 0 ? 0 : -1, funcName: Self.className())
- }
let logger = Logger (subsystem: "net.taler.gnu", category: "WalletModel")
func sendRequest<T: WalletBackendFormattedRequest> (_ request: T, _ delay:
UInt = 0)
diff --git a/TalerWallet1/Views/Balances/BalancesListView.swift
b/TalerWallet1/Views/Balances/BalancesListView.swift
index 9029ff3..4a716eb 100644
--- a/TalerWallet1/Views/Balances/BalancesListView.swift
+++ b/TalerWallet1/Views/Balances/BalancesListView.swift
@@ -80,6 +80,12 @@ struct BalancesListView: View {
.overlay {
if balances.isEmpty {
WalletEmptyView()
+ .refreshable {
+ symLog.log("refreshing")
+ await reloadAction() // this closure is
already async, no need for a Task
+ // TODO: reload
transactions
+ }
+
}
}
.alert("Scanning QR-codes requires access to the camera",
diff --git a/TalerWallet1/Views/Balances/BalancesSectionView.swift
b/TalerWallet1/Views/Balances/BalancesSectionView.swift
index 594b651..29f32e3 100644
--- a/TalerWallet1/Views/Balances/BalancesSectionView.swift
+++ b/TalerWallet1/Views/Balances/BalancesSectionView.swift
@@ -66,25 +66,25 @@ struct BalancesSectionView: View {
#endif
let currency = balance.available.currencyStr
let reloadCompleted = {
- transactions = await model.fetchTransactionsT(currency: currency)
+ transactions = await model.transactionsT(currency: currency)
completedTransactions =
WalletModel.completedTransactions(transactions)
// sectionID = UUID()
}
let reloadPending = {
- transactions = await model.fetchTransactionsT(currency: currency)
+ transactions = await model.transactionsT(currency: currency)
pendingTransactions = WalletModel.pendingTransactions(transactions)
// sectionID = UUID()
}
let reloadUncompleted = {
- transactions = await model.fetchTransactionsT(currency: currency)
+ transactions = await model.transactionsT(currency: currency)
uncompletedTransactions =
WalletModel.uncompletedTransactions(transactions)
// sectionID = UUID()
}
- let deleteAction = model.deleteTransactionT // dummyTransaction
- let abortAction = model.abortTransactionT
- let suspendAction = model.suspendTransactionT // dummyTransaction
- let resumeAction = model.resumeTransactionT
+ let abortAction = model.abortTransaction
+ let deleteAction = model.deleteTransaction // dummyTransaction
let failAction = model.failTransaction
+ let suspendAction = model.suspendTransaction // dummyTransaction
+ let resumeAction = model.resumeTransaction
Section {
// if "KUDOS" == currency && !balance.available.isZero {
@@ -190,8 +190,8 @@ struct BalancesSectionView: View {
}.id(sectionID)
.task {
if shownSectionID != sectionID {
- let response = await model.fetchTransactionsT(currency:
currency)
symLog.log("task for BalancesSectionView \(sectionID) - reload
Transactions")
+ let response = await model.transactionsT(currency: currency)
transactions = response
pendingTransactions = WalletModel.pendingTransactions(response)
uncompletedTransactions =
WalletModel.uncompletedTransactions(response)
diff --git a/TalerWallet1/Views/Payment/PaymentURIView.swift
b/TalerWallet1/Views/Payment/PaymentURIView.swift
index 5b4a0d6..69dbae8 100644
--- a/TalerWallet1/Views/Payment/PaymentURIView.swift
+++ b/TalerWallet1/Views/Payment/PaymentURIView.swift
@@ -3,17 +3,20 @@
* See LICENSE.md
*/
import SwiftUI
+import taler_swift
import SymLog
-// Will be called either by the user scanning a QR code or tapping the
provided link, both from the shop's website
-// we show the user the payment details
+// Will be called either by the user scanning a QR code or tapping the
provided link,
+// both from the shop's website. We show the payment details
struct PaymentURIView: View {
private let symLog = SymLogV()
+ let navTitle = String(localized: "Confirm Payment", comment:"pay merchant")
+
+ // the scanned URL
let url: URL
@EnvironmentObject private var model: WalletModel
- @State var detailsForUri: PaymentDetailsForUri? = nil
func playSound(success: Bool) {
let url = URL(fileURLWithPath:
"/System/Library/Audio/UISounds/payment_"
@@ -21,46 +24,105 @@ struct PaymentURIView: View {
GNU_Taler.playSound(fileURL: url)
}
- func acceptAction() {
+ func acceptAction(detailsForUri: PaymentDetailsForUri) {
Task {
do {
- if let detailsForUri {
- let confirmPayResult = try await
model.confirmPayM(detailsForUri.proposalId)
- symLog.log(confirmPayResult as Any)
- if confirmPayResult.type == "done" {
- // TODO: Show Hints that Payment was successfull
- playSound(success: true)
- } else {
- // TODO: show error
- playSound(success: false)
- }
+ let confirmPayResult = try await
model.confirmPayM(detailsForUri.proposalId)
+ symLog.log(confirmPayResult as Any)
+ if confirmPayResult.type == "done" {
+ playSound(success: true)
+ // TODO: Show Hints that Payment was successfull
+ } else {
+ playSound(success: false)
+ // TODO: show error
}
- } catch { // TODO: error
+ } catch {
playSound(success: false)
+ // TODO: error
symLog.log(error.localizedDescription)
}
dismissTop()
}
}
+ @State var detailsForUri: PaymentDetailsForUri? = nil
+
var body: some View {
- let badURL = "Error in URL: \(url)"
- VStack {
- if let detailsForUri {
- PaymentAcceptView(detailsForUri: detailsForUri, acceptAction:
acceptAction)
- .navigationTitle("Payment")
- } else {
- WithdrawProgressView(message: url.host ?? badURL)
- .navigationTitle("Contacting Exchange")
- }
- }.task {
- do {
- symLog.log(".task")
- let details = try await
model.preparePayForUriM(url.absoluteString)
- detailsForUri = details
- } catch { // TODO: error
- symLog.log(error.localizedDescription)
+ if let detailsForUri {
+ VStack {
+ let baseURL = detailsForUri.contractTerms.exchanges.first?.url
+ let raw = detailsForUri.amountRaw
+ let effective = detailsForUri.amountEffective
+ let currency = raw.currencyStr
+ let fee = try! Amount.diff(raw, effective) // TODO:
different currencies
+ ThreeAmountsView(topTitle: String(localized: "Amount to pay:"),
+ topAmount: raw, fee: fee,
+ bottomTitle: String(localized: "\(currency) to
be spent:"),
+ bottomAmount: effective,
+ large: true, pending: false, incoming:
false,
+ baseURL: baseURL)
+ // TODO: payment: popup with all possible exchanges, check fees
+ .safeAreaInset(edge: .bottom) {
+ Button(navTitle, action: { acceptAction(detailsForUri:
detailsForUri) })
+ .buttonStyle(TalerButtonStyle(type: .prominent))
+ .padding(.horizontal)
+ }
}
+ .navigationTitle(navTitle)
+ } else {
+ let badURL = "Error in URL: \(url)"
+ WithdrawProgressView(message: url.host ?? badURL)
+ .navigationTitle("Contacting Exchange")
+ .task {
+ do {
+ symLog.log(".task")
+ let details = try await
model.preparePayForUriM(url.absoluteString)
+ detailsForUri = details
+ } catch { // TODO: error
+ symLog.log(error.localizedDescription)
+ }
+ }
}
}
}
+// MARK: -
+struct PaymentURIView_Previews: PreviewProvider {
+ static var previews: some View {
+ let merchant = Merchant(name: "Merchant")
+ let extra = Extra(articleName: "articleName")
+ let product = Product(description: "description")
+ let terms = ContractTerms(amount: try! Amount(fromString: LONGCURRENCY
+ ":2.2"),
+ maxFee: try! Amount(fromString: LONGCURRENCY
+ ":0.2"),
+ maxWireFee: try! Amount(fromString:
LONGCURRENCY + ":0.2"),
+ merchant: merchant,
+ extra: extra,
+ summary: "summary",
+ timestamp: Timestamp.now(),
+ payDeadline: Timestamp.tomorrow(),
+ refundDeadline: Timestamp.tomorrow(),
+ wireTransferDeadline: Timestamp.tomorrow(),
+ merchantBaseURL: "merchantBaseURL",
+ fulfillmentURL: "fulfillmentURL",
+ publicReorderURL: "publicReorderURL",
+ auditors: [],
+ exchanges: [],
+ orderID: "orderID",
+ nonce: "nonce",
+ merchantPub: "merchantPub",
+ products: [product],
+ hWire: "hWire",
+ wireMethod: "wireMethod",
+ wireFeeAmortization: 0)
+ let details = PaymentDetailsForUri(
+ amountRaw: try! Amount(fromString: LONGCURRENCY + ":2.2"),
+ amountEffective: try! Amount(fromString: LONGCURRENCY + ":2.4"),
+ noncePriv: "noncePriv",
+ proposalId: "proposalId",
+ contractTerms: terms,
+ contractTermsHash: "termsHash"
+ )
+ let url = URL(string: "taler://pay/some_amount")!
+
+ PaymentURIView(url: url, detailsForUri: details)
+ }
+}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [taler-taler-ios] 30/54: bugfix, (continued)
- [taler-taler-ios] 30/54: bugfix, gnunet, 2023/06/30
- [taler-taler-ios] 43/54: confirmTransferUrl, gnunet, 2023/06/30
- [taler-taler-ios] 33/54: log only release builds, gnunet, 2023/06/30
- [taler-taler-ios] 27/54: Logging, gnunet, 2023/06/30
- [taler-taler-ios] 38/54: actions, gnunet, 2023/06/30
- [taler-taler-ios] 28/54: playSound, gnunet, 2023/06/30
- [taler-taler-ios] 32/54: Adjust DebugView for Notch, gnunet, 2023/06/30
- [taler-taler-ios] 20/54: viewID + comments, gnunet, 2023/06/30
- [taler-taler-ios] 31/54: bump Testflight version, gnunet, 2023/06/30
- [taler-taler-ios] 37/54: Decodable, gnunet, 2023/06/30
- [taler-taler-ios] 29/54: Model cleanup,
gnunet <=
- [taler-taler-ios] 36/54: playSound, gnunet, 2023/06/30
- [taler-taler-ios] 44/54: BalanceReloaded, gnunet, 2023/06/30
- [taler-taler-ios] 35/54: developerMode, gnunet, 2023/06/30
- [taler-taler-ios] 45/54: TransactionDetails, gnunet, 2023/06/30
- [taler-taler-ios] 39/54: Scrollview, gnunet, 2023/06/30
- [taler-taler-ios] 25/54: failTransaction, gnunet, 2023/06/30
- [taler-taler-ios] 23/54: Remove old view, gnunet, 2023/06/30
- [taler-taler-ios] 24/54: balance-change, gnunet, 2023/06/30
- [taler-taler-ios] 47/54: #available(iOS 17.0, *) only with Xcode 15, gnunet, 2023/06/30
- [taler-taler-ios] 46/54: ThreeAmountsSheet, gnunet, 2023/06/30