gnunet-svn
[Top][All Lists]
Advanced

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

[taler-taler-ios] branch master updated (be6a335 -> 989c5ea)


From: gnunet
Subject: [taler-taler-ios] branch master updated (be6a335 -> 989c5ea)
Date: Tue, 05 Dec 2023 16:25:56 +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 be6a335  Bump version to 0.9.3 (28)
     new 95c1c61  Logging
     new 5c3654d  New "flags"
     new ecb2d78  Fix KYC/Confirm
     new 5cc946b  ViewThatFits
     new 6482d33  Refund
     new ced4d03  Exchange: ToS
     new 0ab51f8  Only main title
     new 07efeed  talerURI only sent if valid
     new cfd6c03  cleanup
     new ce739ae  increasedContrast
     new d0d5a75  Payment: show what to pay for
     new f00e31a  Preparation for withdraw-exchange
     new 957c7ee  Exchange icon
     new 6c89fb8  Bump version to 0.9.3 (29)
     new b08eb3a  bugfix for inputDigits=0
     new 820d8b3  cleanup
     new a881bb8  currency & -Info
     new 6af1cf7  cleanup, en/disable button
     new 7516be0  use #0042b3
     new e1bf44f  Bump version to 0.9.3 (30)
     new dee6404  amountToTransfer
     new fa2041e  cleanup
     new 390fce1  shortcutAction
     new 989c5ea  Debugging

The 24 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:
 TalerWallet.xcodeproj/project.pbxproj              |  24 +++-
 .../taler-logo-2023-blue.svg                       |   2 +-
 TalerWallet1/Backend/WalletCore.swift              |   6 +-
 TalerWallet1/Controllers/Controller.swift          |  18 ++-
 TalerWallet1/Controllers/DebugViewC.swift          |  24 ++--
 TalerWallet1/Model/Model+Balances.swift            |  31 +++--
 TalerWallet1/Model/Model+P2P.swift                 |   2 +-
 TalerWallet1/Model/Model+Refund.swift              |  54 +++++++++
 TalerWallet1/Model/Model+Withdraw.swift            |  21 +++-
 TalerWallet1/Model/Transaction.swift               |   5 +-
 TalerWallet1/Views/Balances/BalanceRowView.swift   |  60 +++++----
 TalerWallet1/Views/Balances/BalancesListView.swift |   2 +-
 .../Views/Balances/BalancesSectionView.swift       |  69 ++++-------
 TalerWallet1/Views/Balances/TwoRowButtons.swift    |  53 ++++----
 TalerWallet1/Views/Exchange/ExchangeRowView.swift  | 134 +++++++++++----------
 .../Views/Exchange/ExchangeSectionView.swift       |  14 +--
 TalerWallet1/Views/Exchange/ManualWithdraw.swift   | 132 +++++++++++---------
 TalerWallet1/Views/HelperViews/AmountRowV.swift    |   2 +-
 .../Views/HelperViews/CurrencyInputView.swift      |  16 ++-
 TalerWallet1/Views/Main/MainView.swift             |   4 +-
 TalerWallet1/Views/Peer2peer/P2PReadyV.swift       |  18 +--
 TalerWallet1/Views/Peer2peer/P2PSubjectV.swift     |  67 ++++++++---
 TalerWallet1/Views/Peer2peer/RequestPayment.swift  |  65 +++++-----
 TalerWallet1/Views/Peer2peer/SendAmount.swift      |  69 ++++++-----
 .../Views/Sheets/Payment/PayTemplateView.swift     |   2 +-
 .../Views/Sheets/Payment/PaymentView.swift         |   3 +
 .../Views/Sheets/Refund/RefundURIView.swift        |  60 +++++++++
 TalerWallet1/Views/Sheets/URLSheet.swift           |   6 +
 .../WithdrawBankIntegrated/WithdrawTOSView.swift   |  18 ++-
 .../Views/Transactions/TransactionDetailView.swift |  96 +++++++++------
 .../Views/Transactions/TransactionRowView.swift    |  19 ++-
 TestFlight/WhatToTest.en-US.txt                    |  23 +++-
 taler-swift/Sources/taler-swift/Amount.swift       |  58 +++++----
 33 files changed, 736 insertions(+), 441 deletions(-)
 create mode 100644 TalerWallet1/Model/Model+Refund.swift
 create mode 100644 TalerWallet1/Views/Sheets/Refund/RefundURIView.swift

diff --git a/TalerWallet.xcodeproj/project.pbxproj 
b/TalerWallet.xcodeproj/project.pbxproj
index 5df7760..2b59bc8 100644
--- a/TalerWallet.xcodeproj/project.pbxproj
+++ b/TalerWallet.xcodeproj/project.pbxproj
@@ -246,6 +246,10 @@
                4EFA39612AA7946B00742548 /* ToSButtonView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EFA395F2AA7946B00742548 /* ToSButtonView.swift 
*/; };
                ABC13AA32859962800D23185 /* taler-swift in Frameworks */ = {isa 
= PBXBuildFile; productRef = ABC13AA22859962800D23185 /* taler-swift */; };
                ABE97B1D286D82BF00580772 /* AnyCodable in Frameworks */ = {isa 
= PBXBuildFile; productRef = ABE97B1C286D82BF00580772 /* AnyCodable */; };
+               E37AA62A2AF197E5003850CF /* Model+Refund.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = E37AA6292AF197E5003850CF /* Model+Refund.swift 
*/; };
+               E37AA62B2AF197E5003850CF /* Model+Refund.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = E37AA6292AF197E5003850CF /* Model+Refund.swift 
*/; };
+               E37AA62E2AF19BE0003850CF /* RefundURIView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = E37AA62D2AF19BE0003850CF /* RefundURIView.swift 
*/; };
+               E37AA62F2AF19BE0003850CF /* RefundURIView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = E37AA62D2AF19BE0003850CF /* RefundURIView.swift 
*/; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -413,6 +417,8 @@
                D14AFD1D24D232B300C51073 /* Taler_Wallet.app */ = {isa = 
PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; 
path = Taler_Wallet.app; sourceTree = BUILT_PRODUCTS_DIR; };
                D14AFD3324D232B500C51073 /* TalerTests.xctest */ = {isa = 
PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path 
= TalerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
                D14AFD3E24D232B500C51073 /* TalerUITests.xctest */ = {isa = 
PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path 
= TalerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+               E37AA6292AF197E5003850CF /* Model+Refund.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
"Model+Refund.swift"; sourceTree = "<group>"; };
+               E37AA62D2AF19BE0003850CF /* RefundURIView.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
RefundURIView.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -607,6 +613,7 @@
                                4ECB627F2A0BA6DF004ABBB7 /* Model+P2P.swift */,
                                4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift 
*/,
                                4EB0952C2989CBFE0043A8A1 /* Model+Payment.swift 
*/,
+                E37AA6292AF197E5003850CF /* Model+Refund.swift */,
                                4EB095102989CBB00043A8A1 /* 
Model+Settings.swift */,
                                4EB095322989CBFE0043A8A1 /* 
Model+Transactions.swift */,
                                4EB0953D2989CBFE0043A8A1 /* 
Model+Withdraw.swift */,
@@ -765,6 +772,7 @@
                                4EB095332989CBFE0043A8A1 /* URLSheet.swift */,
                                4EB0953B2989CBFE0043A8A1 /* 
WithdrawBankIntegrated */,
                                4EB0952A2989CBFE0043A8A1 /* Payment */,
+                E37AA62C2AF19BA6003850CF /* Refund */,
                                4E3B4BBF2A41E64000CC88B8 /* P2P_Sheets */,
                        );
                        path = Sheets;
@@ -813,6 +821,14 @@
                        path = TalerUITests;
                        sourceTree = "<group>";
                };
+               E37AA62C2AF19BA6003850CF /* Refund */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E37AA62D2AF19BE0003850CF /* RefundURIView.swift 
*/,
+                       );
+                       path = Refund;
+                       sourceTree = "<group>";
+               };
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -1091,6 +1107,7 @@
                                4E3EAE5C2A990778009F1BE8 /* Model+Pending.swift 
in Sources */,
                                4E3EAE5D2A990778009F1BE8 /* 
ExchangeListView.swift in Sources */,
                                4E3EAE5E2A990778009F1BE8 /* 
WithdrawProgressView.swift in Sources */,
+                               E37AA62E2AF19BE0003850CF /* RefundURIView.swift 
in Sources */,
                                4E3EAE5F2A990778009F1BE8 /* QRSheet.swift in 
Sources */,
                                4E3EAE602A990778009F1BE8 /* 
P2pReceiveURIView.swift in Sources */,
                                4E3EAE612A990778009F1BE8 /* ListStyle.swift in 
Sources */,
@@ -1098,6 +1115,7 @@
                                4E3EAE632A990778009F1BE8 /* WalletCore.swift in 
Sources */,
                                4E3EAE642A990778009F1BE8 /* 
LaunchAnimationView.swift in Sources */,
                                4E3EAE652A990778009F1BE8 /* SideBarView.swift 
in Sources */,
+                               E37AA62A2AF197E5003850CF /* Model+Refund.swift 
in Sources */,
                                4E3EAE682A990778009F1BE8 /* WalletModel.swift 
in Sources */,
                                4E3EAE692A990778009F1BE8 /* URLSheet.swift in 
Sources */,
                                4E3EAE6A2A990778009F1BE8 /* ThreeAmountsV.swift 
in Sources */,
@@ -1196,6 +1214,7 @@
                                4EB0956E2989CBFE0043A8A1 /* Model+Pending.swift 
in Sources */,
                                4EB095522989CBFE0043A8A1 /* 
ExchangeListView.swift in Sources */,
                                4EB095642989CBFE0043A8A1 /* 
WithdrawProgressView.swift in Sources */,
+                               E37AA62F2AF19BE0003850CF /* RefundURIView.swift 
in Sources */,
                                4EEC157A29F9427F00D46A03 /* QRSheet.swift in 
Sources */,
                                4E3B4BC12A41E6C200CC88B8 /* 
P2pReceiveURIView.swift in Sources */,
                                4E6EDD872A363D8D0031D520 /* ListStyle.swift in 
Sources */,
@@ -1203,6 +1222,7 @@
                                4EB095202989CBCB0043A8A1 /* WalletCore.swift in 
Sources */,
                                4EB095672989CBFE0043A8A1 /* 
LaunchAnimationView.swift in Sources */,
                                4EB095662989CBFE0043A8A1 /* SideBarView.swift 
in Sources */,
+                               E37AA62B2AF197E5003850CF /* Model+Refund.swift 
in Sources */,
                                4EB095162989CBB00043A8A1 /* WalletModel.swift 
in Sources */,
                                4EB0955A2989CBFE0043A8A1 /* URLSheet.swift in 
Sources */,
                                4ED2F94B2A278F5100453B40 /* ThreeAmountsV.swift 
in Sources */,
@@ -1464,7 +1484,7 @@
                                CODE_SIGN_ENTITLEMENTS = 
"$(TARGET_NAME).entitlements";
                                CODE_SIGN_IDENTITY = "Apple Development";
                                CODE_SIGN_STYLE = Automatic;
-                               CURRENT_PROJECT_VERSION = 28;
+                               CURRENT_PROJECT_VERSION = 30;
                                DEVELOPMENT_TEAM = GUDDQ9428Y;
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
@@ -1506,7 +1526,7 @@
                                CODE_SIGN_ENTITLEMENTS = 
"$(TARGET_NAME).entitlements";
                                CODE_SIGN_IDENTITY = "Apple Development";
                                CODE_SIGN_STYLE = Automatic;
-                               CURRENT_PROJECT_VERSION = 28;
+                               CURRENT_PROJECT_VERSION = 30;
                                DEVELOPMENT_TEAM = GUDDQ9428Y;
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
diff --git 
a/TalerWallet1/Assets.xcassets/taler-logo-2023-blue.imageset/taler-logo-2023-blue.svg
 
b/TalerWallet1/Assets.xcassets/taler-logo-2023-blue.imageset/taler-logo-2023-blue.svg
index b6d9918..39877da 100644
--- 
a/TalerWallet1/Assets.xcassets/taler-logo-2023-blue.imageset/taler-logo-2023-blue.svg
+++ 
b/TalerWallet1/Assets.xcassets/taler-logo-2023-blue.imageset/taler-logo-2023-blue.svg
@@ -1,5 +1,5 @@
 <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg";>
-<g id="aa" style="fill:blue;fill-rule:evenodd">
+<g id="aa" style="fill:#0042b3;fill-rule:evenodd">
 <!-- 90% -->
 <path d="M57.6 43.4
 c-25.5 4.3-44.9 28-44.9 56.5 0 31.5 23.9 57.2 53.3 57.2
diff --git a/TalerWallet1/Backend/WalletCore.swift 
b/TalerWallet1/Backend/WalletCore.swift
index 8c8ebde..76ba1ab 100644
--- a/TalerWallet1/Backend/WalletCore.swift
+++ b/TalerWallet1/Backend/WalletCore.swift
@@ -153,7 +153,7 @@ extension WalletCore {
                                  userInfo: [AnyHashable: Any]? = nil) {
         Task { // runs on MainActor
             await postNotificationM(aName, object: anObject, userInfo: 
userInfo)
-            logger.log("Notification sent: \(aName.rawValue)")
+            logger.info("Notification sent: \(aName.rawValue)")
         }
     }
 
