gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-ios] branch master updated (df4fe35 -> 4f2b20c)


From: gnunet
Subject: [taler-taler-ios] branch master updated (df4fe35 -> 4f2b20c)
Date: Wed, 22 Feb 2023 16:16:27 +0100

This is an automated email from the git hooks/post-receive script.

marc-stibane pushed a change to branch master
in repository taler-ios.

    from df4fe35  Build instructions
     new 3add557  Error handling, Amount.diff
     new 3cb19f2  ported remaining sync wallet-core funcs to try await
     new 744d32a  added ext+taler and web+taler to the list of recognized URL 
schemes
     new a4069f8  Constants, cleanup
     new cc28130  Transaction definition and JSON decoding - Bug 7678
     new a383bd5  Info & SceneConfigurations, v0.9.2, bundleID
     new 4f2b20c  Transaction list and details

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 Info.plist                                         |  11 +-
 TalerWallet.xcodeproj/project.pbxproj              |  22 +-
 TalerWallet1/Backend/Transaction.swift             | 407 +++++++++------------
 TalerWallet1/Backend/WalletCore.swift              |  18 +-
 TalerWallet1/Controllers/Controller.swift          |   2 +-
 TalerWallet1/Controllers/TalerWallet1App.swift     |   9 +-
 .../{TalerStrings.swift => PublicConstants.swift}  |  16 +-
 TalerWallet1/Model/ExchangeTestModel.swift         |  87 ++---
 TalerWallet1/Model/WalletInitModel.swift           |   6 +-
 .../Views/Balances/CurrenciesListView.swift        |   9 +-
 TalerWallet1/Views/Exchange/ExchangeListView.swift |   4 +-
 TalerWallet1/Views/Payment/PaymentAcceptView.swift |   2 +-
 TalerWallet1/Views/Pending/PendingModel.swift      |   4 +-
 .../Views/Pending/PendingOpsListView.swift         |  16 +-
 TalerWallet1/Views/Settings/SettingsView.swift     |  26 +-
 .../Views/Transactions/TransactionDetail.swift     |  95 +++--
 .../Views/Transactions/TransactionRow.swift        |  31 +-
 .../Views/Transactions/TransactionsListView.swift  |  30 +-
 .../Views/Transactions/TransactionsModel.swift     |   8 -
 .../Views/Withdraw/WithdrawAcceptView.swift        |   2 +-
 TalerWallet1/Views/Withdraw/WithdrawURIView.swift  |   2 +-
 21 files changed, 388 insertions(+), 419 deletions(-)
 copy TalerWallet1/Helper/{TalerStrings.swift => PublicConstants.swift} (75%)

diff --git a/Info.plist b/Info.plist
index b918fac..4f254b8 100644
--- a/Info.plist
+++ b/Info.plist
@@ -8,15 +8,24 @@
                        <key>CFBundleTypeRole</key>
                        <string>Viewer</string>
                        <key>CFBundleURLName</key>
-                       <string>net.taler.talerwallet</string>
+                       <string>com.taler-systems.talerwallet</string>
                        <key>CFBundleURLSchemes</key>
                        <array>
                                <string>taler</string>
+                               <string>ext+taler</string>
+                               <string>web+taler</string>
                                <string>payto</string>
                        </array>
                </dict>
        </array>
        <key>ITSAppUsesNonExemptEncryption</key>
        <false/>
+       <key>UIApplicationSceneManifest</key>
+       <dict>
+               <key>UIApplicationSupportsMultipleScenes</key>
+               <true/>
+               <key>UISceneConfigurations</key>
+               <dict/>
+       </dict>
 </dict>
 </plist>
diff --git a/TalerWallet.xcodeproj/project.pbxproj 
b/TalerWallet.xcodeproj/project.pbxproj
index 4d986fe..6281226 100644
--- a/TalerWallet.xcodeproj/project.pbxproj
+++ b/TalerWallet.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
        objects = {
 
 /* Begin PBXBuildFile section */
+               4EA1ABBE29A3833A008821EA /* PublicConstants.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EA1ABBD29A3833A008821EA /* 
PublicConstants.swift */; };
                4EB094D629896CD20043A8A1 /* TalerWalletTests.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EB094D429896CD20043A8A1 /* 
TalerWalletTests.swift */; };
                4EB094D729896CD20043A8A1 /* WalletBackendTests.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EB094D529896CD20043A8A1 /* 
WalletBackendTests.swift */; };
                4EB094DC29896D030043A8A1 /* TalerWalletUITestsLaunchTests.swift 
in Sources */ = {isa = PBXBuildFile; fileRef = 4EB094D929896D030043A8A1 /* 
TalerWalletUITestsLaunchTests.swift */; };
@@ -97,6 +98,7 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+               4EA1ABBD29A3833A008821EA /* PublicConstants.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
PublicConstants.swift; sourceTree = "<group>"; };
                4EB094D429896CD20043A8A1 /* TalerWalletTests.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= TalerWalletTests.swift; sourceTree = "<group>"; };
                4EB094D529896CD20043A8A1 /* WalletBackendTests.swift */ = {isa 
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; 
path = WalletBackendTests.swift; sourceTree = "<group>"; };
                4EB094D929896D030043A8A1 /* TalerWalletUITestsLaunchTests.swift 
*/ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.swift; path = TalerWalletUITestsLaunchTests.swift; sourceTree = 
"<group>"; };
@@ -234,6 +236,7 @@
                                4EB095062989CB7C0043A8A1 /* TalerDater.swift */,
                                4EB095072989CB7C0043A8A1 /* TalerStrings.swift 
*/,
                                4EB095082989CB7C0043A8A1 /* 
View+dismissTop.swift */,
+                               4EA1ABBD29A3833A008821EA /* 
PublicConstants.swift */,
                        );
                        path = Helper;
                        sourceTree = "<group>";
@@ -588,6 +591,7 @@
                                4EB095562989CBFE0043A8A1 /* 
TransactionsListView.swift in Sources */,
                                4EB0951F2989CBCB0043A8A1 /* 
WalletBackendRequest.swift in Sources */,
                                4EB095572989CBFE0043A8A1 /* 
TransactionRow.swift in Sources */,
+                               4EA1ABBE29A3833A008821EA /* 
PublicConstants.swift in Sources */,
                                4EB0956B2989CBFE0043A8A1 /* 
TextFieldAlert.swift in Sources */,
                                4EB0956C2989CBFE0043A8A1 /* AmountView.swift in 
Sources */,
                                4EB095592989CBFE0043A8A1 /* 
TransactionsModel.swift in Sources */,
@@ -771,15 +775,14 @@
                                ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
                                CLANG_ENABLE_MODULES = YES;
                                CODE_SIGN_STYLE = Automatic;
