[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-taler-ios] branch master updated: parse withdraw transaction deta
From: |
gnunet |
Subject: |
[taler-taler-ios] branch master updated: parse withdraw transaction details |
Date: |
Tue, 23 Aug 2022 21:40:14 +0200 |
This is an automated email from the git hooks/post-receive script.
jonathan-buchanan pushed a commit to branch master
in repository taler-ios.
The following commit(s) were added to refs/heads/master by this push:
new 0d3486f parse withdraw transaction details
0d3486f is described below
commit 0d3486f224f600aac7942ef14999b2779deb4638
Author: Jonathan Buchanan <jonathan.russ.buchanan@gmail.com>
AuthorDate: Tue Aug 23 15:39:31 2022 -0400
parse withdraw transaction details
---
README.md | 2 +-
Taler.xcodeproj/project.pbxproj | 4 +
Taler/Model/BalancesModel.swift | 21 ++-
...BalancesModel.swift => TransactionsModel.swift} | 29 ++-
Taler/Views/BalancesView.swift | 66 +++++--
Taler/WalletBackend.swift | 200 ++++++++++++++++++++-
taler-swift/Sources/taler-swift/Time.swift | 10 +-
7 files changed, 279 insertions(+), 53 deletions(-)
diff --git a/README.md b/README.md
index e9bfa87..bd2bf6b 100644
--- a/README.md
+++ b/README.md
@@ -11,4 +11,4 @@ Before building anything, you should initialize and update
the submodules by run
$ ./bootstrap
-To build the app, open `Taler.xcodeproj` with Xcode. The minimum version of
iOS supported is 13.0. This iOS version is compatible on all iPhone models at
least as new as the iPhone 6S.
+To build the app, open `Taler.xcodeproj` with Xcode. The minimum version of
iOS supported is 14.0. This iOS version is compatible on all iPhone models at
least as new as the iPhone 6S.
diff --git a/Taler.xcodeproj/project.pbxproj b/Taler.xcodeproj/project.pbxproj
index 6b6eac4..99b68e5 100644
--- a/Taler.xcodeproj/project.pbxproj
+++ b/Taler.xcodeproj/project.pbxproj
@@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
AB1F87C82887C94700AB82A0 /* TalerApp.swift in Sources */ = {isa
= PBXBuildFile; fileRef = AB1F87C72887C94700AB82A0 /* TalerApp.swift */; };
AB1F87CA2887D2F400AB82A0 /* ContentView.swift in Sources */ =
{isa = PBXBuildFile; fileRef = AB1F87C92887D2F400AB82A0 /* ContentView.swift
*/; };
+ AB32199128B18859008AAC75 /* TransactionsModel.swift in Sources
*/ = {isa = PBXBuildFile; fileRef = AB32199028B18859008AAC75 /*
TransactionsModel.swift */; };
AB4C534A28AC21C9003004F7 /* BalancesView.swift in Sources */ =
{isa = PBXBuildFile; fileRef = AB4C534928AC21C9003004F7 /* BalancesView.swift
*/; };
AB4C534C28AC25FC003004F7 /* BalancesModel.swift in Sources */ =
{isa = PBXBuildFile; fileRef = AB4C534B28AC25FC003004F7 /* BalancesModel.swift
*/; };
AB69F9FA28AAED53005CCC2E /* WithdrawModel.swift in Sources */ =
{isa = PBXBuildFile; fileRef = AB69F9F928AAED53005CCC2E /* WithdrawModel.swift
*/; };
@@ -60,6 +61,7 @@
/* Begin PBXFileReference section */
AB1F87C72887C94700AB82A0 /* TalerApp.swift */ = {isa =
PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalerApp.swift;
sourceTree = "<group>"; };
AB1F87C92887D2F400AB82A0 /* ContentView.swift */ = {isa =
PBXFileReference; lastKnownFileType = sourcecode.swift; path =
ContentView.swift; sourceTree = "<group>"; };
+ AB32199028B18859008AAC75 /* TransactionsModel.swift */ = {isa =
PBXFileReference; lastKnownFileType = sourcecode.swift; path =
TransactionsModel.swift; sourceTree = "<group>"; };
AB4C534928AC21C9003004F7 /* BalancesView.swift */ = {isa =
PBXFileReference; lastKnownFileType = sourcecode.swift; path =
BalancesView.swift; sourceTree = "<group>"; };
AB4C534B28AC25FC003004F7 /* BalancesModel.swift */ = {isa =
PBXFileReference; lastKnownFileType = sourcecode.swift; path =
BalancesModel.swift; sourceTree = "<group>"; };
AB69F9F928AAED53005CCC2E /* WithdrawModel.swift */ = {isa =
PBXFileReference; lastKnownFileType = sourcecode.swift; path =
WithdrawModel.swift; sourceTree = "<group>"; };
@@ -129,6 +131,7 @@
isa = PBXGroup;
children = (
ABB33066289C658900668B42 /*
BackendManager.swift */,
+ AB32199028B18859008AAC75 /*
TransactionsModel.swift */,
ABB33064289C5BBB00668B42 /*
ExchangeManager.swift */,
ABC4AC3E28A473070047A56F /*
PendingManager.swift */,
AB69F9F928AAED53005CCC2E /* WithdrawModel.swift
*/,
@@ -390,6 +393,7 @@
ABB33065289C5BBB00668B42 /*
ExchangeManager.swift in Sources */,
AB4C534A28AC21C9003004F7 /* BalancesView.swift
in Sources */,
D1D65B9826992E4600C1012A /* WalletBackend.swift
in Sources */,
+ AB32199128B18859008AAC75 /*
TransactionsModel.swift in Sources */,
ABB762AD2891059600E88634 /* SettingsView.swift
in Sources */,
ABC4AC3B28A4619C0047A56F /* PendingView.swift
in Sources */,
ABC4AC3F28A473070047A56F /*
PendingManager.swift in Sources */,
diff --git a/Taler/Model/BalancesModel.swift b/Taler/Model/BalancesModel.swift
index d4b2164..29a6656 100644
--- a/Taler/Model/BalancesModel.swift
+++ b/Taler/Model/BalancesModel.swift
@@ -17,33 +17,32 @@
import Foundation
class BalancesModel: ObservableObject {
- enum State {
- case begin
- case loading
- case loaded([Balance])
- }
-
var backend: WalletBackend
- @Published var state: State
+
+ @Published var loading: Bool = false
+ @Published var balances: [Balance]?
init(backend: WalletBackend) {
self.backend = backend
- self.state = .begin
}
func getBalances() {
- self.state = .loading
+ self.loading = true
let req = WalletBackendGetBalancesRequest()
backend.sendFormattedRequest(request: req) { response, err in
// TODO: Use Combine instead
DispatchQueue.main.async {
+ self.loading = false
if let res = response {
- self.state = .loaded(res.balances)
+ self.balances = res.balances
} else {
// TODO: Handle error.
- self.state = .begin
}
}
}
}
+
+ func getTransactionsModel() -> TransactionsModel {
+ return TransactionsModel(backend: self.backend, currency: nil)
+ }
}
diff --git a/Taler/Model/BalancesModel.swift
b/Taler/Model/TransactionsModel.swift
similarity index 64%
copy from Taler/Model/BalancesModel.swift
copy to Taler/Model/TransactionsModel.swift
index d4b2164..753d5cd 100644
--- a/Taler/Model/BalancesModel.swift
+++ b/Taler/Model/TransactionsModel.swift
@@ -16,32 +16,31 @@
import Foundation
-class BalancesModel: ObservableObject {
- enum State {
- case begin
- case loading
- case loaded([Balance])
- }
-
+class TransactionsModel: ObservableObject {
var backend: WalletBackend
- @Published var state: State
+ var currency: String?
+
+ @Published var loading: Bool = false
+ @Published var transactions: [Transaction]?
- init(backend: WalletBackend) {
+ init(backend: WalletBackend, currency: String?) {
self.backend = backend
- self.state = .begin
+ self.currency = currency
}
- func getBalances() {
- self.state = .loading
- let req = WalletBackendGetBalancesRequest()
+ func loadTransactions(searchString: String? = nil) {
+ self.loading = true
+ let req = WalletBackendGetTransactionsRequest(currency: self.currency,
+ search: searchString)
backend.sendFormattedRequest(request: req) { response, err in
// TODO: Use Combine instead
DispatchQueue.main.async {
+ self.loading = false
if let res = response {
- self.state = .loaded(res.balances)
+ print("x")
+ self.transactions = res.transactions
} else {
// TODO: Handle error.
- self.state = .begin
}
}
}
diff --git a/Taler/Views/BalancesView.swift b/Taler/Views/BalancesView.swift
index 39bf282..a8eab64 100644
--- a/Taler/Views/BalancesView.swift
+++ b/Taler/Views/BalancesView.swift
@@ -16,6 +16,47 @@
import SwiftUI
+struct TransactionsView: View {
+ @ObservedObject var model: TransactionsModel
+
+ var body: some View {
+ VStack {
+ if model.transactions == nil {
+ ProgressView()
+ .onAppear {
+ model.loadTransactions()
+ }
+ } else if model.loading {
+ ProgressView()
+ } else {
+ List(model.transactions!, id: \.self) { tx in
+ VStack {
+ Text("Transaction: \(tx.transactionId)")
+ }
+ }
+ Text("Loaded")
+ /*VStack {
+ Text("Balances")
+ NavigationLink {
+ TransactionsView(model:
self.balancesModel.getTransactionsModel())
+ } label: {
+ Text("Transactions")
+ }
+
+ }
+ .padding(16)
+ .navigationTitle("Balances")
+ .navigationBarItems(
+ leading: Button(action: self.showSidebar, label: {
+ Image(systemName: "line.3.horizontal")
+ })
+ )*/
+ }
+ }
+ .navigationTitle("Transactions")
+ }
+}
+
struct BalancesView: View {
@ObservedObject var balancesModel: BalancesModel
@EnvironmentObject var backend: BackendManager
@@ -23,19 +64,18 @@ struct BalancesView: View {
var body: some View {
NavigationView {
- switch balancesModel.state {
- case .begin:
- EmptyView()
- .onAppear(perform: {
- balancesModel.getBalances()
- })
+ if balancesModel.balances == nil {
+ ProgressView()
.navigationTitle("Balances")
.navigationBarItems(
leading: Button(action: self.showSidebar, label: {
Image(systemName: "line.3.horizontal")
})
)
- case .loading:
+ .onAppear {
+ balancesModel.getBalances()
+ }
+ } else if balancesModel.loading {
ProgressView()
.navigationTitle("Balances")
.navigationBarItems(
@@ -43,9 +83,15 @@ struct BalancesView: View {
Image(systemName: "line.3.horizontal")
})
)
- case .loaded(let balances):
+ } else {
VStack {
Text("Balances")
+ NavigationLink {
+ TransactionsView(model:
self.balancesModel.getTransactionsModel())
+ } label: {
+ Text("Transactions")
+ }
+
}
.padding(16)
.navigationTitle("Balances")
@@ -57,8 +103,4 @@ struct BalancesView: View {
}
}
}
-
- /*init(showSidebar: @escaping () -> Void) {
- self.showSidebar = showSidebar
- }*/
}
diff --git a/Taler/WalletBackend.swift b/Taler/WalletBackend.swift
index d00a8ae..e6df597 100644
--- a/Taler/WalletBackend.swift
+++ b/Taler/WalletBackend.swift
@@ -202,22 +202,203 @@ enum TransactionType: Codable {
}
}
-/// An error associated with a transaction.
-struct TransactionError: Codable {
- var ec: Int
- var hint: String?
- //var details: Any?
+enum TransactionDecodingError: Error {
+ case invalidStringValue
+}
+
+/// Details for a manual withdrawal.
+struct ManualWithdrawalDetails: Codable {
+ /// The payto URIs that the exchange supports.
+ var exchangePaytoUris: [String]
+
+ /// The public key of the newly created reserve.
+ var reservePub: String
+}
+
+/// Details for a bank-integrated withdrawal.
+struct BankIntegratedWithdrawalDetails: Codable {
+ /// Whether the bank has confirmed the withdrawal.
+ var confirmed: Bool
+
+ /// URL for user-initiated confirmation
+ var bankConfirmationUrl: String?
+}
+
+/// A withdrawal transaction.
+struct TransactionWithdrawal: Decodable {
+ enum WithdrawalDetails {
+ case manual(ManualWithdrawalDetails)
+ case bankIntegrated(BankIntegratedWithdrawalDetails)
+ }
+
+ /// The exchange that was withdrawn from.
+ var exchangeBaseUrl: String
+
+ /// The amount of the withdrawal, including fees.
+ var amountRaw: Amount
+
+ /// The amount that will be added to the withdrawer's account.
+ var amountEffective: Amount
+
+ /// The details of the withdrawal.
+ var withdrawalDetails: WithdrawalDetails
+
+ init(from decoder: Decoder) throws {
+ enum CodingKeys: String, CodingKey {
+ case exchangeBaseUrl
+ case amountRaw
+ case amountEffective
+ case withdrawalDetails
+ case type
+ case exchangePaytoUris
+ case reservePub
+ case confirmed
+ case bankConfirmationUrl
+ }
+
+ let value = try decoder.container(keyedBy: CodingKeys.self)
+ self.exchangeBaseUrl = try value.decode(String.self, forKey:
.exchangeBaseUrl)
+ self.amountRaw = try value.decode(Amount.self, forKey: .amountRaw)
+ self.amountEffective = try value.decode(Amount.self, forKey:
.amountEffective)
+
+ let detail = try value.nestedContainer(keyedBy: CodingKeys.self,
forKey: .withdrawalDetails)
+ let detailType = try detail.decode(String.self, forKey: .type)
+ if detailType == "manual-transfer" {
+ let paytoUris = try detail.decode([String].self, forKey:
.exchangePaytoUris)
+ let reservePub = try detail.decode(String.self, forKey:
.reservePub)
+ let manual = ManualWithdrawalDetails(exchangePaytoUris: paytoUris,
reservePub: reservePub)
+ self.withdrawalDetails = .manual(manual)
+ } else if detailType == "taler-bank-integration-api" {
+ let confirmed = try detail.decode(Bool.self, forKey: .confirmed)
+ var bankConfirmationUrl: String? = nil
+ if detail.contains(.bankConfirmationUrl) {
+ bankConfirmationUrl = try detail.decode(String.self, forKey:
.bankConfirmationUrl)
+ }
+ let bankDetails = BankIntegratedWithdrawalDetails(confirmed:
confirmed, bankConfirmationUrl: bankConfirmationUrl)
+ self.withdrawalDetails = .bankIntegrated(bankDetails)
+ } else {
+ throw TransactionDecodingError.invalidStringValue
+ }
+ }
+}
+
+/// A payment transaction.
+struct TransactionPayment: Codable {
+ /// Additional information about the payment.
+ // TODO
+
+ /// An identifier for the payment.
+ var proposalId: String
+
+ /// The current status of the payment.
+ // TODO
+
+ /// The amount that must be paid.
+ var amountRaw: Amount
+
+ /// The amount that was paid.
+ var amountEffective: Amount
+}
+
+/// A refund transaction.
+struct TransactionRefund: Codable {
+ /// Identifier for the refund.
+ var refundedTransactionId: String
+
+ /// Additional information about the refund
+ // TODO
+
+ /// The amount that couldn't be applied because refund permissions expired.
+ var amountInvalid: Amount
+
+ /// The amount refunded by the merchant.
+ var amountRaw: Amount
+
+ /// The amount paid to the wallet after fees.
+ var amountEffective: Amount
+}
+
+/// A tip transaction.
+struct TransactionTip: Codable {
+ /// The current status of the tip.
+ // TODO
+
+ /// The exchange that the tip will be withdrawn from
+ var exchangeBaseUrl: String
+
+ /// More information about the merchant sending the tip.
+ // TODO
+
+ /// The raw amount of the tip without fees.
+ var amountRaw: Amount
+
+ /// The amount added to the recipient's wallet.
+ var amountEffective: Amount
+}
+
+/// A refresh transaction.
+struct TransactionRefresh: Codable {
+ /// The exchange that the coins are refreshed with.
+ var exchangeBaseUrl: String
+
+ /// The raw amount to refresh.
+ var amountRaw: Amount
+
+ /// The amount to be paid as fees for the refresh.
+ var amountEffective: Amount
}
/// A wallet transaction.
-struct Transaction: Codable {
+struct Transaction: Decodable, Hashable {
+ enum TransactionDetail {
+ case withdrawal(TransactionWithdrawal)
+ }
+
var transactionId: String
- var type: TransactionType
var timestamp: Timestamp
var pending: Bool
- var error: TransactionError?
+ var error: AnyCodable?
var amountRaw: Amount
var amountEffective: Amount
+ var detail: TransactionDetail
+
+ init(from decoder: Decoder) throws {
+ enum CodingKeys: String, CodingKey {
+ case transactionId
+ case timestamp
+ case pending
+ case error
+ case amountRaw
+ case amountEffective
+ case type
+ }
+
+ let value = try decoder.container(keyedBy: CodingKeys.self)
+ self.transactionId = try value.decode(String.self, forKey:
.transactionId)
+ self.timestamp = try value.decode(Timestamp.self, forKey: .timestamp)
+ self.pending = try value.decode(Bool.self, forKey: .pending)
+ if value.contains(.error) {
+ self.error = try value.decode(AnyCodable.self, forKey: .error)
+ }
+ self.amountRaw = try value.decode(Amount.self, forKey: .amountRaw)
+ self.amountEffective = try value.decode(Amount.self, forKey:
.amountEffective)
+
+ let type = try value.decode(String.self, forKey: .type)
+ if type == "withdrawal" {
+ let withdrawDetail = try TransactionWithdrawal.init(from: decoder)
+ self.detail = .withdrawal(withdrawDetail)
+ } else {
+ throw TransactionDecodingError.invalidStringValue
+ }
+ }
+
+ static func == (lhs: Transaction, rhs: Transaction) -> Bool {
+ return lhs.transactionId == rhs.transactionId
+ }
+
+ func hash(into hasher: inout Hasher) {
+ transactionId.hash(into: &hasher)
+ }
}
/// A request to get the transactions in the wallet's history.
@@ -231,7 +412,7 @@ struct WalletBackendGetTransactionsRequest:
WalletBackendFormattedRequest {
}
struct Response: Decodable {
-
+ var transactions: [Transaction]
}
func operation() -> String {
@@ -901,6 +1082,7 @@ class WalletBackend: IonoMessageHandler {
}
func handleMessage(message: String) {
+ print(message)
do {
guard let messageData = message.data(using: .utf8) else { throw
WalletBackendError.deserializationError }
let data = try JSONSerialization.jsonObject(with: messageData,
options: .allowFragments) as? [String : Any]
diff --git a/taler-swift/Sources/taler-swift/Time.swift
b/taler-swift/Sources/taler-swift/Time.swift
index cca3bd3..b1d712e 100644
--- a/taler-swift/Sources/taler-swift/Time.swift
+++ b/taler-swift/Sources/taler-swift/Time.swift
@@ -30,15 +30,15 @@ public enum Timestamp: Codable, Equatable {
case never
enum CodingKeys: String, CodingKey {
- case t_ms = "t_ms"
+ case t_s = "t_s"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
- self = Timestamp.milliseconds(try container.decode(UInt64.self,
forKey: .t_ms))
+ self = Timestamp.milliseconds(try container.decode(UInt64.self,
forKey: .t_s) * 1000)
} catch {
- let stringValue = try container.decode(String.self, forKey: .t_ms)
+ let stringValue = try container.decode(String.self, forKey: .t_s)
if stringValue == "never" {
self = Timestamp.never
} else {
@@ -55,9 +55,9 @@ public enum Timestamp: Codable, Equatable {
var value = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .milliseconds(let t_ms):
- try value.encode(t_ms, forKey: .t_ms)
+ try value.encode(t_ms / 1000, forKey: .t_s)
case .never:
- try value.encode("never", forKey: .t_ms)
+ try value.encode("never", forKey: .t_s)
}
}
}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-taler-ios] branch master updated: parse withdraw transaction details,
gnunet <=