@@ -200,7 +200,7 @@ extension WalletCore {
                             postNotification(.TransactionDone, userInfo: 
[TRANSACTIONTRANSITION: decoded])
                             return
                         case .expired:
-                            logger.log("Expired: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
+                            logger.warning("Expired: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
                             Controller.shared.playSound(0)
                             postNotification(.TransactionExpired, userInfo: 
[TRANSACTIONTRANSITION: decoded])
                             return
@@ -224,7 +224,7 @@ extension WalletCore {
                             logger.log("Pending: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
                             postNotification(.TransactionStateTransition, 
userInfo: [TRANSACTIONTRANSITION: decoded])
                         default:
-                            logger.log("Yikes: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
+                            logger.warning("Unknow transition: 
\(decoded.transactionId, privacy: .private(mask: .hash))")
                             postNotification(.TransactionStateTransition, 
userInfo: [TRANSACTIONTRANSITION: decoded])
                     } // switch
                 } // type
diff --git a/TalerWallet1/Controllers/Controller.swift 
b/TalerWallet1/Controllers/Controller.swift
index 23964cc..05f5d77 100644
--- a/TalerWallet1/Controllers/Controller.swift
+++ b/TalerWallet1/Controllers/Controller.swift
@@ -20,10 +20,12 @@ enum BackendState {
 enum UrlCommand {
     case unknown
     case withdraw
+    case withdrawExchange
     case pay
     case payPull
     case payPush
     case payTemplate
+    case refund
     case reward
 }
 
@@ -157,18 +159,22 @@ extension Controller {
         }
         switch command {
             case "withdraw":
-                return UrlCommand.withdraw
+                return .withdraw
+            case "withdraw-exchange":
+                return .withdrawExchange
             case "pay":
-                return UrlCommand.pay
+                return .pay
             case "pay-pull":
-                return UrlCommand.payPull
+                return .payPull
             case "pay-push":
-                return UrlCommand.payPush
+                return .payPush
             case "pay-template":
-                return UrlCommand.payTemplate
+                return .payTemplate
+            case "refund":
+                return .refund
             default:
                 symLog.log("unknown command taler://\(command)")
         }
-        return UrlCommand.unknown
+        return .unknown
     }
 }
diff --git a/TalerWallet1/Controllers/DebugViewC.swift 
b/TalerWallet1/Controllers/DebugViewC.swift
index b135dfd..fd6551b 100644
--- a/TalerWallet1/Controllers/DebugViewC.swift
+++ b/TalerWallet1/Controllers/DebugViewC.swift
@@ -170,31 +170,29 @@ class DebugViewC: ObservableObject {
                 logger.log("\(newID, privacy: .public) stays")
                 // don't set viewID to the same value, it would just trigger 
an unneccessary redraw
             }
-        } else if viewID > 0 {
-            logger.log("switching OFF, will not use \(newID, privacy: 
.public)")
-            viewID = 0                                      // publish OFF
         } else {
-            logger.log("off, will not use \(newID, privacy: .public)")
-            // don't set viewID from 0 to 0 again, it would just trigger an 
unneccessary redraw
+            logger.log("view \(newID, privacy: .public)")
+            if viewID > 0 {
+                viewID = 0                                      // publish OFF
+            }
         }
     }
     
     @MainActor func setSheetID(_ newID: Int) -> Void {
         if developerMode {
             if sheetID != newID {
-                logger.log("switching from \(self.sheetID, privacy: .public) 
to \(newID, privacy: .public) for sheet")
+                logger.log("sheet switching from \(self.sheetID, privacy: 
.public) to \(newID, privacy: .public)")
                 sheetID = newID                             // publish new 
sheetID
             } else {
-                logger.log("\(newID, privacy: .public) stays for sheet")
+                logger.log("sheet \(newID, privacy: .public) stays")
                 // don't set sheetID to the same value, it would just trigger 
an unneccessary redraw
             }
-        } else if sheetID > 0 {
-            // might happen after switching DevMode off, if sheetID still has 
the old value of the last sheet
-            logger.log("switching OFF, will not use \(newID, privacy: .public) 
for sheet")
-            sheetID = 0                                     // publish OFF
         } else {
-            logger.log("off, will not use \(newID, privacy: .public) for 
sheet")
-            // don't set sheetID from 0 to 0 again, it would just trigger an 
unneccessary redraw
+            logger.log("sheet \(newID, privacy: .public)")
+            if sheetID > 0 {
+                // might happen after switching DevMode off, if sheetID still 
has the old value of the last sheet
+                sheetID = 0                                     // publish OFF
+            }
         }
     }
 }
diff --git a/TalerWallet1/Model/Model+Balances.swift 
b/TalerWallet1/Model/Model+Balances.swift
index 1bab4ca..6d4be7f 100644
--- a/TalerWallet1/Model/Model+Balances.swift
+++ b/TalerWallet1/Model/Model+Balances.swift
@@ -7,25 +7,36 @@ import taler_swift
 fileprivate let ASYNCDELAY: UInt = 0   //set e.g to 6 or 9 seconds for 
debugging
 
 // MARK: -
+
+enum BalanceFlag: String, Codable {
+    case incomingAml = "incoming-aml"
+    case incomingConfirmation = "incoming-confirmation"
+    case incomingKyc = "incoming-kyc"
+    case outgoingKyc = "outgoing-kyc"
+}
+
 /// A currency balance
 struct Balance: Decodable, Hashable, Sendable {
-    var available: Amount
     var scopeInfo: ScopeInfo
-    var requiresUserInput: Bool
-    var hasPendingTransactions: Bool
+    var available: Amount
+    var pendingIncoming: Amount
+    var pendingOutgoing: Amount
+    var flags: [BalanceFlag]
 
     public static func == (lhs: Balance, rhs: Balance) -> Bool {
-        return lhs.available == rhs.available &&
-        lhs.scopeInfo == rhs.scopeInfo &&
-        lhs.requiresUserInput == rhs.requiresUserInput &&
-        lhs.hasPendingTransactions == rhs.hasPendingTransactions
+        lhs.scopeInfo == rhs.scopeInfo
+        && lhs.available == rhs.available
+        && lhs.pendingIncoming == rhs.pendingIncoming
+        && lhs.pendingOutgoing == rhs.pendingOutgoing
+        && lhs.flags == rhs.flags
     }
 
     public func hash(into hasher: inout Hasher) {
-        hasher.combine(available)
         hasher.combine(scopeInfo)
-        hasher.combine(requiresUserInput)
-        hasher.combine(hasPendingTransactions)
+        hasher.combine(available)
+        hasher.combine(pendingIncoming)
+        hasher.combine(pendingOutgoing)
+        hasher.combine(flags)
     }
 }
 // MARK: -
diff --git a/TalerWallet1/Model/Model+P2P.swift 
b/TalerWallet1/Model/Model+P2P.swift
index 82e5bc4..8a0d003 100644
--- a/TalerWallet1/Model/Model+P2P.swift
+++ b/TalerWallet1/Model/Model+P2P.swift
@@ -72,7 +72,7 @@ struct InitiatePeerPushDebitResponse: Codable {
     let mergePriv: String
     let pursePub: String
     let exchangeBaseUrl: String
-    let talerUri: String
+    let talerUri: String?
     let transactionId: String
 }
 fileprivate struct InitiatePeerPushDebit: WalletBackendFormattedRequest {
diff --git a/TalerWallet1/Model/Model+Refund.swift 
b/TalerWallet1/Model/Model+Refund.swift
new file mode 100644
index 0000000..4892c61
--- /dev/null
+++ b/TalerWallet1/Model/Model+Refund.swift
@@ -0,0 +1,54 @@
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
+import Foundation
+fileprivate let ASYNCDELAY: UInt = 0   //set e.g to 6 or 9 seconds for 
debugging
+
+// MARK: -
+/// A request to prepare a refund with an obtained URI
+struct StartRefundURIRequest: WalletBackendFormattedRequest {
+    func operation() -> String { "startRefundQueryForUri" }
+    func args() -> Args { Args(talerRefundUri: talerRefundUri) }
+
+    var talerRefundUri: String
+
+    struct Args: Encodable {
+        var talerRefundUri: String
+    }
+
+    /// returns the txID of the new refund
+    struct Response: Decodable {
+        var transactionId: String
+    }
+}
+
+/// A request to prepare a refund with a transactionID
+struct StartRefundQueryRequest: WalletBackendFormattedRequest {
+    func operation() -> String { "startRefundQuery" }
+    func args() -> Args { Args(transactionId: transactionId) }
+
+    var transactionId: String
+
+    struct Args: Encodable {
+        var transactionId: String
+    }
+
+    /// no error means the refunds was successful
+    struct Response: Decodable {}
+}
+// MARK: -
+extension WalletModel {
+    @MainActor
+    func startRefundForUriM(url: String) async throws -> String {
+        let request = StartRefundURIRequest(talerRefundUri: url)
+        let response = try await sendRequest(request, ASYNCDELAY)
+        return response.transactionId
+    }
+
+    @MainActor
+    func startRefundM(transactionId: String) async throws {
+        let request = StartRefundQueryRequest(transactionId: transactionId)
+        let _ = try await sendRequest(request, ASYNCDELAY)
+    }
+}
diff --git a/TalerWallet1/Model/Model+Withdraw.swift 
b/TalerWallet1/Model/Model+Withdraw.swift
index e86f394..8563df4 100644
--- a/TalerWallet1/Model/Model+Withdraw.swift
+++ b/TalerWallet1/Model/Model+Withdraw.swift
@@ -7,6 +7,21 @@ import taler_swift
 import SymLog
 fileprivate let ASYNCDELAY: UInt = 0   //set e.g to 6 or 9 seconds for 
debugging
 
+enum AccountRestrictionType: String, Decodable {
+    case deny
+    case regex
+}
+struct AccountRestriction: Decodable {
+    var type: AccountRestrictionType
+    var payto_regex: String?
+    var human_hint: String?
+    var human_hint_i18n: String?
+}
+struct WithdrawalExchangeAccountDetails: Decodable {
+    var paytoUri: String
+    var transferAmount: Amount
+    var creditRestrictions: [AccountRestriction]?
+}
 // MARK: -
 /// The result from getWithdrawalDetailsForUri
 struct WithdrawUriInfoResponse: Decodable {
@@ -31,9 +46,10 @@ struct WithdrawalAmountDetails: Decodable {
     var tosAccepted: Bool               // Did the user accept the current 
version of the exchange's terms of service?
     var amountRaw: Amount               // Amount that the user will transfer 
to the exchange
     var amountEffective: Amount         // Amount that will be added to the 
user's wallet balance
-    var paytoUris: [String]             // Ways to pay the exchange
-    var ageRestrictionOptions: [Int]?   // Array of ages
     var numCoins: Int?                  // Number of coins this 
amountEffective will create
+    var withdrawalAccountsList: [WithdrawalExchangeAccountDetails]?
+    var ageRestrictionOptions: [Int]?   // Array of ages
+    var scopeInfo: ScopeInfo
 }
 /// A request to get an exchange's withdrawal details.
 fileprivate struct GetWithdrawalDetailsForAmount: 
WalletBackendFormattedRequest {
@@ -117,6 +133,7 @@ fileprivate struct AcceptBankIntegratedWithdrawal: 
WalletBackendFormattedRequest
 struct AcceptManualWithdrawalResult: Decodable {
     var reservePub: String
     var exchangePaytoUris: [String]
+    var withdrawalAccountsList: [WithdrawalExchangeAccountDetails]
     var transactionId: String
 }
 /// A request to accept a manual withdrawl.
diff --git a/TalerWallet1/Model/Transaction.swift 
b/TalerWallet1/Model/Transaction.swift
index a834698..75c88a3 100644
--- a/TalerWallet1/Model/Transaction.swift
+++ b/TalerWallet1/Model/Transaction.swift
@@ -266,13 +266,14 @@ struct WithdrawalTransaction : Sendable{
 }
 
 struct PaymentTransactionDetails: Decodable {
+    var info: OrderShortInfo
     var proposalId: String
     var totalRefundRaw: Amount
     var totalRefundEffective: Amount
     var refundPending: Amount?
+    var refunds: [String]?           // array of refund txIDs for this payment
     var refundQueryActive: Bool?
-    var refunds: [String]?           // TODO: array type?
-    var info: OrderShortInfo
+    var posConfirmation: String?
 }
 
 struct PaymentTransaction : Sendable{
diff --git a/TalerWallet1/Views/Balances/BalanceRowView.swift 
b/TalerWallet1/Views/Balances/BalanceRowView.swift
index 962f2fa..37222f0 100644
--- a/TalerWallet1/Views/Balances/BalanceRowView.swift
+++ b/TalerWallet1/Views/Balances/BalanceRowView.swift
@@ -30,7 +30,6 @@ struct BalanceButton: View {
                         Text(title)
                             .accessibilityFont(.title2)
                             .foregroundColor(colorSchemeContrast == .increased 
? .primary : .secondary)
-//                            .foregroundColor(moreContrast ? .primary : 
.secondary)
                     }
                 }
             }
@@ -59,48 +58,45 @@ struct BalanceRowView: View {
 
     let sendTitle0 = String(localized: "SendButton_Short", defaultValue: 
"Send",
                               comment: "Abbreviation of button `Send Money´")
-    let sendTitle1 = String(localized: "SendButton_Top", defaultValue: "Send",
-                              comment: "Top (first half) of button `Send 
Money´")
-    let sendTitle2 = String(localized: "SendButton_Bottom", defaultValue: 
"Money",
-                              comment: "Bottom (second half) of button `Send 
Money´")
+    let sendTitle1 = String(localized: "SendButton_Full", defaultValue: 
"Send\tMoney",
+                              comment: "`Send Money´ in Balances - set exactly 
1 \\t for line break")
+
     let requestTitle0 = String(localized: "RequestButton_Short", defaultValue: 
"Request",
                                  comment: "Abbreviation of button `Request 
Payment´")
-    let requestTitle1 = String(localized: "RequestButton_Top", defaultValue: 
"Request",
-                                 comment: "Top (first half) of button `Request 
Payment´")
-    let requestTitle2 = String(localized: "RequestButton_Bottom", 
defaultValue: "Payment",
-                                 comment: "Bottom (second half) of button 
`Request Payment´")
-
+    let requestTitle1 = String(localized: "RequestButton_Full", defaultValue: 
"Request\tPayment",
+                                 comment: "`Request Payment´ in Balances - set 
exactly 1 \\t for line break")
     var body: some View {
-        SingleAxisGeometryReader { width in
-            VStack (alignment: .trailing) {
-                let currencyInfo = controller.info(for: amount.currencyStr, 
controller.currencyTicker)
-                let amountStr = amount.string(currencyInfo)
-                BalanceButton(amountStr: amountStr,
-                           sizeCategory: sizeCategory,
-                              rowAction: rowAction)
-                let uiFont = TalerFont.uiFont(.title3)
-                let titles = iconOnly ? [(sendTitle0, uiFont), (requestTitle0, 
uiFont)]
-                           : [(sendTitle1, uiFont), (sendTitle2, uiFont), 
(requestTitle1, uiFont), (requestTitle2, uiFont)]
-                let fitsSideBySide = Self.fitsSideBySide(titles, 
availableWidth: width, sizeCategory: sizeCategory, padding: 10)
-                let twoRowButtons = TwoRowButtons(sendTitles: iconOnly ? 
(sendTitle0, nil) : (sendTitle1, sendTitle2),
-                                                  recvTitles: iconOnly ? 
(requestTitle0, nil) : (requestTitle1, requestTitle2),
-                                              fitsSideBySide: fitsSideBySide,
-                                                   lineLimit: 5,
-                                                sendDisabled: amount.isZero,
-                                                  sendAction: sendAction,
-                                                  recvAction: recvAction)
-                if fitsSideBySide {
-                    HStack(spacing: HSPACING) { twoRowButtons }
-                } else {
+        VStack (alignment: .trailing) {
+            let currencyInfo = controller.info(for: amount.currencyStr, 
controller.currencyTicker)
+            let amountStr = amount.string(currencyInfo)
+            BalanceButton(amountStr: amountStr,
+                       sizeCategory: sizeCategory,
+                          rowAction: rowAction)
+
+            let sendTitle = iconOnly ? sendTitle0 : sendTitle1
+            let requTitle = iconOnly ? requestTitle0 : requestTitle1
+            let twoRowButtons = TwoRowButtons(sendTitle: sendTitle,
+                                              recvTitle: requTitle,
+                                         fitsSideBySide: false,
+                                              lineLimit: 5,
+                                           sendDisabled: amount.isZero,
+                                             sendAction: sendAction,
+                                             recvAction: recvAction)
+            if #available(iOS 16.0, *) {
+                ViewThatFits(in: .horizontal) {
+                    HStack(spacing: HSPACING) {
+                        twoRowButtons.makeCopy(fitsSideBySide: true)
+                    }
                     VStack { twoRowButtons }
                 }
+            } else { // view for iOS 15
+                VStack { twoRowButtons }
             }
         }
     }
 }
 // MARK: -
 #if  DEBUG
-
 struct BalanceRowView_Previews: PreviewProvider {
     @MainActor
   struct StateContainer: View {
diff --git a/TalerWallet1/Views/Balances/BalancesListView.swift 
b/TalerWallet1/Views/Balances/BalancesListView.swift
index f7ee719..b425948 100644
--- a/TalerWallet1/Views/Balances/BalancesListView.swift
+++ b/TalerWallet1/Views/Balances/BalancesListView.swift
@@ -22,7 +22,7 @@ struct BalancesListView: View {
     @EnvironmentObject private var model: WalletModel
 
     @State private var lastReloadedBalances = 0
-    @State private var amountToTransfer = Amount.zero(currency: "")            
 // Update currency when used
+    @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING)   
 // Update currency when used
     @State private var summary: String = ""
     @State private var showQRScanner: Bool = false
     @State private var showCameraAlert: Bool = false
diff --git a/TalerWallet1/Views/Balances/BalancesSectionView.swift 
b/TalerWallet1/Views/Balances/BalancesSectionView.swift
index 5b07055..f6306d1 100644
--- a/TalerWallet1/Views/Balances/BalancesSectionView.swift
+++ b/TalerWallet1/Views/Balances/BalancesSectionView.swift
@@ -78,7 +78,7 @@ extension BalancesSectionView: View {
             if pendingTransactions.count > 0 {
                 BalancesPendingRowView(symLog: symLog,
                                         stack: stack.push(),
-                                     currency: currency,
+                                      balance: balance,
                           pendingTransactions: $pendingTransactions,
                                 reloadPending: reloadPending,
                               reloadOneAction: reloadOneAction)
@@ -113,7 +113,7 @@ extension BalancesSectionView: View {
                 if !iconOnly {
                     Text("Recent transactions")
                         .accessibilityFont(.callout)
-//                        .foregroundColor(moreContrast ? .primary : 
.secondary)
+//                        .foregroundColor(colorSchemeContrast == .increased ? 
.primary : .secondary)
                 }
             }
         } // recent transactions
@@ -123,62 +123,46 @@ extension BalancesSectionView: View {
 fileprivate struct BalancesPendingRowView: View {
     let symLog: SymLogV?
     let stack: CallStack
-    let currency: String // = currencyInfo.scope.currency
+//    let currency: String // = currencyInfo.scope.currency
+    let balance: Balance                            // this is the currency to 
be used
     @Binding var pendingTransactions: [Transaction]
     let reloadPending: (_ stack: CallStack) async -> ()
     let reloadOneAction: ((_ transactionId: String) async throws -> 
Transaction)
 
-    func computePending(currency: String) -> (Amount, Amount) {
-        var incoming = Amount(currency: currency, cent: 0)
-        var outgoing = Amount(currency: currency, cent: 0)
-        for transaction in pendingTransactions {
-            let effective = transaction.common.amountEffective
-            if currency == effective.currencyStr {
-                do {
-                    if transaction.common.incoming() {
-                        incoming = try incoming + effective
-                    } else {
-                        outgoing = try outgoing + effective
-                    }
-                } catch {
-                    // TODO: log error
-                    symLog?.log(error.localizedDescription)
-                }
-            }
-        }
-        return (incoming, outgoing)
-    }
-
     var body: some View {
-        let (pendingIncoming, pendingOutgoing) = computePending(currency: 
currency)
+        let pendingIncoming = balance.pendingIncoming
+        let pendingOutgoing = balance.pendingOutgoing
+        let needsKYCin = balance.flags.contains(.incomingKyc)
+        let needsKYCout = balance.flags.contains(.outgoingKyc)
+        let shouldConfirm = balance.flags.contains(.incomingConfirmation)
 
         NavigationLink {
             //let _ = print("button: Pending Transactions: \(currency)")
             LazyView {
                 TransactionsListView(stack: stack.push(),
                                   navTitle: String(localized: "Pending", 
comment: "ViewTitle of TransactionList"),
-                                  currency: currency,
+                                  currency: balance.scopeInfo.currency,
                               transactions: pendingTransactions,
                                 showUpDown: false,
                            reloadAllAction: reloadPending,
                            reloadOneAction: reloadOneAction)
             }
         } label: {
-            let needsKYC = true
-            let shouldConfirm = false
+            let needsKYC =  needsKYCin || needsKYCout
             let needsKYCStr = String(localized: ". Needs K Y C", comment: 
"VoiceOver")
             let needsConfStr = String(localized: ". Needs bank confirmation", 
comment: "VoiceOver")
             VStack(spacing: 6) {
-                var rows = 0
-                if !pendingIncoming.isZero {
-                    PendingRowView(amount: pendingIncoming, incoming: true, 
shouldConfirm: false, needsKYC: false)     // TODO: !!!
-                    let _ = (rows+=1)
+                let hasIncoming = !pendingIncoming.isZero
+                if hasIncoming {
+                    PendingRowView(amount: pendingIncoming, incoming: true,
+                            shouldConfirm: shouldConfirm, needsKYC: needsKYCin)
                 }
-                if !pendingOutgoing.isZero {
-                    PendingRowView(amount: pendingOutgoing, incoming: false, 
shouldConfirm: false, needsKYC: false)
-                    let _ = (rows+=1)
+                let hasOutgoing = !pendingOutgoing.isZero
+                if hasOutgoing {
+                    PendingRowView(amount: pendingOutgoing, incoming: false,
+                            shouldConfirm: false, needsKYC: needsKYCout)
                 }
-                if rows == 0 {
+                if !hasIncoming && !hasOutgoing {           // should never 
happen
                     Text("Some pending transactions")
                         .accessibilityFont(.body)
                 }
@@ -189,9 +173,7 @@ fileprivate struct BalancesPendingRowView: View {
                                             shouldConfirm ? needsConfStr
                                                           : EMPTYSTRING)
             .accessibilityHint("Will go to Pending transactions.")
-
-        }
-
+        } // NavLinkLabel
     } // body
 } // BalancesPendingRowView
 
@@ -211,7 +193,7 @@ fileprivate struct BalancesNavigationLinksView: View {
 
     func selectAndUpdate(_ button: Int) {
         let currency = balance.scopeInfo.currency
-        amountToTransfer.setCurrency(currency)
+        amountToTransfer.setCurrency(currency)      // replace wrong currency 
here
         symLog?.log("balance.scopeInfo.currency: \(currency)")
 
         buttonSelected = button      // will trigger NavigationLink
@@ -274,10 +256,9 @@ fileprivate struct BindingViewContainer: View {
 
     var body: some View {
         let scopeInfo = ScopeInfo(type: ScopeInfo.ScopeInfoType.exchange, url: 
DEMOEXCHANGE, currency: LONGCURRENCY)
-        let balance = Balance(available: Amount(currency: LONGCURRENCY, 
cent:1),
-                              scopeInfo: scopeInfo,
-                      requiresUserInput: false,
-                 hasPendingTransactions: true)
+        let balance = Balance(scopeInfo: scopeInfo,
+                              available: Amount(currency: LONGCURRENCY, 
cent:1),
+                              hasPendingTransactions: true)
         BalancesSectionView(balance: balance,
                        sectionCount: 2,
                    amountToTransfer: $amountToTransfer,
diff --git a/TalerWallet1/Views/Balances/TwoRowButtons.swift 
b/TalerWallet1/Views/Balances/TwoRowButtons.swift
index 20d87c8..f057936 100644
--- a/TalerWallet1/Views/Balances/TwoRowButtons.swift
+++ b/TalerWallet1/Views/Balances/TwoRowButtons.swift
@@ -5,42 +5,41 @@
 import SwiftUI
 import taler_swift
 
-extension View {
-    func accessTitles(_ titles: (String, String?), _ fitsSideBySide: Bool) -> 
(String, String) {
-        let space = " "
-        let delimiter = fitsSideBySide ? "\n" : space
-        let (title1, title2) = titles
-        if let title2 {
-            return (title1 + delimiter + title2, title1 + space + title2)
-        } else {
-            return (title1, title1)
-        }
-    }
-}
 struct TwoRowButtons: View {
-    let sendTitles: (String, String?)
-    let recvTitles: (String, String?)
+    let sendTitle: String
+    let recvTitle: String
     let fitsSideBySide: Bool
     let lineLimit: Int
     let sendDisabled: Bool
     let sendAction: () -> Void
     let recvAction: () -> Void
 
-    @Environment(\.sizeCategory) var sizeCategory
+//    @Environment(\.sizeCategory) var sizeCategory
+    func makeCopy(fitsSideBySide: Bool) -> TwoRowButtons {
+        TwoRowButtons(sendTitle: sendTitle,
+                      recvTitle: recvTitle,
+                 fitsSideBySide: fitsSideBySide,
+                      lineLimit: lineLimit,
+                   sendDisabled: sendDisabled,
+                     sendAction: sendAction,
+                     recvAction: recvAction)
+    }
 
     var body: some View {
         Group {
-            let sendAccTitles = accessTitles(sendTitles, fitsSideBySide)
-            Button(sendAccTitles.0, action: sendAction)
-                .accessibilityLabel(Text(sendAccTitles.1))
+            let sendButtonTitle = sendTitle.tabbed(oneLine: !fitsSideBySide)
+            let sendVoiceOverTitle = sendTitle.tabbed(oneLine: true)
+            Button(sendButtonTitle, action: sendAction)
+                .accessibilityLabel(Text(sendVoiceOverTitle))
                 .lineLimit(lineLimit)
                 .disabled(sendDisabled)
                 .buttonStyle(TalerButtonStyle(type: .bordered,
                                             dimmed: false,
                                            aligned: .center))
-            let recvAccTitles = accessTitles(recvTitles, fitsSideBySide)
-            Button(recvAccTitles.0, action: recvAction)
-                .accessibilityLabel(Text(recvAccTitles.1))
+            let recvButtonTitle = recvTitle.tabbed(oneLine: !fitsSideBySide)
+            let recvVoiceOverTitle = recvTitle.tabbed(oneLine: true)
+            Button(recvButtonTitle, action: recvAction)
+                .accessibilityLabel(Text(recvVoiceOverTitle))
                 .lineLimit(lineLimit)
                 .disabled(false)
                 .buttonStyle(TalerButtonStyle(type: .bordered,
@@ -49,18 +48,19 @@ struct TwoRowButtons: View {
         }
     }
 }
-
+// MARK: -
+#if  DEBUG
 struct TwoRowButtons_Previews: PreviewProvider {
     static var previews: some View {
         List {
-                TwoRowButtons(sendTitles: ("Send", TESTCURRENCY),
-                              recvTitles: ("Receive", LONGCURRENCY),
+                TwoRowButtons(sendTitle: "Send " + TESTCURRENCY,
+                              recvTitle: "Request" + LONGCURRENCY,
                           fitsSideBySide: false,
                                lineLimit: 2, sendDisabled: true,
                               sendAction: {}, recvAction: {})
                 .listRowSeparator(.hidden)
-                TwoRowButtons(sendTitles: ("Send", DEMOCURRENCY),
-                              recvTitles: ("Receive", DEMOCURRENCY),
+                TwoRowButtons(sendTitle: "Send" + DEMOCURRENCY,
+                              recvTitle: "Request" + DEMOCURRENCY,
                           fitsSideBySide: true,
                                lineLimit: 2, sendDisabled: true,
                               sendAction: {}, recvAction: {})
@@ -68,3 +68,4 @@ struct TwoRowButtons_Previews: PreviewProvider {
         }
     }
 }
+#endif
diff --git a/TalerWallet1/Views/Exchange/ExchangeRowView.swift 
b/TalerWallet1/Views/Exchange/ExchangeRowView.swift
index afea32c..15e9c2a 100644
--- a/TalerWallet1/Views/Exchange/ExchangeRowView.swift
+++ b/TalerWallet1/Views/Exchange/ExchangeRowView.swift
@@ -4,18 +4,23 @@
  */
 import SwiftUI
 import taler_swift
+import SymLog
 
 struct ExchangeRowView: View {
+    private let symLog = SymLogV(0)
     let stack: CallStack
     let exchange: Exchange
-    let currency: String
-    @Binding var amountToTransfer: Amount
+    let currency: String                        // this is the currency to be 
used
+    @Binding var amountToTransfer: Amount       // does still have the wrong 
currency
 
     @Environment(\.sizeCategory) var sizeCategory
+    @EnvironmentObject private var controller: Controller
+    @EnvironmentObject private var model: WalletModel
     @AppStorage("iconOnly") var iconOnly: Bool = false
     @State private var buttonSelected: Int? = nil
 
     func selectAndUpdate(_ button: Int) {
+        amountToTransfer.setCurrency(currency)
         buttonSelected = button      // will trigger NavigationLink
         // TODO: after user tapped a button, while navigation animation runs, 
contact Exchange to update Fees
         // cannot be uncommented yet since there is no scopeInfo
@@ -28,83 +33,90 @@ struct ExchangeRowView: View {
 //        }
     }
 
-    func titles(_ title: String) -> (String, String?) {
-        if let separatorIndex = title.firstIndex(of: "\n") {
-            let title1 = String(title[..<separatorIndex])
-            let title2 = String(title[title.index(separatorIndex, offsetBy: 
1)...])
-            return (title1, title2)
-        }
-        return (title, nil)
-    }
-
-    func titles(_ titles: (String, String?), uiFont: UIFont) -> [(String, 
UIFont)] {
-        let (title1, title2) = titles
-        if let title2 {
-            return [(title1, uiFont), (title2, uiFont)]
-        }
-        return [(title1, uiFont)]
-    }
-
     var body: some View {
+#if DEBUG
+        let _ = Self._printChanges()
+        let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
+        let delay: UInt = 0     // set to 5 to test delayed currency 
information
+#else
+        let delay: UInt = 0
+#endif
         let depositTitle0 = String(localized: "DepositButton_Short", 
defaultValue: "Deposit",
                                    comment: "Abbreviation of `Deposit 
(currency)´")
-                    // TODO: deal with \t
         let depositTitle1 = String(localized: "Deposit\t\(currency)",
                                    comment: "Button `Deposit (currency)´, must 
have ONE \\t and ONE %@")
+
         let withdrawTitle0 = String(localized: "WithdrawButton_Short", 
defaultValue: "Withdraw",
                                     comment: "Abbreviation of `Withdraw 
(currency)´")
         let withdrawTitle1 = String(localized: "Withdraw\t\(currency)",
                                     comment: "Button `Withdraw (currency)´, 
must have ONE \\t and ONE %@")
         let baseURL = exchange.exchangeBaseUrl
-
-        HStack(spacing: 0) {    // can't use the built in Label because it 
adds the accessory arrow
-            Text(baseURL.trimURL())
-                .accessibilityFont(.body)
-
-            NavigationLink(destination: LazyView {
-                EmptyView()     // TODO: Deposit
-            }, tag: 1, selection: $buttonSelected
-            ) { EmptyView() }.frame(width: 0).opacity(0)
-            NavigationLink(destination: LazyView {
-                ManualWithdraw(stack: stack.push(),
-                            exchange: exchange,
-                    amountToTransfer: $amountToTransfer)
-            }, tag: 2, selection: $buttonSelected
-            ) { EmptyView() }.frame(width: 0).opacity(0)
-        }.listRowSeparator(.hidden)
-
-        SingleAxisGeometryReader { width in
-            Group {
-                let uiFont = TalerFont.uiFont(.title3)
-                let (deposit1, deposit2) = titles(depositTitle1)
-                let (withdraw1, withdraw2) = titles(withdrawTitle1)
-                let titles = iconOnly ? [(depositTitle0, uiFont), 
(withdrawTitle0, uiFont)]
-                                      : titles((deposit1, deposit2), uiFont: 
uiFont) +
-                                        titles((withdraw1, withdraw2), uiFont: 
uiFont)
-                let fitsSideBySide = Self.fitsSideBySide(titles, 
availableWidth: width,
-                                                         sizeCategory: 
sizeCategory, padding: 10)
-                                                                               
     // TODO:  amount.currencyStr
-                let twoRowButtons = TwoRowButtons(sendTitles: iconOnly ? 
(depositTitle0, nil)  : (deposit1, deposit2),
-                                                  recvTitles: iconOnly ? 
(withdrawTitle0, nil) : (withdraw1, withdraw2),
-                                              fitsSideBySide: fitsSideBySide,
-                                                   lineLimit: 5,
-                                                sendDisabled: true,            
 // TODO: availableAmount.isZero
-                                                  sendAction: { 
selectAndUpdate(1) },
-                                                  recvAction: { 
selectAndUpdate(2) })
-                if fitsSideBySide {
-                    HStack(spacing: HSPACING) { twoRowButtons }
-                } else {
+        let deposit = LazyView {
+            EmptyView()     // TODO: Deposit
+        }
+        let manualWithdraw = LazyView {
+            ManualWithdraw(stack: stack.push(),
+//                           url: nil,
+                        exchange: exchange,
+                amountToTransfer: $amountToTransfer)
+        }
+        let showToS = LazyView {
+            WithdrawTOSView(stack: stack.push(),
+                  exchangeBaseUrl: baseURL,
+                           viewID: VIEW_WITHDRAW_TOS,
+                     acceptAction: nil)         // pop back to here
+        }
+        let twoRowButtons = TwoRowButtons(sendTitle: iconOnly ? depositTitle0 
: depositTitle1,
+                                          recvTitle: iconOnly ? withdrawTitle0 
: withdrawTitle1,
+                                     fitsSideBySide: false,
+                                          lineLimit: 5,
+                                       sendDisabled: true,             // 
TODO: availableAmount.isZero
+                                         sendAction: { selectAndUpdate(1) },
+                                         recvAction: { selectAndUpdate(2) })
+        Group {
+            HStack(spacing: 0) {    // can't use the built in Label because it 
adds the accessory arrow
+                Text(baseURL.trimURL())
+                    .accessibilityFont(.headline)
+                NavigationLink(destination: deposit, tag: 1, selection: 
$buttonSelected)
+                    { EmptyView() }.frame(width: 0).opacity(0)
+                NavigationLink(destination: manualWithdraw, tag: 2, selection: 
$buttonSelected)
+                    { EmptyView() }.frame(width: 0).opacity(0)
+            }.listRowSeparator(.hidden)
+            NavigationLink(destination: showToS) {
+                Text("Terms of Service")  // VIEW_WITHDRAW_TOS
+                    .accessibilityFont(.body)
+            }.listRowSeparator(.hidden)
+            if #available(iOS 16.0, *) {
+                ViewThatFits(in: .horizontal) {
+                    HStack(spacing: HSPACING) {
+                        twoRowButtons.makeCopy(fitsSideBySide: true)
+                    }
                     VStack { twoRowButtons }
                 }
+            } else { // view for iOS 15
+                VStack { twoRowButtons }
+            }
+        }
+        .task {
+            if !controller.hasInfo(for: currency) {
+                symLog.log("fetching info for \(currency)")
+                // FIXME: remove fake ScopeInfo once the REAL one is in 
exchange.scopeInfo
+                let scopeInfo = exchange.scopeInfo ?? ScopeInfo(type: .global, 
currency: currency)
+                do {
+                    let info = try await model.getCurrencyInfo(scope: 
scopeInfo, delay: delay)
+//                    logger.info("got info: \(scope.currency, privacy: 
.public)")
+                    await controller.setInfo(info)
+                } catch {    // TODO: error handling - couldn't get 
CurrencyInfo
+//                    logger.error("Couldn't get info for: \(scope.currency, 
privacy: .public)\n\(error)")
+                }
             }
         }
     }
 }
-
 // MARK: -
 #if DEBUG
 fileprivate struct ExchangeRow_Container : View {
-    @State private var amountToTransfer = Amount(currency: LONGCURRENCY, cent: 
1234)
+    @State private var amountToPreview = Amount(currency: LONGCURRENCY, cent: 
1234)
 
 //    let amount = Amount(currency: LONGCURRENCY, cent: 123456)
     var body: some View {
@@ -125,7 +137,7 @@ fileprivate struct ExchangeRow_Container : View {
         ExchangeRowView(stack: CallStack("Preview"),
                      exchange: exchange1,
                      currency: LONGCURRENCY,
-             amountToTransfer: $amountToTransfer)
+             amountToTransfer: $amountToPreview)
     }
 }
 
diff --git a/TalerWallet1/Views/Exchange/ExchangeSectionView.swift 
b/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
index 4664d50..0f48133 100644
--- a/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
+++ b/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
@@ -14,23 +14,17 @@ struct ExchangeSectionView: View {
     let exchanges: [Exchange]
     @Binding var amountToTransfer: Amount       // does still have the wrong 
currency
 
-    func setCurrency() -> String {
-        amountToTransfer.setCurrency(currency)
-        return currency
-    }
-
     var body: some View {
 #if DEBUG
         let _ = Self._printChanges()
 //        let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
-        let currency2 = setCurrency()           // update currency in 
amountToTransfer
         Section {
             ForEach(exchanges) { exchange in
                 ExchangeRowView(stack: stack.push(),
                              exchange: exchange,
-                             currency: currency2,            // TODO: 
(balance.available) amount.isZero to disable Deposit-button
-                     amountToTransfer: $amountToTransfer)
+                             currency: currency,            // TODO: 
(balance.available) amount.isZero to disable Deposit-button
+                     amountToTransfer: $amountToTransfer)   // does still have 
the wrong currency
             }
         } header: {
             BarGraphHeader(stack: stack.push(), currency: currency)
@@ -41,7 +35,7 @@ struct ExchangeSectionView: View {
 #if DEBUG
 fileprivate struct ExchangeSection_Previews: PreviewProvider {
 fileprivate struct ExchangeRow_Container : View {
-    @State private var amountToTransfer = Amount(currency: LONGCURRENCY, cent: 
1234)
+    @State private var amountToPreview = Amount(currency: LONGCURRENCY, cent: 
1234)
 
 //    let amount = Amount(currency: LONGCURRENCY, cent: 123456)
     var body: some View {
@@ -61,7 +55,7 @@ fileprivate struct ExchangeRow_Container : View {
                            ageRestrictionOptions: [])
         ExchangeSectionView(stack: CallStack("Preview"), currency: 
LONGCURRENCY,
                             exchanges: [exchange1, exchange2],
-                            amountToTransfer: $amountToTransfer)
+                            amountToTransfer: $amountToPreview)
     }
 }
 
diff --git a/TalerWallet1/Views/Exchange/ManualWithdraw.swift 
b/TalerWallet1/Views/Exchange/ManualWithdraw.swift
index dc33b2c..da6e34b 100644
--- a/TalerWallet1/Views/Exchange/ManualWithdraw.swift
+++ b/TalerWallet1/Views/Exchange/ManualWithdraw.swift
@@ -11,6 +11,8 @@ struct ManualWithdraw: View {
     private let symLog = SymLogV(0)
     let stack: CallStack
 
+//    let url: URL?
+//    let exchange: Exchange?
     let exchange: Exchange
     @Binding var amountToTransfer: Amount
 
@@ -18,6 +20,7 @@ struct ManualWithdraw: View {
     @EnvironmentObject private var model: WalletModel
     @AppStorage("iconOnly") var iconOnly: Bool = false
 
+    @State var tosAccepted = false
     @State var withdrawalAmountDetails: WithdrawalAmountDetails? = nil
 //    @State var ageMenuList: [Int] = []
 //    @State var selectedAge = 0
@@ -32,67 +35,75 @@ struct ManualWithdraw: View {
         let navTitle = String(localized: "NavTitle_Withdraw (currency)", 
defaultValue: "Withdraw \(currency)")
 //        let agePicker = AgePicker(ageMenuList: $ageMenuList, selectedAge: 
$selectedAge)
 
-        ScrollView { VStack {
-                CurrencyInputView(amount: $amountToTransfer,
-                               available: nil,
-                                   title: iconOnly ? String(localized: "How 
much:")
-                                                   : String(localized: "Amount 
to withdraw:"))
-                let someCoins = SomeCoins(details: withdrawalAmountDetails)
-                QuiteSomeCoins(someCoins: someCoins,
-                           shouldShowFee: true,           // TODO: set to 
false if we never charge withdrawal fees
-                                currency: currency,
-                            currencyInfo: currencyInfo,
-                         amountEffective: 
withdrawalAmountDetails?.amountEffective)
-                Text(exchange.exchangeBaseUrl.trimURL())
-                    .multilineTextAlignment(.center)
-                    .accessibilityFont(.body)
-
-                let disabled = amountToTransfer.isZero || someCoins.invalid || 
someCoins.tooMany
-                if !disabled {
-//                    agePicker
-
-                    if let tosAccepted = withdrawalAmountDetails?.tosAccepted {
-                        if tosAccepted {
-//                            let restrictAge: Int? = (selectedAge == 0) ? nil
-//                                                                       : 
selectedAge
-//let _ = print(selectedAge, restrictAge)
-                            NavigationLink(destination: LazyView {
-                                ManualWithdrawDone(stack: stack.push(),
-                                                exchange: exchange,
-                                        amountToTransfer: amountToTransfer)
-//                                              restrictAge: restrictAge)
-                            }) {
-                                Text("Confirm Withdrawal")      // 
VIEW_WITHDRAW_ACCEPT
-                            }
-                            .buttonStyle(TalerButtonStyle(type: .prominent))
-                            .padding(.horizontal)
-                        } else {
-                            ToSButtonView(stack: stack.push(),
-                                exchangeBaseUrl: exchange.exchangeBaseUrl,
-                                         viewID: VIEW_WITHDRAW_TOS,
-                                            p2p: false)
-                        }
-                    }
-                } // disabled
+        let someCoins = SomeCoins(details: withdrawalAmountDetails)
+//      let restrictAge: Int? = (selectedAge == 0) ? nil
+//                                                 : selectedAge
+//  let _ = print(selectedAge, restrictAge)
+        let destination = LazyView {
+            ManualWithdrawDone(stack: stack.push(),
+                            exchange: exchange,
+                    amountToTransfer: amountToTransfer)
+//                       restrictAge: restrictAge)
+        }
+        let disabled = amountToTransfer.isZero || someCoins.invalid || 
someCoins.tooMany
+        ScrollView { VStack(alignment: .trailing) {
+            Text("via \(exchange.exchangeBaseUrl.trimURL())")
+                .multilineTextAlignment(.center)
+                .accessibilityFont(.body)
+            CurrencyInputView(amount: $amountToTransfer,
+                           available: nil,
+                               title: iconOnly ? String(localized: "How much:")
+                                               : String(localized: "Amount to 
withdraw:"),
+                      shortcutAction: nil)
+                .padding(.top)
+            QuiteSomeCoins(someCoins: someCoins,
+                       shouldShowFee: true,           // TODO: set to false if 
we never charge withdrawal fees
+                            currency: currency,
+                        currencyInfo: currencyInfo,
+                     amountEffective: withdrawalAmountDetails?.amountEffective)
+//          agePicker
+            if tosAccepted {
+                NavigationLink(destination: destination) {
+                    Text("Confirm Withdrawal")      // VIEW_WITHDRAW_ACCEPT
+                }
+                .buttonStyle(TalerButtonStyle(type: .prominent))
+                .disabled(disabled)
+                .padding(.top)
+            } else {
+                ToSButtonView(stack: stack.push(),
+                    exchangeBaseUrl: exchange.exchangeBaseUrl,
+                             viewID: VIEW_WITHDRAW_TOS,
+                                p2p: false)
+                .padding(.top)
+            }
         } } // ScrollVStack
         .frame(maxWidth: .infinity, alignment: .leading)
         .padding(.horizontal)
         .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
         .navigationTitle(navTitle)
         .onAppear {
-            symLog.log("onAppear")
             DebugViewC.shared.setViewID(VIEW_WITHDRAWAL, stack: stack.push())
+            symLog.log("❗️ \(navTitle) onAppear")
+        }
+        .onDisappear {
+            symLog.log("❗️ \(navTitle) onDisappear")
         }
         .task(id: amountToTransfer.value) { // re-run this whenever 
amountToTransfer changes
-            if !amountToTransfer.isZero {
-                do {
-                    withdrawalAmountDetails = try await 
model.loadWithdrawalDetailsForAmountM(exchange.exchangeBaseUrl,
-                                                                               
               amount: amountToTransfer)
+            do {
+                let amount = amountToTransfer.isZero ? Amount(currency: 
currency, cent: 100)
+                                                     : amountToTransfer
+//              if amountToTransfer.isZero {
+//                  tosAccepted = GetTosStatus(exchange.exchangeBaseUrl)
+//              } else {
+                    let details = try await 
model.loadWithdrawalDetailsForAmountM(exchange.exchangeBaseUrl,
+                                                                               
   amount: amount)
+                    tosAccepted = details.tosAccepted
+                    withdrawalAmountDetails = amountToTransfer.isZero ? nil : 
details
 //                  agePicker.setAges(ages: 
withdrawalAmountDetails?.ageRestrictionOptions)
-                } catch {    // TODO: error
-                    symLog.log(error.localizedDescription)
-                    withdrawalAmountDetails = nil
-                }
+//              }
+            } catch {    // TODO: error
+                symLog.log(error.localizedDescription)
+                withdrawalAmountDetails = nil
             }
         }
     }
@@ -102,13 +113,9 @@ struct ManualWithdraw: View {
 struct ManualWithdraw_Previews: PreviewProvider {
   struct StateContainer : View {
     @State private var amountToTransfer = Amount(currency: LONGCURRENCY, cent: 
510)
-    @State private var details = WithdrawalAmountDetails(tosAccepted: false,
-                                                           amountRaw: 
Amount(currency: LONGCURRENCY, cent: 510),
-                                                     amountEffective: 
Amount(currency: LONGCURRENCY, cent: 500),
-                                                           paytoUris: [],
-                                               ageRestrictionOptions: [],
-                                                            numCoins: 6)
-    var body: some View {
+
+      var body: some View {
+        let scopeInfo = ScopeInfo(type: ScopeInfo.ScopeInfoType.exchange, url: 
DEMOEXCHANGE, currency: LONGCURRENCY)
         let exchange = Exchange(exchangeBaseUrl: DEMOEXCHANGE,
                                        currency: LONGCURRENCY,
                                       paytoUris: [],
@@ -116,7 +123,16 @@ struct ManualWithdraw_Previews: PreviewProvider {
                             exchangeEntryStatus: .preset,
                            exchangeUpdateStatus: .initial,
                           ageRestrictionOptions: [])
+        let details = WithdrawalAmountDetails(tosAccepted: false,
+                                                amountRaw: Amount(currency: 
LONGCURRENCY, cent: 510),
+                                          amountEffective: Amount(currency: 
LONGCURRENCY, cent: 500),
+                                                 numCoins: 6,
+//                                              paytoUris: [],
+                                   withdrawalAccountsList: [],
+                                    ageRestrictionOptions: [],
+                                                scopeInfo: scopeInfo)
         ManualWithdraw(stack: CallStack("Preview"),
+//                         url: nil,
                     exchange: exchange,
             amountToTransfer: $amountToTransfer,
      withdrawalAmountDetails: details)
diff --git a/TalerWallet1/Views/HelperViews/AmountRowV.swift 
b/TalerWallet1/Views/HelperViews/AmountRowV.swift
index 7fb6298..19f99dc 100644
--- a/TalerWallet1/Views/HelperViews/AmountRowV.swift
+++ b/TalerWallet1/Views/HelperViews/AmountRowV.swift
@@ -22,7 +22,7 @@ struct AmountRowV<Content: View>: View {
 
     var body: some View {
         let text = Text(amountStr)
-            .strikethrough(!doneOrPending)
+//            .strikethrough(!doneOrPending)
             .foregroundColor(amountColor)
             .accessibilityFont(largeAmountFont ? .title : .title2)
             .monospacedDigit()
diff --git a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift 
b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
index 8c3f7c2..3c9cbae 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
@@ -55,6 +55,7 @@ struct CurrencyInputView: View {
     @Binding var amount: Amount         // the `value´
     let available: Amount?
     let title: String
+    let shortcutAction: ((_ amount: Amount) -> Void)?
 
     @EnvironmentObject private var controller: Controller
     
@@ -63,11 +64,15 @@ struct CurrencyInputView: View {
     @State private var useShortcut = 0
 
     func action(shortcut: Int, currencyField: CurrencyField) {
-        useShortcut = shortcut
         let shortie = Amount(currency: amount.currencyStr, cent: 
UInt64(shortcut))      // TODO: adapt for ¥
-        currencyField.updateText(amount: shortie)
-        amount = shortie
-        currencyField.resignFirstResponder()
+        if let shortcutAction {
+            shortcutAction(shortie)
+        } else {
+            useShortcut = shortcut
+            currencyField.updateText(amount: shortie)
+            amount = shortie
+            currencyField.resignFirstResponder()
+        }
     }
 
     var body: some View {
@@ -168,7 +173,8 @@ fileprivate struct Previews: PreviewProvider {
 //            Preview_Content()
             CurrencyInputView(amount: $amountToTransfer,
                            available: nil,
-                               title: "Amount to withdraw:")
+                               title: "Amount to withdraw:",
+                      shortcutAction: nil)
                 .environmentObject(controller)
         }
     }
diff --git a/TalerWallet1/Views/Main/MainView.swift 
b/TalerWallet1/Views/Main/MainView.swift
index 839b546..43c6906 100644
--- a/TalerWallet1/Views/Main/MainView.swift
+++ b/TalerWallet1/Views/Main/MainView.swift
@@ -146,7 +146,7 @@ extension MainView {
                                              hamburgerAction: hamburgerAction)
                                      )),
             SidebarItem(name: exchangesTitle,
-                    sysImage: "building.columns",
+                    sysImage: "arrow.triangle.2.circlepath",
                         view: AnyView(ExchangeListView(stack: 
stack.push(exchangesTitle),
 //                                                    balances: $balances,
                                                     navTitle: exchangesTitle,
@@ -201,7 +201,7 @@ extension MainView {
                                   navTitle: exchangesTitle)
                 }.navigationViewStyle(.stack)
                 .tabItem {
-                    Image(systemName: "building.columns")
+                    Image(systemName: "arrow.triangle.2.circlepath")
                         .accessibilityLabel(exchangesTitle)
                     if !iconOnly { Text(exchangesTitle) }
                 }
diff --git a/TalerWallet1/Views/Peer2peer/P2PReadyV.swift 
b/TalerWallet1/Views/Peer2peer/P2PReadyV.swift
index 682e207..b448eaa 100644
--- a/TalerWallet1/Views/Peer2peer/P2PReadyV.swift
+++ b/TalerWallet1/Views/Peer2peer/P2PReadyV.swift
@@ -8,12 +8,12 @@ import SymLog
 
 // Called when initiating a P2P transaction: Send coins or Send 
Request(Invoice)
 struct P2PReadyV: View {
-    private let symLog = SymLogV()
+    private let symLog = SymLogV(0)
     let stack: CallStack
-    let amountToSend: Amount?
-    let amountToReceive: Amount?
     let summary: String
     let expireDays: UInt
+    let amountToSend: Bool
+    let amountToTransfer: Amount
     @Binding var transactionStarted: Bool
 
     @EnvironmentObject private var model: WalletModel
@@ -62,7 +62,7 @@ struct P2PReadyV: View {
         .onDisappear {
 //            print("❗️ P2PReadyV onDisappear")
         }
-        .task {
+        .task(id: amountToTransfer.value) {
             symLog.log(".task")
             do {
                 guard transactionStarted == false else {
@@ -74,23 +74,23 @@ struct P2PReadyV: View {
                 let timestamp = developerMode ? 
Timestamp.inSomeMinutes(expireDays > 20 ? (24*60)
                                                                       : 
expireDays > 5 ? 60 : 3)
                                               : 
Timestamp.inSomeDays(expireDays)
-                if let amountToSend {
-                    let terms = PeerContractTerms(amount: amountToSend,
+                if amountToSend {
+                    let terms = PeerContractTerms(amount: amountToTransfer,
                                                  summary: summary,
                                         purse_expiration: timestamp)
                     // TODO: let user choose baseURL
                     let response = try await model.initiatePeerPushDebitM(nil, 
terms: terms)
                     // will switch from WithdrawProgressView to 
TransactionDetailView
                     transactionId = response.transactionId
-                } else if let amountToReceive {
-                    let terms = PeerContractTerms(amount: amountToReceive,
+                } else {
+                    let terms = PeerContractTerms(amount: amountToTransfer,
                                                  summary: summary,
                                         purse_expiration: timestamp)
                     // TODO: let user choose baseURL
                     let response = try await 
model.initiatePeerPullCreditM(nil, terms: terms)
                     // will switch from WithdrawProgressView to 
TransactionDetailView
                     transactionId = response.transactionId
-                } else { fatalError() }
+                }
             } catch {    // TODO: error
                 symLog.log(error.localizedDescription)
             }
diff --git a/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift 
b/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
index aa6c13b..d6a788c 100644
--- a/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
+++ b/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
@@ -6,35 +6,61 @@ import SwiftUI
 import taler_swift
 import SymLog
 
+func p2pFee(ppCheck: CheckPeerPushDebitResponse) -> Amount? {
+    do {
+        // Outgoing: fee = effective - raw
+        let fee = try ppCheck.amountEffective - ppCheck.amountRaw
+        return fee
+    } catch {}
+    return nil
+}
+
 struct P2PSubjectV: View {
     private let symLog = SymLogV(0)
     let stack: CallStack
-
-    let amountToSend: Amount?
-    let amountToReceive: Amount?
-//    let amountToTransfer: Amount
-    let navTitle: String
-    let buttonTitle: String
-    let feeLabel: String
+    let feeLabel: String?
     let currencyInfo: CurrencyInfo
+    let amountToSend: Bool
+    @Binding var amountToTransfer: Amount
     @Binding var summary: String
     @Binding var expireDays: UInt
 
+    @EnvironmentObject private var model: WalletModel
     @AppStorage("iconOnly") var iconOnly: Bool = false
 
+    @State private var myFeeLabel: String = EMPTYSTRING
     @State private var transactionStarted: Bool = false
     @FocusState private var isFocused: Bool
 
+    private func buttonTitle(_ amount: Amount, _ currencyInfo: CurrencyInfo) 
-> String {
+        let amountWithCurrency = amount.string(currencyInfo, useSymbol: false)
+        return amountToSend ? String(localized: "Send \(amountWithCurrency) 
now",
+                                       comment: "amount with currency")
+                            : String(localized: "Request 
\(amountWithCurrency)",
+                                       comment: "amount with currency")
+    }
+
+    private func subjectTitle(_ amount: Amount, _ currencyInfo: CurrencyInfo) 
-> String {
+        let amountStr = amount.string(currencyInfo)
+        return amountToSend ? String(localized: "NavTitle_Send_AmountStr",
+                                  defaultValue: "Send \(amountStr)",
+                                       comment: "NavTitle: Send 'amountStr'")
+                            : String(localized: "NavTitle_Request_AmountStr",
+                                  defaultValue: "Request \(amountStr)",
+                                       comment: "NavTitle: Request 
'amountStr'")
+    }
+
     var body: some View {
 #if DEBUG
         let _ = Self._printChanges()
-        let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
+        let _ = symLog.vlog(amountToTransfer.readableDescription)       // 
just to get the # to compare it with .onAppear & onDisappear
 #endif
         ScrollView { VStack (alignment: .leading, spacing: 6) {
-            if feeLabel.count > 0 {
+            let label = feeLabel ?? myFeeLabel
+            if label.count > 0 {
                 HStack {
                     Spacer()
-                    Text(feeLabel)
+                    Text(label)
                         .foregroundColor(.red)
                         .accessibilityFont(.body)
                 }
@@ -81,19 +107,19 @@ struct P2PSubjectV: View {
                 let disabled = (expireDays == 0) || (summary.count < 1)    // 
TODO: check amountAvailable
                 NavigationLink(destination: LazyView {
                     P2PReadyV(stack: stack.push(),
-                       amountToSend: amountToSend,
-                    amountToReceive: amountToReceive,
                             summary: summary,
                          expireDays: expireDays,
+                       amountToSend: amountToSend,
+                   amountToTransfer: amountToTransfer,
                  transactionStarted: $transactionStarted)
                 }) {
-                    Text(buttonTitle)
+                    Text(buttonTitle(amountToTransfer, currencyInfo))
                 }
                 .buttonStyle(TalerButtonStyle(type: .prominent))
                 .disabled(disabled)
                 .accessibilityHint(disabled ? "enabled when subject and 
expiration are set" : EMPTYSTRING)
         }.padding(.horizontal) } // ScrollVStack
-        .navigationTitle(navTitle)
+        .navigationTitle(subjectTitle(amountToTransfer, currencyInfo))
         .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
         .onAppear {
             DebugViewC.shared.setViewID(VIEW_P2P_SUBJECT, stack: stack.push())
@@ -102,6 +128,19 @@ struct P2PSubjectV: View {
         .onDisappear {
 //            print("❗️ P2PSubjectV onDisappear")
         }
+        .task(id: amountToTransfer.value) {
+            if feeLabel == nil {
+                do {
+                    let ppCheck = try await 
model.checkPeerPushDebitM(amountToTransfer)
+                    if let feeAmount = p2pFee(ppCheck: ppCheck) {
+                        let feeStr = feeAmount.string(currencyInfo)
+                        myFeeLabel = String(localized: "+ \(feeStr) send fee")
+                    } else { myFeeLabel = EMPTYSTRING }
+                } catch {    // TODO: error
+                    symLog.log(error.localizedDescription)
+                }
+            }
+        }
     }
 }
 // MARK: -
diff --git a/TalerWallet1/Views/Peer2peer/RequestPayment.swift 
b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
index a16f3c8..45c0441 100644
--- a/TalerWallet1/Views/Peer2peer/RequestPayment.swift
+++ b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
@@ -20,6 +20,13 @@ struct RequestPayment: View {
 
     @State private var peerPullCheck: CheckPeerPullCreditResponse? = nil
     @State private var expireDays: UInt = 0
+    @State private var buttonSelected = false
+    @State private var amountShortcut = Amount.zero(currency: EMPTYSTRING)     
 // Update currency when used
+
+    private func shortcutAction(_ shortcut: Amount) {
+        amountShortcut = shortcut
+        buttonSelected = true
+    }
 
     var body: some View {
 #if DEBUG
@@ -32,54 +39,56 @@ struct RequestPayment: View {
         let navTitle = String(localized: "NavTitle_Request_Currency",
                            defaultValue: "Request \(currencySymbol)",
                                 comment: "NavTitle: Request 'currencySymbol'")
-        let amountStr = amountToTransfer.string(currencyInfo)
-        let amountWithCurrency = amountToTransfer.string(currencyInfo, 
useSymbol: false)
-        let buttonTitle = String(localized: "Request \(amountWithCurrency)", 
comment: "amount with currency")
-        let navTitle2 = String(localized: "NavTitle_Request_AmountStr",
-                            defaultValue: "Request \(amountStr)",
-                                 comment: "NavTitle: Request 'amountStr'")
-
+        let someCoins = SomeCoins(details: peerPullCheck)
         let _ = symLog.log("currency: \(currency)")
+        let inputDestination = LazyView {
+            P2PSubjectV(stack: stack.push(),
+                     feeLabel: someCoins.feeLabel(currencyInfo),
+                 currencyInfo: currencyInfo,
+                 amountToSend: false,
+             amountToTransfer: $amountToTransfer,
+                      summary: $summary,
+                   expireDays: $expireDays)
+        }
+        let shortcutDestination = LazyView {
+            P2PSubjectV(stack: stack.push(),
+                     feeLabel: nil,
+                 currencyInfo: currencyInfo,
+                 amountToSend: false,
+             amountToTransfer: $amountShortcut,
+                      summary: $summary,
+                   expireDays: $expireDays)
+        }
+        let disabled = amountToTransfer.isZero || someCoins.invalid || 
someCoins.tooMany
         ScrollView { VStack(alignment: .trailing) {
             CurrencyInputView(amount: $amountToTransfer,
                            available: nil,
                                title: iconOnly ? String(localized: "How much:")
-                                               : String(localized: "Amount to 
request:"))
-            .padding(.top)
-            let someCoins = SomeCoins(details: peerPullCheck)
+                                               : String(localized: "Amount to 
request:"),
+                      shortcutAction: shortcutAction)
+                .padding(.top)
             QuiteSomeCoins(someCoins: someCoins,
                        shouldShowFee: true,     // always true since the 
requester pays fees
                             currency: currency,
                         currencyInfo: currencyInfo,
                      amountEffective: peerPullCheck?.amountEffective)
-
-            let disabled = amountToTransfer.isZero || someCoins.invalid || 
someCoins.tooMany
-
-            NavigationLink(destination: LazyView {
-                P2PSubjectV(stack: stack.push(),
-                     amountToSend: nil,
-                  amountToReceive: amountToTransfer,
-                         navTitle: navTitle2,
-                      buttonTitle: buttonTitle,
-                         feeLabel: someCoins.feeLabel(currencyInfo),
-                     currencyInfo: currencyInfo,
-                          summary: $summary,
-                       expireDays: $expireDays)
-            }) { Text("Next") }
+            NavigationLink(destination: inputDestination) { Text("Next") }
                 .buttonStyle(TalerButtonStyle(type: .prominent))
                 .disabled(disabled)
-//            Spacer()
+                .background(NavigationLink(destination: shortcutDestination, 
isActive: $buttonSelected)
+                            { EmptyView() }.frame(width: 0).opacity(0).hidden()
+                )
         } } // ScrollVStack
         .frame(maxWidth: .infinity, alignment: .leading)
-        .padding(.horizontal)
+        .padding(.horizontal)   // Lists do this automatically, but this is a 
VStack
         .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
         .navigationTitle(navTitle)
         .onAppear {
             DebugViewC.shared.setViewID(VIEW_P2P_REQUEST, stack: stack.push())
-            symLog.log("❗️Yikes \(navTitle) onAppear")
+            symLog.log("❗️ \(navTitle) onAppear")
         }
         .onDisappear {
-            symLog.log("❗️Yikes \(navTitle) onDisappear")
+            symLog.log("❗️ \(navTitle) onDisappear")
         }
         .task(id: amountToTransfer.value) {
             if amountToTransfer.isZero {
diff --git a/TalerWallet1/Views/Peer2peer/SendAmount.swift 
b/TalerWallet1/Views/Peer2peer/SendAmount.swift
index b63da48..d66f41c 100644
--- a/TalerWallet1/Views/Peer2peer/SendAmount.swift
+++ b/TalerWallet1/Views/Peer2peer/SendAmount.swift
@@ -20,9 +20,11 @@ struct SendAmount: View {
     @AppStorage("iconOnly") var iconOnly: Bool = false
 
     @State var peerPushCheck: CheckPeerPushDebitResponse? = nil
-    @State private var expireDays: UInt = SEVENDAYS
-    @State private var insufficient: Bool = false
+    @State private var expireDays = SEVENDAYS
+    @State private var insufficient = false
     @State private var feeStr: String = EMPTYSTRING
+    @State private var buttonSelected = false
+    @State private var amountShortcut = Amount.zero(currency: EMPTYSTRING)     
 // Update currency when used
 
     private func fee(ppCheck: CheckPeerPushDebitResponse?) -> Amount? {
         do {
@@ -37,6 +39,11 @@ struct SendAmount: View {
     
     var feeLabel: String { feeStr.count > 0 ? String(localized: "+ \(feeStr) 
send fee") : EMPTYSTRING }
 
+    private func shortcutAction(_ shortcut: Amount) {
+        amountShortcut = shortcut
+        buttonSelected = true
+    }
+
     var body: some View {
 #if DEBUG
         let _ = Self._printChanges()
@@ -48,49 +55,52 @@ struct SendAmount: View {
         let navTitle = String(localized: "NavTitle_Send_Currency",
                            defaultValue: "Send \(currencySymbol)",
                                 comment: "NavTitle: Send 'currencySymbol'")
-        let amountStr = amountToTransfer.string(currencyInfo)
-        let amountWithCurrency = amountToTransfer.string(currencyInfo, 
useSymbol: false)
-        let buttonTitle = String(localized: "Send \(amountWithCurrency) now", 
comment: "amount with currency")
-        let navTitle2 = String(localized: "NavTitle_Send_AmountStr",
-                            defaultValue: "Send \(amountStr)",
-                                 comment: "NavTitle: Send 'amountStr'")
-
         let available = amountAvailable.string(currencyInfo)
+//        let _ = print("available: \(available)")
         let _ = symLog.log("currency: \(currency), available: \(available)")
         let amountVoiceOver = amountToTransfer.string(currencyInfo)
         let insufficientLabel = String(localized: "You don't have enough 
\(currency).")
         let insufficientLabel2 = String(localized: "but you only have 
\(available) to send.")
 
+        let inputDestination = LazyView {
+            P2PSubjectV(stack: stack.push(),
+                     feeLabel: feeLabel,
+                 currencyInfo: currencyInfo,
+                 amountToSend: true,
+             amountToTransfer: $amountToTransfer,
+                      summary: $summary,
+                   expireDays: $expireDays)
+        }
+        let shortcutDestination = LazyView {
+            P2PSubjectV(stack: stack.push(),
+                     feeLabel: nil,
+                 currencyInfo: currencyInfo,
+                 amountToSend: true,
+             amountToTransfer: $amountShortcut,
+                      summary: $summary,
+                   expireDays: $expireDays)
+        }
+        let disabled = insufficient || amountToTransfer.isZero
         ScrollView { VStack(alignment: .trailing) {
-//          let _ = print("available: \(available)")
             Text("Available:\t\(available)")
                 .accessibilityFont(.title3)
                 .padding(.bottom, 2)
             CurrencyInputView(amount: $amountToTransfer,
                            available: amountAvailable,
                                title: iconOnly ? String(localized: "How much:")
-                                               : String(localized: "Amount to 
send:"))
-            let disabled = insufficient || amountToTransfer.isZero
+                                               : String(localized: "Amount to 
send:"),
+                      shortcutAction: shortcutAction)
             Text(insufficient ? insufficientLabel
                               : feeLabel)
                 .accessibilityFont(.body)
                 .foregroundColor(.red)
                 .padding(4)
-
-            NavigationLink(destination: LazyView {
-                P2PSubjectV(stack: stack.push(),
-                     amountToSend: amountToTransfer,
-                  amountToReceive: nil,
-                         navTitle: navTitle2,
-                      buttonTitle: buttonTitle,
-                         feeLabel: feeLabel,
-                     currencyInfo: currencyInfo,
-                          summary: $summary,
-                       expireDays: $expireDays)
-            }) { Text("Next") }
+            NavigationLink(destination: inputDestination) { Text("Next") }
                 .buttonStyle(TalerButtonStyle(type: .prominent))
                 .disabled(disabled)
-//            Spacer()
+                .background(NavigationLink(destination: shortcutDestination, 
isActive: $buttonSelected)
+                    { EmptyView() }.frame(width: 0).opacity(0).hidden()
+                )
         } } // ScrollVStack
         .frame(maxWidth: .infinity, alignment: .leading)
         .padding(.horizontal)
@@ -98,15 +108,14 @@ struct SendAmount: View {
         .navigationTitle(navTitle)
         .onAppear {
             DebugViewC.shared.setViewID(VIEW_P2P_SEND, stack: stack.push())
-            symLog.log("❗️Yikes SendAmount onAppear")
+            symLog.log("❗️ \(navTitle) onAppear")
         }
         .onDisappear {
-            symLog.log("❗️Yikes SendAmount onDisappear")
+            symLog.log("❗️ \(navTitle) onDisappear")
         }
         .task(id: amountToTransfer.value) {
             do {
                 insufficient = try amountToTransfer > amountAvailable
-                print("amountStr: \(amountVoiceOver)")
             } catch {
                 print("Yikes❗️ insufficient failed❗️")
                 insufficient = true
@@ -137,14 +146,14 @@ struct SendAmount: View {
 // MARK: -
 #if DEBUG
 fileprivate struct Preview_Content: View {
-    @State private var amountToTransfer = Amount(currency: TESTCURRENCY, cent: 
510)
+    @State private var amountToPreview = Amount(currency: TESTCURRENCY, cent: 
510)
     @State private var summary: String = ""
 
     var body: some View {
         let amount = Amount(currency: TESTCURRENCY, integer: 10, fraction: 0)
         SendAmount(stack: CallStack("Preview"),
          amountAvailable: amount,
-        amountToTransfer: $amountToTransfer,
+        amountToTransfer: $amountToPreview,
                  summary: $summary)
     }
 }
diff --git a/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift 
b/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
index 5a66006..0a7028e 100644
--- a/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
+++ b/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
@@ -9,7 +9,7 @@ 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 payment details
 struct PayTemplateView: View {
-    private let symLog = SymLogV()
+    private let symLog = SymLogV(0)
     let stack: CallStack
     let navTitle = String(localized: "Confirm Payment", comment:"pay merchant")
 
diff --git a/TalerWallet1/Views/Sheets/Payment/PaymentView.swift 
b/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
index 534a7d4..4ce2970 100644
--- a/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
+++ b/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
@@ -43,7 +43,10 @@ struct PaymentView: View {
     var body: some View {
         if let preparePayResult {
             let effective = preparePayResult.amountEffective
+            let terms = preparePayResult.contractTerms
             List {
+                Text(terms.summary)
+                    .accessibilityFont(.title3)
                 let baseURL = 
preparePayResult.contractTerms.exchanges.first?.url
                 let raw = preparePayResult.amountRaw
                 let currency = raw.currencyStr
diff --git a/TalerWallet1/Views/Sheets/Refund/RefundURIView.swift 
b/TalerWallet1/Views/Sheets/Refund/RefundURIView.swift
new file mode 100644
index 0000000..edebcff
--- /dev/null
+++ b/TalerWallet1/Views/Sheets/Refund/RefundURIView.swift
@@ -0,0 +1,60 @@
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
+import SwiftUI
+import SymLog
+import taler_swift
+
+struct RefundURIView: View {
+    private let symLog = SymLogV(0)
+    let stack: CallStack
+    let url: URL
+
+    @EnvironmentObject private var model: WalletModel
+
+//    let navTitle = String(localized: "Refund", comment:"receive refund")
+
+    @State var refundTransactionId: String? = nil
+
+    func reloadOneAction(_ transactionId: String) async throws -> Transaction {
+        return try await model.getTransactionByIdT(transactionId)
+    }
+
+    var body: some View {
+        if let refundTransactionId {
+            TransactionDetailView(stack: stack.push(),
+                          transactionId: refundTransactionId,
+                           reloadAction: reloadOneAction,
+                               navTitle: nil,   // navTitle,
+                             doneAction: ViewState.shared.popToRootView,
+                            abortAction: model.abortTransaction,
+                           deleteAction: model.deleteTransaction,
+                             failAction: model.failTransaction,
+                          suspendAction: model.suspendTransaction,
+                           resumeAction: model.resumeTransaction)
+        } else {
+            let badURL = "Error in Link: \(url)"
+            WithdrawProgressView(message: url.host ?? badURL)
+                .navigationTitle("Find Exchange")
+                .task {
+                    do {
+                        symLog.log(".task")
+                        let result = try await model.startRefundForUriM(url: 
url.absoluteString)
+                        refundTransactionId = result
+                    } catch {    // TODO: error
+                        symLog.log(error.localizedDescription)
+                    }
+                }
+        }
+    }
+}
+
+// MARK: -
+struct RefundURIView_Previews: PreviewProvider {
+    static var previews: some View {
+        let transactionId = "txn:refund:12345"
+        let url = URL(string: "taler://reward/survey")!
+        RefundURIView(stack: CallStack("Preview"), url: url, 
refundTransactionId: transactionId)
+    }
+}
diff --git a/TalerWallet1/Views/Sheets/URLSheet.swift 
b/TalerWallet1/Views/Sheets/URLSheet.swift
index 4c9f99d..8e38872 100644
--- a/TalerWallet1/Views/Sheets/URLSheet.swift
+++ b/TalerWallet1/Views/Sheets/URLSheet.swift
@@ -3,6 +3,7 @@
  * See LICENSE.md
  */
 import SwiftUI
+import taler_swift
 import SymLog
 
 struct URLSheet: View {
@@ -12,6 +13,7 @@ struct URLSheet: View {
     var urlToOpen: URL
 
     @EnvironmentObject private var controller: Controller
+    @State private var amountToTransfer = Amount.zero(currency: "")
 
     var body: some View {
 #if DEBUG
@@ -24,6 +26,8 @@ struct URLSheet: View {
             switch urlCommand {
                 case .withdraw:
                     WithdrawURIView(stack: stack.push(), url: urlToOpen)
+//                case .withdrawExchange:
+//                    ManualWithdraw(stack: stack.push(), url: urlToOpen, 
exchange: nil, amountToTransfer: $amountToTransfer)
                 case .pay:
                     PaymentView(stack: stack.push(), url: urlToOpen)
                 case .payPull:
@@ -32,6 +36,8 @@ struct URLSheet: View {
                     P2pReceiveURIView(stack: stack.push(), url: urlToOpen)
                 case .payTemplate:
                     PayTemplateView(stack: stack.push(), url: urlToOpen)
+                case .refund:
+                    RefundURIView(stack: stack.push(), url: urlToOpen)
 //                case .reward:
 //                    RewardURIView(url: urlToOpen)
                 default:        // Error view
diff --git 
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift 
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
index 0142d42..0eba89c 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
@@ -26,13 +26,15 @@ struct WithdrawTOSView: View {
             Content(symLog: symLog, exchangeTOS: exchangeTOS, myListStyle: 
$myListStyle) {
                 Task { // runs on MainActor
                     do {
-                        if let exchangeBaseUrl {
-                            _ = try await 
model.setExchangeTOSAcceptedM(exchangeBaseUrl, etag: exchangeTOS!.currentEtag)
+                        if let exchangeBaseUrl, let exchangeTOS {
+                            _ = try await 
model.setExchangeTOSAcceptedM(exchangeBaseUrl, etag: exchangeTOS.currentEtag)
                             if acceptAction != nil {
                                 acceptAction!()
                             } else { // just go back - caller will reload
                                 self.presentationMode.wrappedValue.dismiss()
                             }
+                        } else {
+                            // TODO: error
                         }
                     } catch {    // TODO: Show Error
                         symLog.log(error.localizedDescription)
@@ -110,9 +112,15 @@ extension WithdrawTOSView {
                         }
                     }
                 }.safeAreaInset(edge: .bottom) {
-                    Button(String(localized: "Accept Terms of Service", 
comment: "Button"), action: acceptAction)
-                        .buttonStyle(TalerButtonStyle(type: .prominent))
-                        .padding(.horizontal)
+                    let currentEtag = tos.currentEtag
+                    let showButton = tos.acceptedEtag == nil ? true
+                                   : tos.acceptedEtag! == tos.currentEtag ? 
false
+                                                                          : 
true
+                    if showButton {
+                        Button(String(localized: "Accept Terms of Service", 
comment: "Button"), action: acceptAction)
+                            .buttonStyle(TalerButtonStyle(type: .prominent))
+                            .padding(.horizontal)
+                    }
                 }
                 .listStyle(myListStyle.style).anyView
             } else {
diff --git a/TalerWallet1/Views/Transactions/TransactionDetailView.swift 
b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
index 4dd575f..666c4e1 100644
--- a/TalerWallet1/Views/Transactions/TransactionDetailView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
@@ -22,7 +22,7 @@ extension Transaction {             // for Dummys
 }
 // MARK: -
 struct TransactionDetailView: View {
-    private let symLog = SymLogV()
+    private let symLog = SymLogV(0)
     let stack: CallStack
     let transactionId: String
     let reloadAction: ((_ transactionId: String) async throws -> Transaction)
@@ -76,6 +76,8 @@ struct TransactionDetailView: View {
                     return true
                 }
             }
+        } else { // no sheet but the details view -> reload
+            checkReload(notification, logStr)
         }
         return false
     }
@@ -124,7 +126,6 @@ struct TransactionDetailView: View {
                     .accessibilityFont(.body)
                     .accessibilityLabel(accessibilityDate)
                     .foregroundColor(colorSchemeContrast == .increased ? 
.primary : .secondary)
-//                    .foregroundColor(moreContrast ? .primary : .secondary)
                     .listRowSeparator(.hidden)
                 HStack {
                     Text(verbatim: "|")   // only reason for this 
leading-aligned text is to get a nice full length listRowSeparator
@@ -134,7 +135,7 @@ struct TransactionDetailView: View {
                     Text("Status: \(common.txState.major.localizedState)")
                 }    .listRowSeparator(.automatic)
                     .accessibilityFont(.title)
-                SwitchCase(transaction: $transaction, hasDone: doneAction != 
nil)
+                TypeDetail(transaction: $transaction, hasDone: doneAction != 
nil)
 
                 if transaction.isAbortable { if let abortAction {
                     TransactionButton(transactionId: common.transactionId,
@@ -192,10 +193,47 @@ struct TransactionDetailView: View {
 //}
 //
 //extension TransactionDetail {
-    struct SwitchCase: View {
+    struct KycButton: View {
+        let destination: URL
+        @AppStorage("iconOnly") var iconOnly: Bool = false
+
+        var body: some View {
+            VStack(alignment: .leading) {  // Show Hint that User must pass 
KYC on website
+                if !iconOnly {
+                    Text("You need to pass a KYC procedure")
+                        .fixedSize(horizontal: false, vertical: true)       // 
wrap in scrollview
+                        .multilineTextAlignment(.leading)                   // 
otherwise
+                        .listRowSeparator(.hidden)
+                }
+                Link("Open KYC website", destination: destination)
+                    .buttonStyle(TalerButtonStyle(type: .prominent, badge: 
NEEDS_KYC))
+                    .accessibilityHint("Will go to KYC website to confirm this 
withdrawal.")
+            }
+        }
+    }
+
+    struct ConfirmationButton: View {
+        let destination: URL
+        @AppStorage("iconOnly") var iconOnly: Bool = false
+
+        var body: some View {
+            VStack(alignment: .leading) {  // Show Hint that User should 
Confirm on bank website
+                if !iconOnly {
+                    Text("Waiting for bank confirmation")
+                        .fixedSize(horizontal: false, vertical: true)       // 
wrap in scrollview
+                        .multilineTextAlignment(.leading)                   // 
otherwise
+                        .listRowSeparator(.hidden)
+                }
+                Link("Confirm with bank", destination: destination)
+                    .buttonStyle(TalerButtonStyle(type: .prominent, badge: 
CONFIRM_BANK))
+                    .accessibilityHint("Will go to bank website to confirm 
this withdrawal.")
+            }
+        }
+    }
+
+    struct TypeDetail: View {
         @Binding var transaction: Transaction
         let hasDone: Bool
-        @AppStorage("iconOnly") var iconOnly: Bool = false
 
         var body: some View {
             let common = transaction.common
@@ -209,43 +247,25 @@ struct TransactionDetailView: View {
                     case .withdrawal(let withdrawalTransaction):
                         let details = withdrawalTransaction.details
                         if pending {
+                            if transaction.isPendingKYC {
+                                if let kycUrl = common.kycUrl {
+                                    if let destination = URL(string: kycUrl) {
+                                        KycButton(destination: destination)
+                                    }
+                                }
+                            }
                             let withdrawalDetails = details.withdrawalDetails
                             switch withdrawalDetails.type {
                                 case .manual:               // "Make a wire 
transfer of \(amount) to"
-                                    if transaction.isPendingKYC {
-                                        if let kycUrl = common.kycUrl {
-                                            if let destination = URL(string: 
kycUrl) {
-                                                VStack(alignment: .leading) {  
// Show Hint that User must pass KYC on website
-                                                    if !iconOnly {
-                                                        Text("You need to pass 
a KYC procedure")
-                                                            
.fixedSize(horizontal: false, vertical: true)       // wrap in scrollview
-                                                            
.multilineTextAlignment(.leading)                   // otherwise
-                                                            
.listRowSeparator(.hidden)
-                                                    }
-                                                    Link("Open KYC website", 
destination: destination)
-                                                        
.buttonStyle(TalerButtonStyle(type: .prominent, badge: NEEDS_KYC))
-                                                        
.accessibilityHint("Will go to KYC website to confirm this withdrawal.")
-                                                }
-                                            }
-                                        }
-                                    }
                                     ManualDetailsV(common: common, details: 
withdrawalDetails)
 
                                 case .bankIntegrated:       // "Confirm with 
bank"
-                                    let confirmed = 
withdrawalDetails.confirmed ?? false
-                                    if !confirmed {
-                                        if let confirmationUrl = 
withdrawalDetails.bankConfirmationUrl {
-                                            if let destination = URL(string: 
confirmationUrl) {
-                                                VStack(alignment: .leading) {  
// Show Hint that User should Confirm on bank website
-                                                    if !iconOnly {
-                                                        Text("Waiting for bank 
confirmation")
-                                                            
.fixedSize(horizontal: false, vertical: true)       // wrap in scrollview
-                                                            
.multilineTextAlignment(.leading)                   // otherwise
-                                                            
.listRowSeparator(.hidden)
-                                                    }
-                                                    Link("Confirm with bank", 
destination: destination)
-                                                        
.buttonStyle(TalerButtonStyle(type: .prominent, badge: CONFIRM_BANK))
-                                                        
.accessibilityHint("Will go to bank website to confirm this withdrawal.")
+                                    if !transaction.isPendingKYC {             
 // both should never happen, but...
+                                        let confirmed = 
withdrawalDetails.confirmed ?? false
+                                        if !confirmed {
+                                            if let confirmationUrl = 
withdrawalDetails.bankConfirmationUrl {
+                                                if let destination = 
URL(string: confirmationUrl) {
+                                                    
ConfirmationButton(destination: destination)
                                                 }
                                             }
                                         }
@@ -258,9 +278,7 @@ struct TransactionDetailView: View {
                     case .payment(let paymentTransaction):
                         let details = paymentTransaction.details
                         Text(details.info.summary)
-                            .accessibilityFont(.title2)
-                            .lineLimit(4)
-                            .padding(.bottom)
+                            .accessibilityFont(.title3)
                         ThreeAmountsSheet(common: common, topAbbrev: 
String(localized: "Pay:"),
                                         topTitle: String(localized: "Sum to be 
paid:"),
                                          baseURL: nil, large: true)     // 
TODO: baseURL
diff --git a/TalerWallet1/Views/Transactions/TransactionRowView.swift 
b/TalerWallet1/Views/Transactions/TransactionRowView.swift
index f4a6aa4..aafc403 100644
--- a/TalerWallet1/Views/Transactions/TransactionRowView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionRowView.swift
@@ -46,6 +46,8 @@ struct TransactionRowContentV: View {
     let done: Bool
     let foreColor:Color
 
+    @Environment(\.colorSchemeContrast) private var colorSchemeContrast
+
     public static func width(titles: (String, String?), isHorizontal: Bool,
                              sizeCategory: ContentSizeCategory) -> CGFloat {
         let imageFont = TalerFont.uiFont(.largeTitle)
@@ -74,16 +76,21 @@ struct TransactionRowContentV: View {
         let iconBadge = IconBadge(foreColor: foreColor, done: done, incoming: 
incoming,
                               shouldConfirm: shouldConfirm, needsKYC: needsKYC)
         let doneOrPending = done || pending
+        let increasedContrast = colorSchemeContrast == .increased
+        let textColor = doneOrPending ? .primary :
+                    increasedContrast ? .secondary : WalletColors().gray3
         HStack(spacing: 8) {
             iconBadge
             VStack(alignment: .leading) {
                 Text(centerTop)
-                    .strikethrough(!doneOrPending)
+                    .foregroundColor(textColor)
+                    .strikethrough(!doneOrPending, color: .red)
                     .accessibilityFont(.headline)
 //                  .fontWeight(.medium)      iOS 16
                     .padding(.bottom, -2.0)
+                    .accessibilityLabel(doneOrPending ? centerTop : centerTop 
+  ", canceled")
                 Text(centerBottom)
-                    .strikethrough(!doneOrPending)
+                    .foregroundColor(textColor)
                     .accessibilityFont(.callout)
             }
         }
@@ -190,10 +197,10 @@ extension Transaction {             // for PreViews
                                      merchant: merchant,
                                       summary: "some product summary",
                                      products: [])
-            let pDetails = PaymentTransactionDetails(proposalId: "some 
proposal ID",
-                                                 totalRefundRaw: 
Amount(currency: LONGCURRENCY, cent: 300),
-                                           totalRefundEffective: 
Amount(currency: LONGCURRENCY, cent: 280),
-                                                           info: info)
+            let pDetails = PaymentTransactionDetails(info: info,
+                                               proposalId: "some proposal ID",
+                                           totalRefundRaw: Amount(currency: 
LONGCURRENCY, cent: 300),
+                                     totalRefundEffective: Amount(currency: 
LONGCURRENCY, cent: 280))
             self = .payment(PaymentTransaction(common: common, details: 
pDetails))
         }
     }
diff --git a/TestFlight/WhatToTest.en-US.txt b/TestFlight/WhatToTest.en-US.txt
index b91d35f..452b0d7 100644
--- a/TestFlight/WhatToTest.en-US.txt
+++ b/TestFlight/WhatToTest.en-US.txt
@@ -1,4 +1,17 @@
 
+Version 0.9.3 (30)
+
+• New feature: 
+- Bugfix: Manual withdrawal works again (was broken in 29)
+- Bugfix: Currency input with zero fractional digits (test.taler.net)
+
+
+Version 0.9.3 (29)
+
+• New feature: ToS in Exchanges Tab
+- Bugfix: P2P Send works again (was broken in 28)
+
+
 Version 0.9.3 (28)
 
 • Unsuccessful (Incomplete) transactions no longer have their own list, but 
are now
@@ -14,7 +27,7 @@ Version 0.9.3 (27)
     Notice the difference in the Pending list
 • KYC in pending transactions accessible from details
 
-- BugFix: after P2P no QR code was shown
+- Bugfix: after P2P no QR code was shown
 - Layout improvements iOS16+. We'll continue to support iOS15, it just won't 
look so nice as on iOS 16+.
 
 
@@ -23,7 +36,7 @@ Version 0.9.3 (26)
 • New feature: KYC Support. But you won't get a second chance if you don't use 
the first one.
     (KYC will be added to pending transactions in the next version)
 
-- BugFix: With more than one exchange currencies were mixed in P2P
+- Bugfix: With more than one exchange currencies were mixed in P2P
 - The whole textfield can now be used to bring up the keyboard in P2P
 
 
@@ -32,7 +45,7 @@ Version 0.9.3 (25)
 • New feature: Shortcuts Buttons (50,25,10,5) for P2P + Withdrawal
 
 - CurrencyFormatter for P2P + Withdrawal
-- BugFix: Accessibility announcements always did reset the focus to start of 
view
+- Bugfix: Accessibility announcements always did reset the focus to start of 
view
 
 A/B test result: Nobody liked GNU Taler (with SideView & Hamburger-button) 
better,
 thus we concentrate on Taler Wallet (with TabBar)
@@ -43,7 +56,7 @@ Version 0.9.3 (24)
 • Sound & Haptics ON by default
 • Blue Taler Logo
 - Lots of Accessibility improvements
-- BugFix: P2P Transactions were started twice
+- Bugfix: P2P Transactions were started twice
 - Incomplete Transactions no longer show "Obtained" or "Paid"
 
 
@@ -60,7 +73,7 @@ Version 0.9.3 (22)
 
 Version 0.9.3 (21)
 
-- BugFix: P2P payments should work again
+- Bugfix: P2P payments should work again
 - CurrencyFormatter for Balance
 
 
diff --git a/taler-swift/Sources/taler-swift/Amount.swift 
b/taler-swift/Sources/taler-swift/Amount.swift
index 0d5a9ba..301f555 100644
--- a/taler-swift/Sources/taler-swift/Amount.swift
+++ b/taler-swift/Sources/taler-swift/Amount.swift
@@ -312,33 +312,43 @@ public final class Amount: Codable, Hashable, @unchecked 
Sendable, CustomStringC
 
     /// Multiplies by ten, then adds digit
     public func shiftLeft(add digit: UInt8, _ inputDigits: UInt) {
-        // how many digits to shift right (e.g. inputD=2 ==> shift:=6)
-        let shift = Self.fractionalBaseDigits - inputDigits
-        // mask to zero out fractions smaller than inputDigits
-        let shiftMask = fractionalBase(shift)
-
-        let carryMask = fractionalBase(Self.fractionalBaseDigits - 1)
-        // get biggest fractional digit
-        let carry = fraction / carryMask
-        var remainder = fraction % carryMask
-//        print("fraction: \(fraction) = \(carry) + \(remainder)")
-
-        let shiftedInt = integer * 10 + UInt64(carry)
-        if shiftedInt < Self.maxValue {
-            self.integer = shiftedInt
-//            print("remainder: \(remainder) / shiftMask \(shiftMask) = 
\(remainder / shiftMask)")
-            remainder = (remainder / shiftMask) * 10
-        } else { // will get too big
-            // Just swap the last significant digit for the one the user typed 
last
-            if shiftMask >= 10 {
-                remainder = (remainder / (shiftMask / 10)) * 10
+        if inputDigits > 0 {
+            // how many digits to shift right (e.g. inputD=2 ==> shift:=6)
+            let shift = Self.fractionalBaseDigits - inputDigits
+            // mask to zero out fractions smaller than inputDigits
+            let shiftMask = fractionalBase(shift)
+
+            let carryMask = fractionalBase(Self.fractionalBaseDigits - 1)
+            // get biggest fractional digit
+            let carry = fraction / carryMask
+            var remainder = fraction % carryMask
+//            print("fraction: \(fraction) = \(carry) + \(remainder)")
+
+            let shiftedInt = integer * 10 + UInt64(carry)
+            if shiftedInt < Self.maxValue {
+                self.integer = shiftedInt
+//                print("remainder: \(remainder) / shiftMask \(shiftMask) = 
\(remainder / shiftMask)")
+                remainder = (remainder / shiftMask) * 10
+            } else { // will get too big
+                     // Just swap the last significant digit for the one the 
user typed last
+                if shiftMask >= 10 {
+                    remainder = (remainder / (shiftMask / 10)) * 10
+                } else {
+                    remainder = (remainder / 10) * 10
+                }
+            }
+            let sum = remainder + UInt32(digit)
+            self.fraction = sum * shiftMask
+   //        print("(remainder: \(remainder) + \(digit)) * base(shift) 
\(shiftMask) = fraction \(fraction)")
+        } else {
+            let shiftedInt = integer * 10 + UInt64(digit)
+            if shiftedInt < Self.maxValue {
+                self.integer = shiftedInt
             } else {
-                remainder = (remainder / 10) * 10
+                self.integer = Self.maxValue
             }
+            self.fraction = 0
         }
-        let sum = remainder + UInt32(digit)
-        self.fraction = sum * shiftMask
-//        print("(remainder: \(remainder) + \(digit)) * base(shift) 
\(shiftMask) = fraction \(fraction)")
     }
 
     /// Sets all fractional digits after inputDigits to 0

-- 
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]