-                               CURRENT_PROJECT_VERSION = 2;
+                               CURRENT_PROJECT_VERSION = 1;
                                DEVELOPMENT_TEAM = "";
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
                                INFOPLIST_FILE = Info.plist;
                                INFOPLIST_KEY_CFBundleDisplayName = "Taler 
Wallet";
                                INFOPLIST_KEY_LSApplicationCategoryType = 
"public.app-category.finance";
-                               INFOPLIST_KEY_NSHumanReadableCopyright = "© 
Taler.net";
-                               
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+                               INFOPLIST_KEY_NSHumanReadableCopyright = "© 
Taler-Systems.com";
                                INFOPLIST_KEY_UILaunchScreen_Generation = YES;
                                INFOPLIST_KEY_UISupportedInterfaceOrientations 
= UIInterfaceOrientationPortrait;
                                
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = 
"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
@@ -788,8 +791,8 @@
                                        "$(inherited)",
                                        "@executable_path/Frameworks",
                                );
-                               MARKETING_VERSION = 0.9.1;
-                               PRODUCT_BUNDLE_IDENTIFIER = 
net.taler.talerwallet;
+                               MARKETING_VERSION = 0.9.2;
+                               PRODUCT_BUNDLE_IDENTIFIER = 
"com.taler-systems.talerwallet15";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SUPPORTED_PLATFORMS = "iphoneos 
iphonesimulator";
                                SUPPORTS_MACCATALYST = NO;
@@ -807,15 +810,14 @@
                                ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME 
= AccentColor;
                                CLANG_ENABLE_MODULES = YES;
                                CODE_SIGN_STYLE = Automatic;
-                               CURRENT_PROJECT_VERSION = 2;
+                               CURRENT_PROJECT_VERSION = 1;
                                DEVELOPMENT_TEAM = "";
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
                                INFOPLIST_FILE = Info.plist;
                                INFOPLIST_KEY_CFBundleDisplayName = "Taler 
Wallet";
                                INFOPLIST_KEY_LSApplicationCategoryType = 
"public.app-category.finance";
-                               INFOPLIST_KEY_NSHumanReadableCopyright = "© 
Taler.net";
-                               
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+                               INFOPLIST_KEY_NSHumanReadableCopyright = "© 
Taler-Systems.com";
                                INFOPLIST_KEY_UILaunchScreen_Generation = YES;
                                INFOPLIST_KEY_UISupportedInterfaceOrientations 
= UIInterfaceOrientationPortrait;
                                
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = 
"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
@@ -824,8 +826,8 @@
                                        "$(inherited)",
                                        "@executable_path/Frameworks",
                                );
-                               MARKETING_VERSION = 0.9.1;
-                               PRODUCT_BUNDLE_IDENTIFIER = 
net.taler.talerwallet;
+                               MARKETING_VERSION = 0.9.2;
+                               PRODUCT_BUNDLE_IDENTIFIER = 
"com.taler-systems.talerwallet15";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SUPPORTED_PLATFORMS = "iphoneos 
iphonesimulator";
                                SUPPORTS_MACCATALYST = NO;
diff --git a/TalerWallet1/Backend/Transaction.swift 
b/TalerWallet1/Backend/Transaction.swift
index 86a0ae9..873733e 100644
--- a/TalerWallet1/Backend/Transaction.swift
+++ b/TalerWallet1/Backend/Transaction.swift
@@ -22,293 +22,214 @@ enum TransactionTypeError: Error {
     case unknownTypeError
 }
 
-/// Different types of transactions.
-enum TransactionType: Codable {
-    case withdrawal
-    case payment
-    case refund
-    case tip
-    case refresh
-    
-    init(from decoder: Decoder) throws {
-        let value = try decoder.singleValueContainer()
-        let str = try value.decode(String.self)
-        let codingNames = [
-            "TransactionWithdrawal" : TransactionType.withdrawal,
-            "TransactionPayment" : TransactionType.payment,
-            "TransactionRefund" : TransactionType.refund,
-            "TransactionTip" : TransactionType.tip,
-            "TransactionRefresh" : TransactionType.refresh
-        ]
-        if let type = codingNames[str] {
-            self = type
-        } else {
-            throw TransactionTypeError.unknownTypeError
-        }
-    }
-    
-    func encode(to encoder: Encoder) throws {
-        var value = encoder.singleValueContainer()
-        switch self {
-            case .withdrawal:
-                try value.encode("TransactionWithdrawal")
-            case .payment:
-                try value.encode("TransactionPayment")
-            case .refund:
-                try value.encode("TransactionRefund")
-            case .tip:
-                try value.encode("TransactionTip")
-            case .refresh:
-                try value.encode("TransactionRefresh")
-        }
-    }
-}
-
 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
+struct TransactionCommon: Decodable {
+    var type: String
+    var amountEffective: Amount
+    var amountRaw: Amount
+    var transactionId: String
+    var timestamp: Timestamp
+    var extendedStatus: String      // TODO: enum with some fixed values?
+    var pending: Bool
+    var frozen: Bool
+
+    func fee() -> Amount {
+        do {
+            return try Amount.diff(amountRaw, amountEffective)
+        } catch {
+            return Amount(currency: amountRaw.currencyStr, integer: 0, 
fraction: 0)
+        }
+    }
 }
 
-/// Details for a bank-integrated withdrawal.
-struct BankIntegratedWithdrawalDetails: Codable {
-    /// Whether the bank has confirmed the withdrawal.
-    var confirmed: Bool
+struct WithdrawalDetails: Decodable {
+    enum WithdrawalType: String, Decodable {
+        case manual = "manual-transfer"
+        case bankIntegrated = "taler-bank-integration-api"
+        case peerPullCredit = "peer-pull-credit"
+        case peerPushCredit = "peer-push-credit"
+    }
+    var type: WithdrawalType
+    /// The public key of the reserve.
+    var reservePub: String
 
-    /// The public key of the newly created reserve.
-    var reservePub: String?
+  /// Details for manual withdrawals:
+    /// The payto URIs that the exchange supports.
+    var exchangePaytoUris: [String]?
 
+  /// Details for bank-integrated withdrawals:
+    /// 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.
+struct WithdrawalTransactionDetails: Decodable {
     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)
-            }
-            var reservePub : String? = nil
-            if detail.contains(.reservePub) {
-                reservePub = try detail.decode(String.self, forKey: 
.reservePub)
-            }
-            let bankDetails = BankIntegratedWithdrawalDetails(confirmed: 
confirmed, reservePub: reservePub,
-                                                              
bankConfirmationUrl: bankConfirmationUrl)
-            self.withdrawalDetails = .bankIntegrated(bankDetails)
-        } else {
-            throw TransactionDecodingError.invalidStringValue
-        }
-    }
 }
-#if DEBUG
-extension TransactionWithdrawal {       // for PreViews
-    init(url: String) {
-        self.exchangeBaseUrl = url
-        self.amountRaw = try! Amount(fromString: "Taler:5")
-        self.amountEffective = try! Amount(fromString: "Taler:4.8")
-        let bankDetails = BankIntegratedWithdrawalDetails(confirmed: true, 
reservePub: nil,
-                                                          bankConfirmationUrl: 
nil)
-        self.withdrawalDetails = .bankIntegrated(bankDetails)
-    }
+
+struct WithdrawalTransaction {
+    var common: TransactionCommon
+    var details: WithdrawalTransactionDetails
 }
-#endif
 
-/// A payment transaction.
-struct TransactionPayment: Codable {
-    /// Additional information about the payment.
-    // TODO
-    
-    /// An identifier for the payment.
+struct PaymentTransactionDetails: Decodable {
     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
+    var status: String          // "paid"
+    var totalRefundRaw: Amount
+    var totalRefundEffective: Amount
+    var refundPending: Amount?
+//    var refunds: []
+    var info: OrderShortInfo
 }
 
-/// A refund transaction.
-struct TransactionRefund: Codable {
-    /// Identifier for the refund.
+struct PaymentTransaction {
+    var common: TransactionCommon
+    var details: PaymentTransactionDetails
+}
+
+struct RefundTransactionDetails: Decodable {
     var refundedTransactionId: String
-    
-    /// Additional information about the refund
-    // TODO
-    
+    var refundPending: Amount?
     /// 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
+    var amountInvalid: Amount?
+    var info: OrderShortInfo
+}
+
+struct RefundTransaction {
+    var common: TransactionCommon
+    var details: RefundTransactionDetails
 }
 
-/// A tip transaction.
-struct TransactionTip: Codable {
-    /// The current status of the tip.
-    // TODO
-    
+struct TipTransactionDetails: Decodable {
     /// 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 {
+struct TipTransaction {
+    var common: TransactionCommon
+    var details: TipTransactionDetails
+}
+
+struct RefreshTransactionDetails: Decodable {
     /// 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: Decodable, Hashable {
-//    private let symLog = SymLogC(0)
-
-    var type: String
-    var amountRaw: Amount
-    var amountEffective: Amount
-    var transactionId: String
-    var timestamp: Timestamp
-    var extendedStatus: String
-    var pending: Bool
-    var frozen: Bool
+struct RefreshTransaction {
+    var common: TransactionCommon
+    var details: RefreshTransactionDetails
+}
 
-    var error: AnyCodable?
-    var exchangeBaseUrl: String?
+enum Transaction: Decodable, Hashable, Identifiable {
+    case withdrawal (WithdrawalTransaction)
+    case payment (PaymentTransaction)
+    case refund (RefundTransaction)
+    case tip (TipTransaction)
+    case refresh (RefreshTransaction)
 
+    init(from decoder: Decoder) throws {
+        let common = try TransactionCommon.init(from: decoder)
+
+        switch (common.type) {
+            case WITHDRAWAL:
+                let details = try WithdrawalTransactionDetails.init(from: 
decoder)
+                self = .withdrawal(WithdrawalTransaction(common: common, 
details: details))
+            case PAYMENT:
+                let details = try PaymentTransactionDetails.init(from: decoder)
+                self = .payment(PaymentTransaction(common: common, details: 
details))
+            case REFUND:
+                let details = try RefundTransactionDetails.init(from: decoder)
+                self = .refund(RefundTransaction(common: common, details: 
details))
+            case TIP:
+                let details = try TipTransactionDetails.init(from: decoder)
+                self = .tip(TipTransaction(common: common, details: details))
+            case REFRESH:
+                let details = try RefreshTransactionDetails.init(from: decoder)
+                self = .refresh(RefreshTransaction(common: common, details: 
details))
+            default:
+                let context = DecodingError.Context(
+                    codingPath: decoder.codingPath,
+                    debugDescription: "Invalid transaction type")
+                throw DecodingError.typeMismatch(Transaction.self, context)
+        }
+    }
 
+    var id: String { common().transactionId }
 
-//    enum TransactionDetail {
-//        case withdrawal(TransactionWithdrawal)
-//    }
-    
-//    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
-//        }
-//        symLog.log("\(self)")
-//    }
-    
     static func == (lhs: Transaction, rhs: Transaction) -> Bool {
-        return lhs.transactionId == rhs.transactionId
+        return lhs.id == rhs.id
     }
-    
+
     func hash(into hasher: inout Hasher) {
-        transactionId.hash(into: &hasher)
+        id.hash(into: &hasher)
+    }
+
+    func common() -> TransactionCommon {
+        switch self {
+            case .withdrawal(let withdrawalTransaction):
+                return withdrawalTransaction.common
+            case .payment(let paymentTransaction):
+                return paymentTransaction.common
+            case .refund(let refundTransaction):
+                return refundTransaction.common
+            case .tip(let tipTransaction):
+                return tipTransaction.common
+            case .refresh(let refreshTransaction):
+                return refreshTransaction.common
+        }
+    }
+
+    func detailsToShow() -> Dictionary<String, String> {
+        var result: [String:String] = [:]
+        switch self {
+            case .withdrawal(let withdrawalTransaction):
+                result[EXCHANGEBASEURL] = 
withdrawalTransaction.details.exchangeBaseUrl
+            case .payment(let paymentTransaction):
+                result["status"] = paymentTransaction.details.status
+            case .refund(let refundTransaction):
+                result["summary"] = refundTransaction.details.info.summary
+            case .tip(let tipTransaction):
+                result[EXCHANGEBASEURL] = 
tipTransaction.details.exchangeBaseUrl
+            case .refresh(let refreshTransaction):
+                result[EXCHANGEBASEURL] = 
refreshTransaction.details.exchangeBaseUrl
+        }
+        return result
     }
 }
 
+
 #if DEBUG
 extension Transaction {             // for PreViews
-    init(id: String, time: Timestamp) {
-        self.type = "withdrawal"
-        self.amountRaw = try! Amount(fromString: "Taler:5")
-        self.amountEffective = try! Amount(fromString: "Taler:4.8")
-        self.transactionId = id
-        self.timestamp = time
-        self.extendedStatus = "done"
-        self.pending = false
-        self.frozen = false
-        self.error = nil
-        self.exchangeBaseUrl = "Exchange.Demo.Taler.net"
-//        let withdrawDetail = TransactionWithdrawal(url: 
"Exchange.Demo.Taler.net")
-//        self.detail = .withdrawal(withdrawDetail)
+    init(incoming: Bool, id: String, time: Timestamp) {
+        let effective = incoming ? "Taler:4.8" : "Taler:5.2"
+        let common = TransactionCommon(type: incoming ? WITHDRAWAL : PAYMENT,
+                            amountEffective: try! Amount(fromString: 
effective),
+                                  amountRaw: try! Amount(fromString: 
"Taler:5"),
+                              transactionId: id, timestamp: time,
+                             extendedStatus: "done", pending: false, frozen: 
false)
+        if incoming {
+            let withdrawalDetails = WithdrawalDetails(type: 
WithdrawalDetails.WithdrawalType.bankIntegrated,
+                                                      reservePub: "Public Key 
of the Exchange",
+                                                      confirmed: true)
+            let wDetails = WithdrawalTransactionDetails(exchangeBaseUrl: 
"Exchange.Demo.Taler.net",
+                                                        withdrawalDetails: 
withdrawalDetails)
+            self = .withdrawal(WithdrawalTransaction(common: common, details: 
wDetails))
+        } else {
+            let merchant = Merchant(name: "some random shop")
+            let info = OrderShortInfo(orderId: "some order ID",
+                                      merchant: merchant, summary: "some 
product summary", products: [])
+            let pDetails = PaymentTransactionDetails(proposalId: "some 
proposal ID",
+                                                     status: "paid",
+                                                     totalRefundRaw: try! 
Amount(fromString: "Taler:3.2"),
+                                                     totalRefundEffective: 
try! Amount(fromString: "Taler:3"),
+                                                     info: info)
+            self = .payment(PaymentTransaction(common: common, details: 
pDetails))
+        }
     }
 }
 #endif
diff --git a/TalerWallet1/Backend/WalletCore.swift 
b/TalerWallet1/Backend/WalletCore.swift
index ff67b15..be7450d 100644
--- a/TalerWallet1/Backend/WalletCore.swift
+++ b/TalerWallet1/Backend/WalletCore.swift
@@ -116,6 +116,7 @@ extension WalletCore {
                     symLog.log(payload)
                     Task {
                         do {
+                            // automatically fetch balances after receiving 
these notifications
                             try await 
Controller.shared.balancesModel.fetchBalances()
                         } catch {
                             // TODO: show error
@@ -210,23 +211,6 @@ extension WalletCore {
             completionHandler(id, nil, WalletCore.serializeRequestError());
         }
     }
-
-    /// call this to send requests to wallet-core
-    func sendFormattedRequest<T: WalletBackendFormattedRequest>
-            (request: T, completionHandler: @escaping (T.Response?, 
WalletBackendResponseError?) -> Void)
-    {
-        let reqData = WalletBackendRequest(operation: request.operation(),
-                                                args: 
AnyEncodable(request.args()))
-        sendRequest(request: reqData) { (id: UInt, result: Data?, err: 
WalletBackendResponseError?) in
-            guard let json = result else { completionHandler(nil, err); return 
}
-            do {
-                let decoded = try JSONDecoder().decode(T.Response.self, from: 
json)
-                completionHandler(decoded, err)
-            } catch {
-                completionHandler(nil, WalletCore.parseResponseError())
-            }
-        }
-    }
 }
 // MARK: -  async / await function
 extension WalletCore {
diff --git a/TalerWallet1/Controllers/Controller.swift 
b/TalerWallet1/Controllers/Controller.swift
index 513472a..8e10ec6 100644
--- a/TalerWallet1/Controllers/Controller.swift
+++ b/TalerWallet1/Controllers/Controller.swift
@@ -89,7 +89,7 @@ extension Controller {
             case "taler+http":
                 uncrypted = true
                 fallthrough
-            case "taler":
+            case "taler", "ext+taler", "web+taler":
                 return talerScheme(url, uncrypted)
             case "payto":
                 messageForSheet = url.absoluteString
diff --git a/TalerWallet1/Controllers/TalerWallet1App.swift 
b/TalerWallet1/Controllers/TalerWallet1App.swift
index 11f8645..1eac830 100644
--- a/TalerWallet1/Controllers/TalerWallet1App.swift
+++ b/TalerWallet1/Controllers/TalerWallet1App.swift
@@ -16,11 +16,6 @@
 import BackgroundTasks
 import SwiftUI
 import SymLog
-#if DEBUG
-let schemes: Set = ["taler", "payto", "taler+http"]
-#else
-let schemes: Set = ["taler", "payto"]
-#endif
 
 @main
 struct TalerWallet1App: App {
@@ -40,10 +35,12 @@ struct TalerWallet1App: App {
         WindowGroup {
             symLog { ContentView()
                     .environmentObject(controller)
+                        /// external events are taler:// or payto:// URLs 
passed to this app
+                        /// we handle them in .onOpenURL in ContentView.swift
                     .handlesExternalEvents(preferring: ["*"], allowing: ["*"])
                     .task {
                         symLog.log("task -> initWalletCore")
-                        try? await controller.initWalletCore()
+                        try! await controller.initWalletCore()      // will 
(and should) crash on failure
                         symLog.log("task done")
                     }
             }
diff --git a/TalerWallet1/Helper/TalerStrings.swift 
b/TalerWallet1/Helper/PublicConstants.swift
similarity index 75%
copy from TalerWallet1/Helper/TalerStrings.swift
copy to TalerWallet1/Helper/PublicConstants.swift
index b642798..01680a0 100644
--- a/TalerWallet1/Helper/TalerStrings.swift
+++ b/TalerWallet1/Helper/PublicConstants.swift
@@ -15,14 +15,10 @@
  */
 import Foundation
 
-extension StringProtocol {
+public let EXCHANGEBASEURL = "exchangeBaseUrl"
 
-    func trimURL() -> String {
-        if let url = URL(string: String(self)) {
-            if let host = url.host {
-                return host
-            }
-        }
-        return String(self)
-    }
-}
+public let WITHDRAWAL = "withdrawal"
+public let PAYMENT = "payment"
+public let REFUND = "refund"
+public let TIP = "tip"
+public let REFRESH = "refresh"
diff --git a/TalerWallet1/Model/ExchangeTestModel.swift 
b/TalerWallet1/Model/ExchangeTestModel.swift
index f73592a..98702d5 100644
--- a/TalerWallet1/Model/ExchangeTestModel.swift
+++ b/TalerWallet1/Model/ExchangeTestModel.swift
@@ -16,70 +16,47 @@
 import Foundation
 import taler_swift
 import SymLog
+fileprivate let ASYNCDELAY: UInt = 0   //set e.g to 6 or 9 seconds for 
debugging
 
-fileprivate let EXCHANGEBASEURL = "https://exchange.demo.taler.net/";
-fileprivate let BANKBASEURL = "https://bank.demo.taler.net/";
-fileprivate let BANKACCESSAPIBASEURL = 
"https://bank.demo.taler.net/demobanks/default/access-api/";
-fileprivate let MERCHANTBASEURL = "https://backend.demo.taler.net/";
-fileprivate let MERCHANTAUTHTOKEN = "secret-token:sandbox"
+fileprivate let DEMO_EXCHANGEBASEURL = "https://exchange.demo.taler.net/";
+fileprivate let DEMO_BANKBASEURL = "https://bank.demo.taler.net/";
+fileprivate let DEMO_BANKAPIBASEURL = 
"https://bank.demo.taler.net/demobanks/default/access-api/";
+fileprivate let DEMO_MERCHANTBASEURL = "https://backend.demo.taler.net/";
+fileprivate let DEMO_MERCHANTAUTHTOKEN = "secret-token:sandbox"
 
 // MARK: -
-class ExchangeTestModel: ObservableObject {
-    private let symLog = SymLogC(0)
-
-    var walletCore: WalletCore
-    
-    @Published var loading: Bool = false
-    
-    init(walletCore: WalletCore) {
-        self.walletCore = walletCore
-    }
+class ExchangeTestModel: WalletModel {
 }
 // MARK: -
 extension ExchangeTestModel {
-    func loadTestKudos() {
-        loading = true
-        
-        let amount = Amount(currency: "KUDOS", integer: 11, fraction: 0)
-        let req = WalletBackendWithdrawTestBalance(amount: amount, 
bankBaseUrl: BANKBASEURL,
-                        exchangeBaseUrl: EXCHANGEBASEURL, 
bankAccessApiBaseUrl: BANKACCESSAPIBASEURL)
-        symLog.log("sending: \(req)")
-        walletCore.sendFormattedRequest(request: req) { response, err in
-            DispatchQueue.main.async {
-                self.loading = false
-                if let res = response {
-                    // TODO: ?
-                    self.symLog.log("received: \(res)")
-                } else {
-                    // TODO: Handle error
-                }
-            }
+    @MainActor func loadTestKudos() async throws {
+        do {
+            let amount = Amount(currency: "KUDOS", 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)")
+        } catch {
+            throw error
         }
     }
 
-    func runIntegrationTest() {
-        loading = true
-
-        let amountW = Amount(currency: "KUDOS", integer: 3, fraction: 0)
-        let amountS = Amount(currency: "KUDOS", integer: 1, fraction: 0)
-        let req = WalletBackendRunIntegration(amountToWithdraw: amountW,
-                                              amountToSpend: amountS,
-                                              bankBaseUrl: 
BANKACCESSAPIBASEURL,
-                                              exchangeBaseUrl: EXCHANGEBASEURL,
-                                              merchantBaseUrl: MERCHANTBASEURL,
-                                              merchantAuthToken: 
MERCHANTAUTHTOKEN
-        )
-        symLog.log("sending: \(req)")
-        walletCore.sendFormattedRequest(request: req) { response, err in
-            DispatchQueue.main.async {
-                self.loading = false
-                if let res = response {
-                    // TODO: ?
-                    self.symLog.log("received: \(res)")
-                } else {
-                    // TODO: Handle error
-                }
-            }
+    @MainActor func runIntegrationTest() async throws {
+        do {
+            let amountW = Amount(currency: "KUDOS", integer: 3, fraction: 0)
+            let amountS = Amount(currency: "KUDOS", integer: 1, fraction: 0)
+            let request = WalletBackendRunIntegration(amountToWithdraw: 
amountW,
+                                                         amountToSpend: 
amountS,
+                                                           bankBaseUrl: 
DEMO_BANKAPIBASEURL,
+                                                       exchangeBaseUrl: 
DEMO_EXCHANGEBASEURL,
+                                                       merchantBaseUrl: 
DEMO_MERCHANTBASEURL,
+                                                     merchantAuthToken: 
DEMO_MERCHANTAUTHTOKEN)
+            let response = try await sendRequest(request, ASYNCDELAY)
+            symLog?.log("received: \(response)")
+        } catch {
+            throw error
         }
     }
 }
diff --git a/TalerWallet1/Model/WalletInitModel.swift 
b/TalerWallet1/Model/WalletInitModel.swift
index e254d41..7be0dff 100644
--- a/TalerWallet1/Model/WalletInitModel.swift
+++ b/TalerWallet1/Model/WalletInitModel.swift
@@ -27,12 +27,14 @@ fileprivate struct WalletBackendInitRequest: 
WalletBackendFormattedRequest {
     func operation() -> String { return "init" }
     func args() -> Args {
         return Args(persistentStoragePath: persistentStoragePath,
-                    cryptoWorkerType: "sync")
+//                    cryptoWorkerType: "sync",
+                    logLevel: "info")       //  "trace", "info", "warn", 
"error", "none"
     }
 
     struct Args: Encodable {
         var persistentStoragePath: String
-        var cryptoWorkerType: String?
+//        var cryptoWorkerType: String
+        var logLevel: String
     }
 
     var persistentStoragePath: String
diff --git a/TalerWallet1/Views/Balances/CurrenciesListView.swift 
b/TalerWallet1/Views/Balances/CurrenciesListView.swift
index a40b074..192d531 100644
--- a/TalerWallet1/Views/Balances/CurrenciesListView.swift
+++ b/TalerWallet1/Views/Balances/CurrenciesListView.swift
@@ -69,8 +69,13 @@ extension CurrenciesListView {
                 }
                     .navigationBarTitleDisplayMode(.large)      // .inline
                     .refreshable {
-                        symLog?.log("refreshing")
-                        try? await reloadAction()       // TODO: catch error
+                        do {
+                            symLog?.log("refreshing")
+                            try await reloadAction()
+                        } catch {
+                            // TODO: error
+                            symLog?.log(error.localizedDescription)
+                        }
                     }
             }
         }
diff --git a/TalerWallet1/Views/Exchange/ExchangeListView.swift 
b/TalerWallet1/Views/Exchange/ExchangeListView.swift
index b4b8b9f..fbd52e4 100644
--- a/TalerWallet1/Views/Exchange/ExchangeListView.swift
+++ b/TalerWallet1/Views/Exchange/ExchangeListView.swift
@@ -85,11 +85,11 @@ extension ExchangeListView {
                     }
                         .navigationBarTitleDisplayMode(.large)      // .inline
                         .refreshable {
-                            symLog.log("refreshing")
                             do {
+                                symLog.log("refreshing")
                                 try await reloadAction()
                             } catch {
-                                // TODO: catch error
+                                // TODO: error
                                 symLog.log(error.localizedDescription)
                             }
                         }
diff --git a/TalerWallet1/Views/Payment/PaymentAcceptView.swift 
b/TalerWallet1/Views/Payment/PaymentAcceptView.swift
index 7920b87..26eb916 100644
--- a/TalerWallet1/Views/Payment/PaymentAcceptView.swift
+++ b/TalerWallet1/Views/Payment/PaymentAcceptView.swift
@@ -35,7 +35,7 @@ struct PaymentAcceptView: View {
         symLog { Group {
             let raw = detailsForAmount.amountRaw
             let effective = detailsForAmount.amountEffective
-            let fee = try! effective - raw
+            let fee = try! Amount.diff(raw, effective)      // TODO: different 
currencies
             Form {
                 AmountView(title: "Amount to pay:",
                            value: raw.readableDescription, color: 
Color(UIColor.label))
diff --git a/TalerWallet1/Views/Pending/PendingModel.swift 
b/TalerWallet1/Views/Pending/PendingModel.swift
index cb88523..6b0e38e 100644
--- a/TalerWallet1/Views/Pending/PendingModel.swift
+++ b/TalerWallet1/Views/Pending/PendingModel.swift
@@ -56,7 +56,7 @@ struct PendingOperation: Codable, Hashable {
 
 }
 //let pending1 = ["type": "exchange-update",
-//                "exchangeBaseUrl": "https://exchange.demo.taler.net/";,
+//                EXCHANGEBASEURL: "https://exchange.demo.taler.net/";,
 //                "id": "exchange-update:https://exchange.demo.taler.net/";,
 //                "timestampDue": ["t_ms": 1669931055000],
 //                "isDue": false,
@@ -64,7 +64,7 @@ struct PendingOperation: Codable, Hashable {
 //                "givesLifeness": false] as [String : Any]
 //
 //let pending2 = ["type": "exchange-check-refresh",
-//                "exchangeBaseUrl": "https://exchange.demo.taler.net/";,
+//                EXCHANGEBASEURL: "https://exchange.demo.taler.net/";,
 //                "id": "exchange-update:https://exchange.demo.taler.net/";,
 //                "timestampDue": ["t_ms": 1670013862000],
 //                "isDue": false,
diff --git a/TalerWallet1/Views/Pending/PendingOpsListView.swift 
b/TalerWallet1/Views/Pending/PendingOpsListView.swift
index 30a5de3..a4d9e61 100644
--- a/TalerWallet1/Views/Pending/PendingOpsListView.swift
+++ b/TalerWallet1/Views/Pending/PendingOpsListView.swift
@@ -37,7 +37,12 @@ struct PendingOpsListView: View {
             }
         }.task {
             symLog.log(".task")
-            try? await reloadAction()       // TODO: catch error
+            do {
+                try await reloadAction()
+            } catch {
+                // TODO: show error
+                symLog.log(error.localizedDescription)
+            }
         }
     }
 }
@@ -56,8 +61,13 @@ extension PendingOpsListView {
                 }
                 .navigationBarTitleDisplayMode(.large)      // .inline
                 .refreshable {
-                    symLog?.log("refreshing")
-                    try? await reloadAction()       // TODO: catch error
+                    do {
+                        symLog?.log("refreshing")
+                        try await reloadAction()
+                    } catch {
+                        // TODO: error
+                        symLog?.log(error.localizedDescription)
+                    }
                 }
             }
         }
diff --git a/TalerWallet1/Views/Settings/SettingsView.swift 
b/TalerWallet1/Views/Settings/SettingsView.swift
index a2f3ea5..b84b242 100644
--- a/TalerWallet1/Views/Settings/SettingsView.swift
+++ b/TalerWallet1/Views/Settings/SettingsView.swift
@@ -66,9 +66,16 @@ struct SettingsView: View {
                     SettingsItem(name: "Withdraw KUDOS", description: "Get 
money for testing") {
                         Button("Withdraw") {
                             withDrawDisabled = true    // don't run twice
-                            let testModel: ExchangeTestModel = 
ExchangeTestModel(walletCore: walletCore)
-                            symLog.log("Withdrawing ")
-                            testModel.loadTestKudos()
+                            Task {
+                                let testModel: ExchangeTestModel = 
ExchangeTestModel(walletCore: walletCore)
+                                symLog.log("Withdrawing")
+                                do {
+                                    try await testModel.loadTestKudos()
+                                } catch {
+                                    // TODO: show error
+                                    symLog.log(error.localizedDescription)
+                                }
+                            }
                         }
                         .buttonStyle(.bordered)
                         .disabled(withDrawDisabled)
@@ -76,9 +83,16 @@ struct SettingsView: View {
                     SettingsItem(name: "Run Integration Test", description: 
"Check if wallet-core works") {
                         Button("Check") {
                             checkDisabled = true    // don't run twice
-                            let testModel: ExchangeTestModel = 
ExchangeTestModel(walletCore: walletCore)
-                            symLog.log("running integration test ")
-                            testModel.runIntegrationTest()
+                            Task {
+                                let testModel: ExchangeTestModel = 
ExchangeTestModel(walletCore: walletCore)
+                                symLog.log("running integration test")
+                                do {
+                                    try await testModel.runIntegrationTest()
+                                } catch {
+                                    // TODO: show error
+                                    symLog.log(error.localizedDescription)
+                                }
+                            }
                         }
                         .buttonStyle(.bordered)
                         .disabled(checkDisabled)
diff --git a/TalerWallet1/Views/Transactions/TransactionDetail.swift 
b/TalerWallet1/Views/Transactions/TransactionDetail.swift
index 84b442a..57faf07 100644
--- a/TalerWallet1/Views/Transactions/TransactionDetail.swift
+++ b/TalerWallet1/Views/Transactions/TransactionDetail.swift
@@ -20,40 +20,47 @@ struct TransactionDetail: View {
     var transaction : Transaction
 
     var body: some View {
-        let raw = transaction.amountRaw
-        let effective = transaction.amountEffective
-        let fee = try! Amount.diff(raw, effective)
-        let dateString = TalerDater.dateString(from: transaction.timestamp)
+        let common = transaction.common()
+        let details = transaction.detailsToShow()
+        let dateString = TalerDater.dateString(from: common.timestamp)
 
         VStack() {
             Spacer()
-            Text("\(dateString)")
+            Text("\(common.type)")      // TODO: translation
                 .font(.title)
                 .fontWeight(.medium)
                 .padding(.bottom)
-            AmountView(title: "Chosen amount to withdraw:",
-                       value:  raw.readableDescription, color: 
Color(UIColor.label))
-                .padding(.bottom)
-            AmountView(title: "Exchange fee:",
-                       value: fee.readableDescription, color: 
Color("Outgoing"))
-                .padding(.bottom)
-            AmountView(title: "Obtained coins:",
-                       value: effective.readableDescription, color: 
Color("Incoming"))
-                .padding(.bottom)
-            if let baseURL = transaction.exchangeBaseUrl {
-                VStack {
-                    Text("From exchange:")
-                        .font(.title3)
-                    Text("\(baseURL.trimURL())")
-                        .font(.title)
-                        .fontWeight(.medium)
-                }
-                .frame(maxWidth: .infinity, alignment: .center)
+            Text("\(dateString)")
+                .font(.title)
+                .fontWeight(.medium)
+                .padding(.vertical)
+            switch transaction {
+                case .withdrawal(let withdrawalTransaction):
+                    threeAmounts(common: common, topTitle: "Chosen amount to 
withdraw:", bottomTitle: "Obtained coins:", incoming: true)
+                case .payment(let paymentTransaction):
+                    threeAmounts(common: common, topTitle: "Sum to be paid:", 
bottomTitle: "Paid coins:", incoming: false)
+                case .refund(let refundTransaction):
+                    threeAmounts(common: common, topTitle: "Refunded amount:", 
bottomTitle: "Obtained coins:", incoming: true)
+                case .tip(let tipTransaction):
+                    threeAmounts(common: common, topTitle: "Tip to be paid:", 
bottomTitle: "Paid coins:", incoming: false)
+                case .refresh(let refreshTransaction):
+                    threeAmounts(common: common, topTitle: "Refreshed 
amount:", bottomTitle: "Paid coins:", incoming: false)
             }
+
+//            if let baseURL = transaction.exchangeBaseUrl {
+//                VStack {
+//                    Text("From exchange:")
+//                        .font(.title3)
+//                    Text("\(baseURL.trimURL())")
+//                        .font(.title)
+//                        .fontWeight(.medium)
+//                }
+//                .frame(maxWidth: .infinity, alignment: .center)
+//            }
             Spacer()
             Button(role: .destructive, action: {
                 // TODO: delete from wallet-core
-                print("Should delete \(transaction.transactionId)")
+                print("Should delete \(common.transactionId)")
             }, label: {
                 HStack {
                     Text("Delete from list" + "        ")
@@ -69,11 +76,47 @@ struct TransactionDetail: View {
     }
 }
 
+extension TransactionDetail {
+    struct threeAmounts: View {
+        var common: TransactionCommon
+        var topTitle: String
+        var bottomTitle: String
+        var incoming: Bool
+
+        var body: some View {
+            let raw = common.amountRaw
+            let effective = common.amountEffective
+            let fee = common.fee()
+            let labelColor = Color(UIColor.label)
+            let outColor = Color("Outgoing")
+            let inColor = Color("Incoming")
+
+            AmountView(title: topTitle,
+                       value:  raw.readableDescription, color: labelColor)
+            .padding(.bottom)
+            AmountView(title: "Exchange fee:",
+                       value: fee.readableDescription, color: fee.isZero ? 
labelColor : outColor)
+            .padding(.bottom)
+            AmountView(title: bottomTitle,
+                       value: effective.readableDescription, color: incoming ? 
inColor : outColor)
+            .padding(.bottom)
+        }
+    }
+}
+
 #if DEBUG
 struct TransactionDetail_Previews: PreviewProvider {
-    static var transaction = Transaction(id:"some transActionID", time: 
Timestamp(from: 1_666_000_000_000))
+    static var withdrawal = Transaction(incoming: true,
+                                        id: "some withdrawal ID",
+                                        time: Timestamp(from: 
1_666_000_000_000))
+    static var payment = Transaction(incoming: false,
+                                     id: "some payment ID",
+                                     time: Timestamp(from: 1_666_666_000_000))
     static var previews: some View {
-        TransactionDetail(transaction: transaction)
+        Group {
+            TransactionDetail(transaction: withdrawal)
+            TransactionDetail(transaction: payment)
+        }
     }
 }
 #endif
diff --git a/TalerWallet1/Views/Transactions/TransactionRow.swift 
b/TalerWallet1/Views/Transactions/TransactionRow.swift
index 63df274..899aaf4 100644
--- a/TalerWallet1/Views/Transactions/TransactionRow.swift
+++ b/TalerWallet1/Views/Transactions/TransactionRow.swift
@@ -36,13 +36,16 @@ struct TransactionRow: View {
     var transaction : Transaction
 
     var body: some View {
-        let amount = transaction.amountEffective
-        let withdraw: Bool = transaction.type == "withdrawal"
-        let payment: Bool = transaction.type == "payment"
-        let refund: Bool = transaction.type == "refund"
+        let common = transaction.common()
+        let details = transaction.detailsToShow()
+        let keys = details.keys
+        let amount = common.amountEffective
+        let withdraw: Bool = common.type == WITHDRAWAL
+        let payment: Bool = common.type == PAYMENT
+        let refund: Bool = common.type == REFUND
         let incoming = withdraw || refund
-        let counterparty = transaction.exchangeBaseUrl
-        let dateString = TalerDater.dateString(from: transaction.timestamp, 
relative: true)
+//        let counterparty = transaction.exchangeBaseUrl
+        let dateString = TalerDater.dateString(from: common.timestamp, 
relative: true)
 
         HStack {
             Image(systemName: incoming ? "text.badge.plus" : 
"text.badge.minus")
@@ -50,8 +53,8 @@ struct TransactionRow: View {
                 .padding(.trailing)
                 .font(.largeTitle)
 
-            if withdraw {
-                if let baseURL = counterparty {
+            if keys.contains(EXCHANGEBASEURL) {
+                if let baseURL = details[EXCHANGEBASEURL] {
                     TransactionRowCenter(centerTop: baseURL.trimURL(), 
centerBottom: dateString)
                 }
             } else if payment {
@@ -73,9 +76,17 @@ struct TransactionRow: View {
 
 #if DEBUG
 struct TransactionRow_Previews: PreviewProvider {
-    static var transaction = Transaction(id:"some transActionID", time: 
Timestamp(from: 1_666_000_000_000))
+    static var withdrawal = Transaction(incoming: true,
+                                              id: "some withdrawal ID",
+                                            time: Timestamp(from: 
1_666_000_000_000))
+    static var payment = Transaction(incoming: false,
+                                        id: "some payment ID",
+                                        time: Timestamp(from: 
1_666_666_000_000))
     static var previews: some View {
-        TransactionRow(transaction: transaction)
+        VStack {
+            TransactionRow(transaction: withdrawal)
+            TransactionRow(transaction: payment)
+        }
     }
 }
 #endif
diff --git a/TalerWallet1/Views/Transactions/TransactionsListView.swift 
b/TalerWallet1/Views/Transactions/TransactionsListView.swift
index 8f9a6a3..ef365e1 100644
--- a/TalerWallet1/Views/Transactions/TransactionsListView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionsListView.swift
@@ -53,15 +53,16 @@ extension TransactionsListView {
 
         var body: some View {
             let transactions = viewModel.transactions!
-                List(transactions, id: \.transactionId) { transaction in
-                    NavigationLink {
-                        TransactionDetail(transaction: transaction)
-                    } label: {
-                        TransactionRow(transaction: transaction)
-                    }
+            List(transactions) { transaction in
+                let common = transaction.common()
+                NavigationLink {
+                    TransactionDetail(transaction: transaction)
+                } label: {
+                    TransactionRow(transaction: transaction)
+                }
                     .swipeActions(edge: .leading, allowsFullSwipe: true) {
                         Button {
-                            symLog?.log("bookmarked 
\(transaction.transactionId)")
+                            symLog?.log("bookmarked \(common.transactionId)")
                             // TODO: Bookmark
                         } label: {
                             Label("Bookmark", systemImage: "bookmark")
@@ -69,18 +70,23 @@ extension TransactionsListView {
                     }
                     .swipeActions(edge: .trailing, allowsFullSwipe: true) {
                         Button(role: .destructive) {
-                            symLog?.log("deleted \(transaction.transactionId)")
+                            symLog?.log("deleted \(common.transactionId)")
                             // TODO: delete from Model. SwiftUI deletes this 
row from view already :-)
                         } label: {
                             Label("Delete", systemImage: "trash")
                         }
                     }
-                }
-                    .navigationBarTitleDisplayMode(.large)      // .inline
-                    .refreshable {
+            }
+                .navigationBarTitleDisplayMode(.large)      // .inline
+                .refreshable {
+                    do {
                         symLog?.log("refreshing")
-                        try? await reloadAction()       // TODO: catch error
+                        try await reloadAction()
+                    } catch {
+                        // TODO: error
+                        symLog?.log(error.localizedDescription)
                     }
+                }
         }
     }
 }
diff --git a/TalerWallet1/Views/Transactions/TransactionsModel.swift 
b/TalerWallet1/Views/Transactions/TransactionsModel.swift
index 9786c1e..253a557 100644
--- a/TalerWallet1/Views/Transactions/TransactionsModel.swift
+++ b/TalerWallet1/Views/Transactions/TransactionsModel.swift
@@ -22,14 +22,6 @@ fileprivate let ASYNCDELAY: UInt = 0   //set e.g to 6 or 9 
seconds for debugging
 class TransactionsModel: WalletModel {
     @Published var transactions: [Transaction]?             // update view
 }
-//extension Transaction {
-//    func exchangeBaseUrl() -> String {
-//        switch detail {
-//            case .withdrawal(let transactionWithdrawal):
-//                return transactionWithdrawal.exchangeBaseUrl
-//        }
-//    }
-//}
 
 // MARK: -
 /// A request to get the transactions in the wallet's history.
diff --git a/TalerWallet1/Views/Withdraw/WithdrawAcceptView.swift 
b/TalerWallet1/Views/Withdraw/WithdrawAcceptView.swift
index becc6dd..4f9578c 100644
--- a/TalerWallet1/Views/Withdraw/WithdrawAcceptView.swift
+++ b/TalerWallet1/Views/Withdraw/WithdrawAcceptView.swift
@@ -37,7 +37,7 @@ struct WithdrawAcceptView: View {
                 case .receivedAmountDetails, .receivedTOS, .receivedTOSAck:
                     let raw = detailsForAmount.amountRaw
                     let effective = detailsForAmount.amountEffective
-                    let fee = try! raw - effective
+                    let fee = try! Amount.diff(raw, effective)      // TODO: 
different currencies
                     Form {
                         AmountView(title: "Chosen amount to withdraw:",
                                    value: raw.readableDescription, color: 
Color(UIColor.label))
diff --git a/TalerWallet1/Views/Withdraw/WithdrawURIView.swift 
b/TalerWallet1/Views/Withdraw/WithdrawURIView.swift
index 939c24d..e928e44 100644
--- a/TalerWallet1/Views/Withdraw/WithdrawURIView.swift
+++ b/TalerWallet1/Views/Withdraw/WithdrawURIView.swift
@@ -66,7 +66,7 @@ struct WithdrawURIView: View {
                 symLog.log(".task")
                 detailsForUri = try await 
viewModel.loadWithdrawalDetailsForURI(url.absoluteString)
                 let baseURL = detailsForUri!.defaultExchangeBaseUrl
-                symLog.log("amount: \(detailsForUri!.amount), baseURL: 
\(baseURL)")
+                symLog.log("amount: \(detailsForUri!.amount), baseURL: 
\(String(describing: baseURL))")
                 // TODO: let user choose exchange from array
                 detailsForAmount = try await 
viewModel.loadWithdrawalDetailsForAmount(detailsForUri!)
                 symLog.log("raw: \(detailsForAmount!.amountRaw), effective: 
\(detailsForAmount!.amountEffective)")

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]