gnunet-svn
[Top][All Lists]
Advanced

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

[taler-taler-ios] branch master updated (e79881c -> b01070b)


From: gnunet
Subject: [taler-taler-ios] branch master updated (e79881c -> b01070b)
Date: Sun, 19 Nov 2023 23:53:24 +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 e79881c  Bump version to 0.9.3 (25)
     new 5083da8  Bugfix: Arithmetic error
     new d019233  Use "nu" (instead of "te") for Null
     new ad9c376  Dismiss keyboard
     new 6fec1ca  cleanup, debugging
     new f26e277  ScrollVStack, cleanup
     new a914a93  Notifications
     new e4ce634  KYC
     new 95f7ff6  Expired
     new 4d66a94  Bugfix: set currency in buttonAction
     new cbcdd34  Remove PendingOps
     new 0a21140  cleanup
     new f3cfa6f  leadingCurrencySymbol
     new 8fbf2b4  cleanup
     new f7750a6  needsKYC badge
     new 5d877d3  ScrollVStack
     new fbf878e  ShortcutButton
     new 2f43f42  accessibilityAddTraits
     new a9f3701  cleanup
     new 0df6b05  tabbed button strings
     new a1a5c84  ViewThatFits instead of own computations, iconBadge
     new dedf6a3  fix shortcuts
     new c0cf846  DD51 for fee
     new aca4f65  unify P2P subject
     new 820783b  cleanup P2P
     new da2fe9b  cleanup
     new 74336e7  badge for button
     new c9ab503  Remove PendingOps
     new 4d8abc6  fixup ScrollVStack
     new 9f7e6e5  cleanup  notifications
     new b01070b  Bump version to 0.9.3 (27)

The 30 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              |  54 ++-----
 TalerWallet1/Backend/WalletCore.swift              |  59 +++++--
 TalerWallet1/Controllers/DebugViewC.swift          |  10 +-
 TalerWallet1/Controllers/PublicConstants.swift     |  14 +-
 TalerWallet1/Helper/CurrencySpecification.swift    |  34 +++-
 TalerWallet1/Helper/TalerStrings.swift             |   9 ++
 TalerWallet1/Helper/View+dismissTop.swift          |   7 +
 TalerWallet1/Model/Model+Exchange.swift            |  18 ---
 TalerWallet1/Model/Model+Withdraw.swift            |   4 +-
 TalerWallet1/Model/Transaction.swift               |  36 ++---
 TalerWallet1/Model/WalletModel.swift               |   6 +-
 TalerWallet1/Views/Balances/BalanceRowView.swift   |   3 +-
 .../Views/Balances/BalancesSectionView.swift       |  35 ++--
 TalerWallet1/Views/Balances/PendingRowView.swift   | 137 ++++++----------
 TalerWallet1/Views/Balances/TwoRowButtons.swift    |  19 ++-
 TalerWallet1/Views/Exchange/ManualWithdraw.swift   |  22 +--
 TalerWallet1/Views/Exchange/QuiteSomeCoins.swift   |  41 +++--
 TalerWallet1/Views/HelperViews/AmountRowV.swift    |  19 +--
 TalerWallet1/Views/HelperViews/Buttons.swift       |  67 ++++----
 TalerWallet1/Views/HelperViews/CurrencyField.swift |  70 +++++---
 .../Views/HelperViews/CurrencyInputView.swift      | 135 +++++++++++++---
 TalerWallet1/Views/HelperViews/SelectDays.swift    |   8 +-
 TalerWallet1/Views/Main/MainView.swift             |  31 ++--
 .../Peer2peer/{SendDoneV.swift => P2PReadyV.swift} |  30 ++--
 TalerWallet1/Views/Peer2peer/P2PSubjectV.swift     | 121 ++++++++++++++
 TalerWallet1/Views/Peer2peer/RequestPayment.swift  |  65 +++++---
 TalerWallet1/Views/Peer2peer/RequestPurpose.swift  | 106 ------------
 TalerWallet1/Views/Peer2peer/SendAmount.swift      | 100 +++++++-----
 TalerWallet1/Views/Peer2peer/SendPurpose.swift     | 131 ---------------
 .../Views/Settings/Pending/PendingOpView.swift     |  63 --------
 .../Settings/Pending/PendingOpsListView.swift      |  47 ------
 TalerWallet1/Views/Settings/SettingsItem.swift     |  10 +-
 TalerWallet1/Views/Settings/SettingsView.swift     |  37 ++---
 .../WithdrawBankIntegrated/WithdrawURIView.swift   |  16 +-
 .../Views/Transactions/ManualDetailsV.swift        |  10 +-
 .../Views/Transactions/TransactionDetailView.swift | 179 ++++++++++++---------
 .../Views/Transactions/TransactionRowView.swift    |  37 ++++-
 TestFlight/WhatToTest.en-US.txt                    |  21 +++
 taler-swift/Sources/taler-swift/Amount.swift       |   7 +-
 39 files changed, 917 insertions(+), 901 deletions(-)
 rename TalerWallet1/Views/Peer2peer/{SendDoneV.swift => P2PReadyV.swift} (91%)
 create mode 100644 TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
 delete mode 100644 TalerWallet1/Views/Peer2peer/RequestPurpose.swift
 delete mode 100644 TalerWallet1/Views/Peer2peer/SendPurpose.swift
 delete mode 100644 TalerWallet1/Views/Settings/Pending/PendingOpView.swift
 delete mode 100644 TalerWallet1/Views/Settings/Pending/PendingOpsListView.swift

diff --git a/TalerWallet.xcodeproj/project.pbxproj 
b/TalerWallet.xcodeproj/project.pbxproj
index 242ba39..999c516 100644
--- a/TalerWallet.xcodeproj/project.pbxproj
+++ b/TalerWallet.xcodeproj/project.pbxproj
@@ -62,12 +62,11 @@
                4E3EAE422A990778009F1BE8 /* KeyboardResponder.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EAD117529F672FA008EDD0B /* 
KeyboardResponder.swift */; };
                4E3EAE432A990778009F1BE8 /* TransactionRowView.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EB095302989CBFE0043A8A1 /* 
TransactionRowView.swift */; };
                4E3EAE442A990778009F1BE8 /* PublicConstants.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EA1ABBD29A3833A008821EA /* 
PublicConstants.swift */; };
-               4E3EAE452A990778009F1BE8 /* SendDoneV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB3136029FEE79B007D68BC /* SendDoneV.swift */; 
};
+               4E3EAE452A990778009F1BE8 /* P2PReadyV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB3136029FEE79B007D68BC /* P2PReadyV.swift */; 
};
                4E3EAE462A990778009F1BE8 /* TextFieldAlert.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EB095482989CBFE0043A8A1 /* 
TextFieldAlert.swift */; };
                4E3EAE472A990778009F1BE8 /* QuiteSomeCoins.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EBA82AC2A3F580500E5F39A /* 
QuiteSomeCoins.swift */; };
                4E3EAE482A990778009F1BE8 /* PayTemplateView.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EBA56402A7FF5200084948B /* 
PayTemplateView.swift */; };
                4E3EAE492A990778009F1BE8 /* ManualWithdrawDone.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EB431662A1E55C700C5690E /* 
ManualWithdrawDone.swift */; };
-               4E3EAE4A2A990778009F1BE8 /* RequestPurpose.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4E9320462A164BC700A87B0E /* 
RequestPurpose.swift */; };
                4E3EAE4B2A990778009F1BE8 /* ShareSheet.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E753A072A0B6A5F002D9328 /* ShareSheet.swift */; 
};
                4E3EAE4C2A990778009F1BE8 /* AmountView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095492989CBFE0043A8A1 /* AmountView.swift */; 
};
                4E3EAE4D2A990778009F1BE8 /* P2pAcceptDone.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E3B4BC22A42252300CC88B8 /* P2pAcceptDone.swift 
*/; };
@@ -95,14 +94,12 @@
                4E3EAE632A990778009F1BE8 /* WalletCore.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB0951C2989CBCB0043A8A1 /* WalletCore.swift */; 
};
                4E3EAE642A990778009F1BE8 /* LaunchAnimationView.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4EB095432989CBFE0043A8A1 /* 
LaunchAnimationView.swift */; };
                4E3EAE652A990778009F1BE8 /* SideBarView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095422989CBFE0043A8A1 /* SideBarView.swift 
*/; };
-               4E3EAE662A990778009F1BE8 /* PendingOpView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB0954D2989CBFE0043A8A1 /* PendingOpView.swift 
*/; };
-               4E3EAE672A990778009F1BE8 /* PendingOpsListView.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EB0954E2989CBFE0043A8A1 /* 
PendingOpsListView.swift */; };
                4E3EAE682A990778009F1BE8 /* WalletModel.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095112989CBB00043A8A1 /* WalletModel.swift 
*/; };
                4E3EAE692A990778009F1BE8 /* URLSheet.swift in Sources */ = {isa 
= PBXBuildFile; fileRef = 4EB095332989CBFE0043A8A1 /* URLSheet.swift */; };
                4E3EAE6A2A990778009F1BE8 /* ThreeAmountsV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4ED2F94A2A278F5100453B40 /* ThreeAmountsV.swift 
*/; };
                4E3EAE6B2A990778009F1BE8 /* Model+Withdraw.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EB0953D2989CBFE0043A8A1 /* 
Model+Withdraw.swift */; };
                4E3EAE6C2A990778009F1BE8 /* ExchangeSectionView.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4EC90C772A1B528B0071DC58 /* 
ExchangeSectionView.swift */; };
-               4E3EAE6D2A990778009F1BE8 /* SendPurpose.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E7940DD29FC307C00A9AEA1 /* SendPurpose.swift 
*/; };
+               4E3EAE6D2A990778009F1BE8 /* P2PSubjectV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E7940DD29FC307C00A9AEA1 /* P2PSubjectV.swift 
*/; };
                4E3EAE6E2A990778009F1BE8 /* Model+P2P.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4ECB627F2A0BA6DF004ABBB7 /* Model+P2P.swift */; 
};
                4E3EAE6F2A990778009F1BE8 /* TalerStrings.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095072989CB7C0043A8A1 /* TalerStrings.swift 
*/; };
                4E3EAE702A990778009F1BE8 /* CurrencyInputView.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EA551242A2C923600FEC9A8 /* 
CurrencyInputView.swift */; };
@@ -157,7 +154,7 @@
                4E6EDD872A363D8D0031D520 /* ListStyle.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E6EDD862A363D8D0031D520 /* ListStyle.swift */; 
};
                4E753A062A0952F8002D9328 /* DebugViewC.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E753A052A0952F7002D9328 /* DebugViewC.swift */; 
};
                4E753A082A0B6A5F002D9328 /* ShareSheet.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E753A072A0B6A5F002D9328 /* ShareSheet.swift */; 
};
-               4E7940DE29FC307C00A9AEA1 /* SendPurpose.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E7940DD29FC307C00A9AEA1 /* SendPurpose.swift 
*/; };
+               4E7940DE29FC307C00A9AEA1 /* P2PSubjectV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E7940DD29FC307C00A9AEA1 /* P2PSubjectV.swift 
*/; };
                4E87C8732A31CB7F001C6406 /* TransactionsEmptyView.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4E87C8722A31CB7F001C6406 /* 
TransactionsEmptyView.swift */; };
                4E87C8752A34B411001C6406 /* IncompleteRowV.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4E87C8742A34B411001C6406 /* 
IncompleteRowV.swift */; };
                4E8C17202A6509BB005B2392 /* 
Atkinson-Hyperlegible-Regular-102.otf in Resources */ = {isa = PBXBuildFile; 
fileRef = 4E8C171C2A6509BB005B2392 /* Atkinson-Hyperlegible-Regular-102.otf */; 
};
@@ -167,7 +164,6 @@
                4E8E25332A1CD39700A27BFA /* EqualIconWidthDomain.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4E8E25322A1CD39700A27BFA /* 
EqualIconWidthDomain.swift */; };
                4E9320432A14F6EA00A87B0E /* WalletColors.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E9320422A14F6EA00A87B0E /* WalletColors.swift 
*/; };
                4E9320452A1645B600A87B0E /* RequestPayment.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4E9320442A1645B600A87B0E /* 
RequestPayment.swift */; };
-               4E9320472A164BC700A87B0E /* RequestPurpose.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4E9320462A164BC700A87B0E /* 
RequestPurpose.swift */; };
                4E9796902A3765ED006F73BC /* AgePicker.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E97968F2A3765ED006F73BC /* AgePicker.swift */; 
};
                4E983C292ADBDD3500FA9CC5 /* SingleAxisGeometryReader.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4E983C282ADBDD3500FA9CC5 /* 
SingleAxisGeometryReader.swift */; };
                4E983C2A2ADBDD3500FA9CC5 /* SingleAxisGeometryReader.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4E983C282ADBDD3500FA9CC5 /* 
SingleAxisGeometryReader.swift */; };
@@ -225,9 +221,7 @@
                4EB0956C2989CBFE0043A8A1 /* AmountView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095492989CBFE0043A8A1 /* AmountView.swift */; 
};
                4EB0956D2989CBFE0043A8A1 /* LoadingView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB0954A2989CBFE0043A8A1 /* LoadingView.swift 
*/; };
                4EB0956E2989CBFE0043A8A1 /* Model+Pending.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift 
*/; };
-               4EB0956F2989CBFE0043A8A1 /* PendingOpView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB0954D2989CBFE0043A8A1 /* PendingOpView.swift 
*/; };
-               4EB095702989CBFE0043A8A1 /* PendingOpsListView.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EB0954E2989CBFE0043A8A1 /* 
PendingOpsListView.swift */; };
-               4EB3136129FEE79B007D68BC /* SendDoneV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB3136029FEE79B007D68BC /* SendDoneV.swift */; 
};
+               4EB3136129FEE79B007D68BC /* P2PReadyV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB3136029FEE79B007D68BC /* P2PReadyV.swift */; 
};
                4EB431672A1E55C700C5690E /* ManualWithdrawDone.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EB431662A1E55C700C5690E /* 
ManualWithdrawDone.swift */; };
                4EBA563F2A7FD9390084948B /* SuperScriptDigits.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EBA563E2A7FD9390084948B /* 
SuperScriptDigits.swift */; };
                4EBA56412A7FF5200084948B /* PayTemplateView.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EBA56402A7FF5200084948B /* 
PayTemplateView.swift */; };
@@ -333,7 +327,7 @@
                4E753A042A08E720002D9328 /* transactions.json */ = {isa = 
PBXFileReference; lastKnownFileType = text.json; path = transactions.json; 
sourceTree = "<group>"; };
                4E753A052A0952F7002D9328 /* DebugViewC.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= DebugViewC.swift; sourceTree = "<group>"; };
                4E753A072A0B6A5F002D9328 /* ShareSheet.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= ShareSheet.swift; sourceTree = "<group>"; };
-               4E7940DD29FC307C00A9AEA1 /* SendPurpose.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= SendPurpose.swift; sourceTree = "<group>"; };
+               4E7940DD29FC307C00A9AEA1 /* P2PSubjectV.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= P2PSubjectV.swift; sourceTree = "<group>"; };
                4E7CFD372A532CE100CBAFF3 /* WhatToTest.en-US.txt */ = {isa = 
PBXFileReference; lastKnownFileType = text; path = "WhatToTest.en-US.txt"; 
sourceTree = "<group>"; };
                4E87C8722A31CB7F001C6406 /* TransactionsEmptyView.swift */ = 
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.swift; path = TransactionsEmptyView.swift; sourceTree = "<group>"; };
                4E87C8742A34B411001C6406 /* IncompleteRowV.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= IncompleteRowV.swift; sourceTree = "<group>"; };
@@ -344,7 +338,6 @@
                4E8E25322A1CD39700A27BFA /* EqualIconWidthDomain.swift */ = 
{isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
EqualIconWidthDomain.swift; sourceTree = "<group>"; };
                4E9320422A14F6EA00A87B0E /* WalletColors.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= WalletColors.swift; sourceTree = "<group>"; };
                4E9320442A1645B600A87B0E /* RequestPayment.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= RequestPayment.swift; sourceTree = "<group>"; };
-               4E9320462A164BC700A87B0E /* RequestPurpose.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= RequestPurpose.swift; sourceTree = "<group>"; };
                4E97968F2A3765ED006F73BC /* AgePicker.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= AgePicker.swift; sourceTree = "<group>"; };
                4E983C282ADBDD3500FA9CC5 /* SingleAxisGeometryReader.swift */ = 
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.swift; path = SingleAxisGeometryReader.swift; sourceTree = 
"<group>"; };
                4E983C2B2ADC416800FA9CC5 /* View+fitsSideBySide.swift */ = {isa 
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; 
path = "View+fitsSideBySide.swift"; sourceTree = "<group>"; };
@@ -400,9 +393,7 @@
                4EB095492989CBFE0043A8A1 /* AmountView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= AmountView.swift; sourceTree = "<group>"; };
                4EB0954A2989CBFE0043A8A1 /* LoadingView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= LoadingView.swift; sourceTree = "<group>"; };
                4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= "Model+Pending.swift"; sourceTree = "<group>"; };
-               4EB0954D2989CBFE0043A8A1 /* PendingOpView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= PendingOpView.swift; sourceTree = "<group>"; };
-               4EB0954E2989CBFE0043A8A1 /* PendingOpsListView.swift */ = {isa 
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; 
path = PendingOpsListView.swift; sourceTree = "<group>"; };
-               4EB3136029FEE79B007D68BC /* SendDoneV.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= SendDoneV.swift; sourceTree = "<group>"; };
+               4EB3136029FEE79B007D68BC /* P2PReadyV.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= P2PReadyV.swift; sourceTree = "<group>"; };
                4EB431662A1E55C700C5690E /* ManualWithdrawDone.swift */ = {isa 
= PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
ManualWithdrawDone.swift; sourceTree = "<group>"; };
                4EBA563E2A7FD9390084948B /* SuperScriptDigits.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
SuperScriptDigits.swift; sourceTree = "<group>"; };
                4EBA56402A7FF5200084948B /* PayTemplateView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= PayTemplateView.swift; sourceTree = "<group>"; };
@@ -658,7 +649,6 @@
                                4EB095252989CBFE0043A8A1 /* SettingsView.swift 
*/,
                                4EC400882AE3E7E800DF72C7 /* AboutView.swift */,
                                4EB095262989CBFE0043A8A1 /* SettingsItem.swift 
*/,
-                               4EB0954B2989CBFE0043A8A1 /* Pending */,
                        );
                        path = Settings;
                        sourceTree = "<group>";
@@ -759,23 +749,13 @@
                        path = HelperViews;
                        sourceTree = "<group>";
                };
-               4EB0954B2989CBFE0043A8A1 /* Pending */ = {
-                       isa = PBXGroup;
-                       children = (
-                               4EB0954E2989CBFE0043A8A1 /* 
PendingOpsListView.swift */,
-                               4EB0954D2989CBFE0043A8A1 /* PendingOpView.swift 
*/,
-                       );
-                       path = Pending;
-                       sourceTree = "<group>";
-               };
                4ECB627E2A0BA4DA004ABBB7 /* Peer2peer */ = {
                        isa = PBXGroup;
                        children = (
                                4E40E0BD29F25ABB00B85369 /* SendAmount.swift */,
-                               4E7940DD29FC307C00A9AEA1 /* SendPurpose.swift 
*/,
-                               4EB3136029FEE79B007D68BC /* SendDoneV.swift */,
                                4E9320442A1645B600A87B0E /* 
RequestPayment.swift */,
-                               4E9320462A164BC700A87B0E /* 
RequestPurpose.swift */,
+                               4E7940DD29FC307C00A9AEA1 /* P2PSubjectV.swift 
*/,
+                               4EB3136029FEE79B007D68BC /* P2PReadyV.swift */,
                        );
                        path = Peer2peer;
                        sourceTree = "<group>";
@@ -1085,12 +1065,11 @@
                                4E3EAE422A990778009F1BE8 /* 
KeyboardResponder.swift in Sources */,
                                4E3EAE432A990778009F1BE8 /* 
TransactionRowView.swift in Sources */,
                                4E3EAE442A990778009F1BE8 /* 
PublicConstants.swift in Sources */,
-                               4E3EAE452A990778009F1BE8 /* SendDoneV.swift in 
Sources */,
+                               4E3EAE452A990778009F1BE8 /* P2PReadyV.swift in 
Sources */,
                                4E3EAE462A990778009F1BE8 /* 
TextFieldAlert.swift in Sources */,
                                4E3EAE472A990778009F1BE8 /* 
QuiteSomeCoins.swift in Sources */,
                                4E3EAE482A990778009F1BE8 /* 
PayTemplateView.swift in Sources */,
                                4E3EAE492A990778009F1BE8 /* 
ManualWithdrawDone.swift in Sources */,
-                               4E3EAE4A2A990778009F1BE8 /* 
RequestPurpose.swift in Sources */,
                                4E3EAE4B2A990778009F1BE8 /* ShareSheet.swift in 
Sources */,
                                4EC4008F2AE8019700DF72C7 /* 
ExchangeRowView.swift in Sources */,
                                4E3EAE4C2A990778009F1BE8 /* AmountView.swift in 
Sources */,
@@ -1124,14 +1103,12 @@
                                4E3EAE632A990778009F1BE8 /* WalletCore.swift in 
Sources */,
                                4E3EAE642A990778009F1BE8 /* 
LaunchAnimationView.swift in Sources */,
                                4E3EAE652A990778009F1BE8 /* SideBarView.swift 
in Sources */,
-                               4E3EAE662A990778009F1BE8 /* PendingOpView.swift 
in Sources */,
-                               4E3EAE672A990778009F1BE8 /* 
PendingOpsListView.swift in Sources */,
                                4E3EAE682A990778009F1BE8 /* WalletModel.swift 
in Sources */,
                                4E3EAE692A990778009F1BE8 /* URLSheet.swift in 
Sources */,
                                4E3EAE6A2A990778009F1BE8 /* ThreeAmountsV.swift 
in Sources */,
                                4E3EAE6B2A990778009F1BE8 /* 
Model+Withdraw.swift in Sources */,
                                4E3EAE6C2A990778009F1BE8 /* 
ExchangeSectionView.swift in Sources */,
-                               4E3EAE6D2A990778009F1BE8 /* SendPurpose.swift 
in Sources */,
+                               4E3EAE6D2A990778009F1BE8 /* P2PSubjectV.swift 
in Sources */,
                                4E3EAE6E2A990778009F1BE8 /* Model+P2P.swift in 
Sources */,
                                4E3EAE6F2A990778009F1BE8 /* TalerStrings.swift 
in Sources */,
                                4E3EAE702A990778009F1BE8 /* 
CurrencyInputView.swift in Sources */,
@@ -1194,12 +1171,11 @@
                                4EAD117629F672FA008EDD0B /* 
KeyboardResponder.swift in Sources */,
                                4EB095572989CBFE0043A8A1 /* 
TransactionRowView.swift in Sources */,
                                4EA1ABBE29A3833A008821EA /* 
PublicConstants.swift in Sources */,
-                               4EB3136129FEE79B007D68BC /* SendDoneV.swift in 
Sources */,
+                               4EB3136129FEE79B007D68BC /* P2PReadyV.swift in 
Sources */,
                                4EB0956B2989CBFE0043A8A1 /* 
TextFieldAlert.swift in Sources */,
                                4EBA82AD2A3F580500E5F39A /* 
QuiteSomeCoins.swift in Sources */,
                                4EBA56412A7FF5200084948B /* 
PayTemplateView.swift in Sources */,
                                4EB431672A1E55C700C5690E /* 
ManualWithdrawDone.swift in Sources */,
-                               4E9320472A164BC700A87B0E /* 
RequestPurpose.swift in Sources */,
                                4E753A082A0B6A5F002D9328 /* ShareSheet.swift in 
Sources */,
                                4EC400902AE8019700DF72C7 /* 
ExchangeRowView.swift in Sources */,
                                4EB0956C2989CBFE0043A8A1 /* AmountView.swift in 
Sources */,
@@ -1233,14 +1209,12 @@
                                4EB095202989CBCB0043A8A1 /* WalletCore.swift in 
Sources */,
                                4EB095672989CBFE0043A8A1 /* 
LaunchAnimationView.swift in Sources */,
                                4EB095662989CBFE0043A8A1 /* SideBarView.swift 
in Sources */,
-                               4EB0956F2989CBFE0043A8A1 /* PendingOpView.swift 
in Sources */,
-                               4EB095702989CBFE0043A8A1 /* 
PendingOpsListView.swift in Sources */,
                                4EB095162989CBB00043A8A1 /* WalletModel.swift 
in Sources */,
                                4EB0955A2989CBFE0043A8A1 /* URLSheet.swift in 
Sources */,
                                4ED2F94B2A278F5100453B40 /* ThreeAmountsV.swift 
in Sources */,
                                4EB095622989CBFE0043A8A1 /* 
Model+Withdraw.swift in Sources */,
                                4EC90C782A1B528B0071DC58 /* 
ExchangeSectionView.swift in Sources */,
-                               4E7940DE29FC307C00A9AEA1 /* SendPurpose.swift 
in Sources */,
+                               4E7940DE29FC307C00A9AEA1 /* P2PSubjectV.swift 
in Sources */,
                                4ECB62802A0BA6DF004ABBB7 /* Model+P2P.swift in 
Sources */,
                                4EB0950A2989CB7C0043A8A1 /* TalerStrings.swift 
in Sources */,
                                4EA551252A2C923600FEC9A8 /* 
CurrencyInputView.swift in Sources */,
@@ -1496,7 +1470,7 @@
                                CODE_SIGN_ENTITLEMENTS = 
"$(TARGET_NAME).entitlements";
                                CODE_SIGN_IDENTITY = "Apple Development";
                                CODE_SIGN_STYLE = Automatic;
-                               CURRENT_PROJECT_VERSION = 25;
+                               CURRENT_PROJECT_VERSION = 27;
                                DEVELOPMENT_TEAM = GUDDQ9428Y;
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
@@ -1538,7 +1512,7 @@
                                CODE_SIGN_ENTITLEMENTS = 
"$(TARGET_NAME).entitlements";
                                CODE_SIGN_IDENTITY = "Apple Development";
                                CODE_SIGN_STYLE = Automatic;
-                               CURRENT_PROJECT_VERSION = 25;
+                               CURRENT_PROJECT_VERSION = 27;
                                DEVELOPMENT_TEAM = GUDDQ9428Y;
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
diff --git a/TalerWallet1/Backend/WalletCore.swift 
b/TalerWallet1/Backend/WalletCore.swift
index 350f194..8c8ebde 100644
--- a/TalerWallet1/Backend/WalletCore.swift
+++ b/TalerWallet1/Backend/WalletCore.swift
@@ -133,7 +133,7 @@ extension WalletCore {
         }
         do {
             let jsonData = try JSONEncoder().encode(result)
-            symLog.log(result)
+            symLog.log("id:\(requestId)  \(result)")
 //            logger.info(result)   TODO: log result
             completion(requestId, timeSent, jsonData, nil)
         } catch {        // JSON encoding of response.result failed / should 
never happen
@@ -184,26 +184,51 @@ extension WalletCore {
     @MainActor private func handleStateTransition(_ jsonData: Data) throws {
         do {
             let decoded = try JSONDecoder().decode(TransactionTransition.self, 
from: jsonData)
-            if decoded.newTxState != decoded.oldTxState {
-                let components = decoded.transactionId.components(separatedBy: 
":")
-                if components.count >= 3 {  // txn:$txtype:$uid
-                    if let type = TransactionType(rawValue: components[1]) {
-                        guard type != .refresh else { return }
-                        if decoded.newTxState.major == .done {
+            guard decoded.newTxState != decoded.oldTxState else {
+                // TODO: Same state usually means that an error is transmitted
+                logger.info("No State change: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
+                return
+            }
+            let components = decoded.transactionId.components(separatedBy: ":")
+            if components.count >= 3 {  // txn:$txtype:$uid
+                if let type = TransactionType(rawValue: components[1]) {
+                    guard type != .refresh else { return }
+                    switch decoded.newTxState.major {
+                        case .done:
                             logger.info("Done: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
                             Controller.shared.playSound(type.isIncoming ? 2 : 
1)
-                        } else if decoded.newTxState.major == .expired {
+                            postNotification(.TransactionDone, userInfo: 
[TRANSACTIONTRANSITION: decoded])
+                            return
+                        case .expired:
                             logger.log("Expired: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
                             Controller.shared.playSound(0)
-                        }
-                        postNotification(.TransactionStateTransition,
-                                         userInfo: [TRANSACTIONTRANSITION: 
decoded])
-                    }
-                }
-            } else {
-                // TODO: Same state usually means that an error is transmitted
-                logger.info("No State change: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
-            }
+                            postNotification(.TransactionExpired, userInfo: 
[TRANSACTIONTRANSITION: decoded])
+                            return
+                        case .pending:
+                            if let newMinor = decoded.newTxState.minor {
+                                if newMinor == .ready {
+                                    logger.log("PendingReady: 
\(decoded.transactionId, privacy: .private(mask: .hash))")
+                                    postNotification(.PendingReady, userInfo: 
[TRANSACTIONTRANSITION: decoded])
+                                    return
+                                } else if newMinor == .exchangeWaitReserve     
 // user did confirm on bank website
+                                       || newMinor == .withdrawCoins {         
 // coin-withdrawal has started
+                                    logger.log("DismissSheet: 
\(decoded.transactionId, privacy: .private(mask: .hash))")
+                                    postNotification(.DismissSheet, userInfo: 
[TRANSACTIONTRANSITION: decoded])
+                                    return
+                                } else if newMinor == .kyc {       // user did 
confirm on bank website, but KYC is needed
+                                    logger.log("KYCrequired: 
\(decoded.transactionId, privacy: .private(mask: .hash))")
+                                    postNotification(.KYCrequired, userInfo: 
[TRANSACTIONTRANSITION: decoded])
+                                    return
+                                }
+                            }
+                            logger.log("Pending: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
+                            postNotification(.TransactionStateTransition, 
userInfo: [TRANSACTIONTRANSITION: decoded])
+                        default:
+                            logger.log("Yikes: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
+                            postNotification(.TransactionStateTransition, 
userInfo: [TRANSACTIONTRANSITION: decoded])
+                    } // switch
+                } // type
+            } // 3 components
         } catch {       // rethrows
             symLog.log(jsonData)       // TODO: .error
             throw WalletBackendError.deserializationError
diff --git a/TalerWallet1/Controllers/DebugViewC.swift 
b/TalerWallet1/Controllers/DebugViewC.swift
index 2c18cce..b135dfd 100644
--- a/TalerWallet1/Controllers/DebugViewC.swift
+++ b/TalerWallet1/Controllers/DebugViewC.swift
@@ -52,15 +52,15 @@ public let VIEW_DEPOSIT_ACCEPT = VIEW_DEPOSIT + 2           
        // 42
 
 // MARK: P2P Send Coins (from Balances)
 // push debit to another wallet ==> shows QR code to be scanned / link to be 
sent by mail or messenger
-public let VIEW_SEND_P2P = VIEW_DEPOSIT + 10                        // 50 Send 
Coins
+public let VIEW_P2P_SEND = VIEW_DEPOSIT + 10                        // 50 Send 
Coins
 //public let VIEW_SEND_TOS                                          // - user 
already accepted the ToS at withdrawal, invoice or receive
-public let VIEW_SEND_PURPOSE = VIEW_SEND_P2P + 2                    // 52 Send 
Purpose
+public let VIEW_P2P_SUBJECT = VIEW_P2P_SEND + 2                     // 52 Send 
/ Request Subject
+public let VIEW_P2P_READY = VIEW_P2P_SEND + 3                       // 53 Send 
/ Request Ready 
 
 // MARK: P2P Private Receive (from Balances)
 // pull credit from another wallet ==> shows QR code to be scanned / link to 
be sent by mail or messenger
-public let VIEW_REQUEST_P2P = VIEW_SEND_P2P + 10                    // 60 
Request Amount
-public let VIEW_REQUEST_TOS = VIEW_REQUEST_P2P + 1                  // 61 
Request ToS
-public let VIEW_REQUEST_PURPOSE = VIEW_REQUEST_TOS + 1              // 62 
Request Purpose
+public let VIEW_P2P_REQUEST = VIEW_P2P_SEND + 10                    // 60 
Request Amount
+public let VIEW_P2P_TOS = VIEW_P2P_REQUEST + 1                      // 61 
Request ToS
 
 // MARK: P2P Business Invoice (from Balances)
 // TBD
diff --git a/TalerWallet1/Controllers/PublicConstants.swift 
b/TalerWallet1/Controllers/PublicConstants.swift
index ddcd126..1b5c9ae 100644
--- a/TalerWallet1/Controllers/PublicConstants.swift
+++ b/TalerWallet1/Controllers/PublicConstants.swift
@@ -13,7 +13,9 @@ public let ONEDAY: UInt = 1         // 1..3
 public let SEVENDAYS: UInt = 7      // 3..9
 public let THIRTYDAYS: UInt = 30    // 10..30
 
-public let EMPTYSTRING = ""                        // avoid automatic 
translation of empty "" textLiterals in Text()
+public let EMPTYSTRING = ""                         // avoid automatic 
translation of empty "" textLiterals in Text()
+public let CONFIRM_BANK = "circle.fill"             // badge in PendingRow, 
TransactionRow and TransactionDetail
+public let NEEDS_KYC = "star.fill"                  // badge in PendingRow, 
TransactionRow and TransactionDetail
 public let HTTPS = "https://";
 //public let DEMOBANK = HTTPS + "bAnK.dEmO.tAlEr.nEt"             // should be 
weird to read, but still work
 //public let DEMOEXCHANGE = HTTPS + "eXcHaNgE.dEmO.tAlEr.nEt"
@@ -46,12 +48,20 @@ public let EXCHANGEBASEURL = "exchangeBaseUrl"
 public let TALERURI = "talerUri"
 
 public let TRANSACTIONTRANSITION = "transactionTransition"
+public let TRANSACTIONID = "transactionID"
 
 /// Notifications sent by wallet-core
 extension Notification.Name {
     static let BalanceChange = Notification.Name("balance-change")
-    static let TransactionStateTransition = 
Notification.Name(TransactionTransition.TransitionType.transition.rawValue)
     static let ExchangeAdded = Notification.Name("exchange-added")
+    static let TransactionStateTransition = 
Notification.Name(TransactionTransition.TransitionType.transition.rawValue)
+    static let TransactionDone = Notification.Name("transaction-done")
+    static let TransactionExpired = Notification.Name("transaction-expired")
+    static let PendingReady = Notification.Name("pending-ready")
+    static let WaitReserve = Notification.Name("wait-reserve")
+    static let WithdrawCoins = Notification.Name("withdraw-coins")
+    static let KYCrequired = Notification.Name("kyc-required")
+    static let DismissSheet = Notification.Name("dismiss-sheet")
     static let PendingOperationProcessed = 
Notification.Name("pending-operation-processed")
     static let ReserveNotYetFound = Notification.Name("reserve-not-yet-found")
 //    static let WithdrawalGroupBankConfirmed = 
Notification.Name("withdrawal-group-bank-confirmed")
diff --git a/TalerWallet1/Helper/CurrencySpecification.swift 
b/TalerWallet1/Helper/CurrencySpecification.swift
index 1cac315..812a962 100644
--- a/TalerWallet1/Helper/CurrencySpecification.swift
+++ b/TalerWallet1/Helper/CurrencySpecification.swift
@@ -5,10 +5,23 @@
 import Foundation
 import taler_swift
 
+extension Locale {
+    func leadingCurrencySymbol() -> Bool {
+        let currencyFormatter = NumberFormatter()
+        currencyFormatter.numberStyle = .currency
+        currencyFormatter.locale = self
+
+        let positiveFormat = currencyFormatter.positiveFormat as NSString
+        let currencySymbolLocation = positiveFormat.range(of: "¤").location
+
+        return currencySymbolLocation == 0
+    }
+}
+
 extension Amount {
-    func string(_ currencyInfo: CurrencyInfo?) -> String {
+    func string(_ currencyInfo: CurrencyInfo?, useSymbol: Bool = true) -> 
String {
         if let currencyInfo {
-            return currencyInfo.string(for: valueAsFloatTuple)
+            return currencyInfo.string(for: valueAsFloatTuple, useSymbol: 
useSymbol)
         } else {
             return valueStr
         }
@@ -46,10 +59,9 @@ public struct CurrencyInfo {
                          fractionalInputDigits: 0,
                         fractionalNormalDigits: 0,
                   fractionalTrailingZeroDigits: 0,
-                                  altUnitNames: [0 : "テ"])
-        return CurrencyInfo(scope: scope, specs: specs,
-                        formatter: CurrencyFormatter.formatter(scope: scope,
-                                                               specs: specs))
+                                  altUnitNames: [0 : "ヌ"])  // use `nu´ for 
Null
+        let formatter = CurrencyFormatter.formatter(scope: scope, specs: specs)
+        return CurrencyInfo(scope: scope, specs: specs, formatter: formatter)
     }
 
     /// returns all characters left from the decimalSeparator
@@ -58,7 +70,7 @@ public struct CurrencyInfo {
             // decimalSeparator was found ==> return all characters left of it
             return String(integerStr[..<integerIndex])
         }
-        guard let firstChar = integerStr.first else { return "" }    // TODO: 
should NEVER happen! Show error
+        guard let firstChar = integerStr.first else { return "0" }    // TODO: 
should NEVER happen! Show error
         let digitSet = CharacterSet.decimalDigits
         if digitSet.contains(firstChar) {
             // Currency Symbol is after the amount ==> return only the digits
@@ -78,11 +90,12 @@ public struct CurrencyInfo {
         return nil
     }
 
+    // TODO: use valueAsDecimalTuple instead of valueAsFloatTuple
     func string(for valueTuple: (Double, Double), useSymbol: Bool = true) -> 
String {
         formatter.setUseSymbol(useSymbol)
         let (integer, fraction) = valueTuple
         if let integerStr = formatter.string(for: integer) {
-            if fraction == 0 { return integerStr }              // TODO: add 
trailing zeroes
+            if fraction == 0 { return integerStr }              // formatter 
already added trailing zeroes
             if let fractionStr = formatter.string(for: fraction) {
                 if let decimalSeparator = formatter.currencyDecimalSeparator {
                     if let fractionIndex = fractionStr.endIndex(of: 
decimalSeparator) {
@@ -168,6 +181,7 @@ public struct CurrencySpecification: Codable, Sendable {
 public class CurrencyFormatter: NumberFormatter {
 
     var hasAltUnitName0: Bool   // specs.altUnitNames[0] should have the 
Symbol ($,€,¥)
+    var leadingCurrencySymbol: Bool
     /// factory
     static func formatter(scope: ScopeInfo, specs: CurrencySpecification) -> 
CurrencyFormatter {
         let formatter = CurrencyFormatter()
@@ -182,6 +196,7 @@ public class CurrencyFormatter: NumberFormatter {
 
     public override init() {
         self.hasAltUnitName0 = false
+        self.leadingCurrencySymbol = false
         super.init()
         self.locale = Locale.current
         self.usesGroupingSeparator = true
@@ -196,6 +211,9 @@ public class CurrencyFormatter: NumberFormatter {
 //        self.groupingSize = 3                 // thousands
 //        self.groupingSeparator = ","
 //        self.decimalSeparator = "."
+        let positiveFormat = self.positiveFormat as NSString
+        let currencySymbolLocation = positiveFormat.range(of: "¤").location
+        self.leadingCurrencySymbol = currencySymbolLocation == 0
     }
 
     func setUseSymbol(_ useSymbol: Bool) {
diff --git a/TalerWallet1/Helper/TalerStrings.swift 
b/TalerWallet1/Helper/TalerStrings.swift
index 117b5ee..fa1fcd2 100644
--- a/TalerWallet1/Helper/TalerStrings.swift
+++ b/TalerWallet1/Helper/TalerStrings.swift
@@ -40,6 +40,15 @@ extension StringProtocol {
 }
 
 extension String {
+    func tabbed(oneLine: Bool) -> String {
+        let fragments = self.components(separatedBy: "\t")
+        if fragments.count > 1 {
+            let separator = oneLine ? " " : "\n"
+            return fragments.joined(separator: separator)
+        }
+        return self
+    }
+
     func widthOfString(usingUIFont font: UIFont) -> CGFloat {
         let fontAttributes = [NSAttributedString.Key.font: font]
         let size = self.size(withAttributes: fontAttributes)
diff --git a/TalerWallet1/Helper/View+dismissTop.swift 
b/TalerWallet1/Helper/View+dismissTop.swift
index 46ea8c7..3d11bd0 100644
--- a/TalerWallet1/Helper/View+dismissTop.swift
+++ b/TalerWallet1/Helper/View+dismissTop.swift
@@ -42,3 +42,10 @@ extension View {
         }
     }
 }
+// MARK: -
+extension View {
+    @MainActor
+    static func endEditing() {
+        UIApplication.shared.windows.forEach { $0.endEditing(true) }
+    }
+}
diff --git a/TalerWallet1/Model/Model+Exchange.swift 
b/TalerWallet1/Model/Model+Exchange.swift
index 032ab24..46aeb6c 100644
--- a/TalerWallet1/Model/Model+Exchange.swift
+++ b/TalerWallet1/Model/Model+Exchange.swift
@@ -61,24 +61,6 @@ struct Exchange: Codable, Hashable, Identifiable {
     }
 }
 
-struct ExchangeListItem: Codable, Hashable {
-    var exchangeBaseUrl: String
-    var currency: String
-    var paytoUris: [String]
-
-    public static func == (lhs: ExchangeListItem, rhs: ExchangeListItem) -> 
Bool {
-        return lhs.exchangeBaseUrl == rhs.exchangeBaseUrl &&
-        lhs.currency == rhs.currency &&
-        lhs.paytoUris == rhs.paytoUris
-    }
-
-    public func hash(into hasher: inout Hasher) {
-        hasher.combine(exchangeBaseUrl)
-        hasher.combine(currency)
-        hasher.combine(paytoUris)
-    }
-}
-
 struct ExchangeUrlItem: Codable, Hashable, Identifiable {
     var exchangeBaseUrl: String
     var id: String { exchangeBaseUrl }
diff --git a/TalerWallet1/Model/Model+Withdraw.swift 
b/TalerWallet1/Model/Model+Withdraw.swift
index 5b5fe8f..e86f394 100644
--- a/TalerWallet1/Model/Model+Withdraw.swift
+++ b/TalerWallet1/Model/Model+Withdraw.swift
@@ -11,8 +11,8 @@ fileprivate let ASYNCDELAY: UInt = 0   //set e.g to 6 or 9 
seconds for debugging
 /// The result from getWithdrawalDetailsForUri
 struct WithdrawUriInfoResponse: Decodable {
     var amount: Amount
-    var defaultExchangeBaseUrl: String?             // TODO: might be nil 
❗️Yikes
-    var possibleExchanges: [ExchangeListItem]       // TODO: query these for 
fees?
+    var defaultExchangeBaseUrl: String?
+    var possibleExchanges: [Exchange]       // TODO: query these for fees?
 }
 /// A request to get an exchange's withdrawal details.
 fileprivate struct GetWithdrawalDetailsForURI: WalletBackendFormattedRequest {
diff --git a/TalerWallet1/Model/Transaction.swift 
b/TalerWallet1/Model/Transaction.swift
index 307e548..3f7c69e 100644
--- a/TalerWallet1/Model/Transaction.swift
+++ b/TalerWallet1/Model/Transaction.swift
@@ -97,6 +97,7 @@ struct TransactionTransition: Codable {             // 
Notification
     var oldTxState: TransactionState
     var newTxState: TransactionState
     var transactionId: String
+    var experimentalUserData: String?       // KYC
 }
 
 enum TxAction: String, Codable {
@@ -436,6 +437,8 @@ enum Transaction: Decodable, Hashable, Identifiable, 
Sendable {
     var isP2pIncoming: Bool { isSendInvoice || isRcvCoins}
 
     var isPending    : Bool { common.txState.major == .pending }
+    var isPendingReady : Bool { common.txState.minor == .ready }
+    var isPendingKYC   : Bool { common.txState.minor == .kyc }
     var isDone       : Bool { common.txState.major == .done }
     var isAborting   : Bool { common.txState.major == .aborting }
     var isAborted    : Bool { common.txState.major == .aborted }
@@ -456,27 +459,23 @@ enum Transaction: Decodable, Hashable, Identifiable, 
Sendable {
         switch self {
             case .withdrawal(let withdrawalTransaction):
                 let details = withdrawalTransaction.details.withdrawalDetails
-                return details.type == .bankIntegrated // && 
details.bankConfirmationUrl != nil
-            default:
-                return false
+                guard details.bankConfirmationUrl != nil else { return false }
+                if let confirmed = details.confirmed {
+                    return details.type == .bankIntegrated && confirmed == 
false
+                }
+            default: break
         }
+        return false
     }
     var common: TransactionCommon {
-        switch self {
-            case .dummy(let dummyTransaction):
-                return dummyTransaction.common
-            case .withdrawal(let withdrawalTransaction):
-                return withdrawalTransaction.common
-            case .payment(let paymentTransaction):
-                return paymentTransaction.common
-            case .refund(let refundTransaction):
-                return refundTransaction.common
-            case .reward(let rewardTransaction):
-                return rewardTransaction.common
-            case .refresh(let refreshTransaction):
-                return refreshTransaction.common
-            case .peer2peer(let p2pTransaction):
-                return p2pTransaction.common
+        return switch self {
+            case .dummy(let dummyTransaction):           
dummyTransaction.common
+            case .withdrawal(let withdrawalTransaction): 
withdrawalTransaction.common
+            case .payment(let paymentTransaction):       
paymentTransaction.common
+            case .refund(let refundTransaction):         
refundTransaction.common
+            case .reward(let rewardTransaction):         
rewardTransaction.common
+            case .refresh(let refreshTransaction):       
refreshTransaction.common
+            case .peer2peer(let p2pTransaction):         p2pTransaction.common
         }
     }
 
@@ -502,7 +501,6 @@ enum Transaction: Decodable, Hashable, Identifiable, 
Sendable {
                 result["summary"] = p2pTransaction.details.info.summary
                 result[TALERURI] = p2pTransaction.details.talerUri ?? ""
         }
-
         return result
     }
 }
diff --git a/TalerWallet1/Model/WalletModel.swift 
b/TalerWallet1/Model/WalletModel.swift
index 588003b..428af7e 100644
--- a/TalerWallet1/Model/WalletModel.swift
+++ b/TalerWallet1/Model/WalletModel.swift
@@ -122,8 +122,9 @@ extension WalletModel {
     }
 
     private func docPath (sqlite3: Bool) throws -> String {
-        if let documentsUrl = FileManager.default.urls(for: 
.documentDirectory, in: .userDomainMask).first {
-            let documentUrl = documentsUrl.appendingPathComponent(DATABASE, 
isDirectory: false)
+//        if let documentsDir = FileManager.default.urls(for: 
.applicationSupportDirectory, in: .userDomainMask).first {
+        if let documentsDir = FileManager.default.urls(for: 
.documentDirectory, in: .userDomainMask).first {
+            let documentUrl = documentsDir.appendingPathComponent(DATABASE, 
isDirectory: false)
                                           .appendingPathExtension(sqlite3 ? 
"sqlite3" : "json")
             let docPath = documentUrl.path
             logger.debug("\(docPath)")
@@ -174,3 +175,4 @@ extension WalletModel {
         _ = try await sendRequest(request, 0)
     }
 }
+// MARK: -
diff --git a/TalerWallet1/Views/Balances/BalanceRowView.swift 
b/TalerWallet1/Views/Balances/BalanceRowView.swift
index 1c78853..241c503 100644
--- a/TalerWallet1/Views/Balances/BalanceRowView.swift
+++ b/TalerWallet1/Views/Balances/BalanceRowView.swift
@@ -10,8 +10,8 @@ struct BalanceButton: View {
     let sizeCategory: ContentSizeCategory
     let rowAction: () -> Void
 
-    @AppStorage("iconOnly") var iconOnly: Bool = false
     @Environment(\.colorSchemeContrast) private var colorSchemeContrast
+    @AppStorage("iconOnly") var iconOnly: Bool = false
 //    @AppStorage("moreContrast") var moreContrast: Bool = false
 
     var body: some View {
@@ -51,6 +51,7 @@ struct BalanceRowView: View {
     let sendAction: () -> Void
     let recvAction: () -> Void
     let rowAction: () -> Void
+
     @Environment(\.sizeCategory) var sizeCategory
     @EnvironmentObject private var controller: Controller
     @AppStorage("iconOnly") var iconOnly: Bool = false
diff --git a/TalerWallet1/Views/Balances/BalancesSectionView.swift 
b/TalerWallet1/Views/Balances/BalancesSectionView.swift
index 5d58670..e7c390e 100644
--- a/TalerWallet1/Views/Balances/BalancesSectionView.swift
+++ b/TalerWallet1/Views/Balances/BalancesSectionView.swift
@@ -44,21 +44,18 @@ struct BalancesSectionView {
         let currency = balance.scopeInfo.currency
         transactions = await model.transactionsT(stack.push(), currency: 
currency)
         completedTransactions = WalletModel.completedTransactions(transactions)
-//        sectionID = UUID()
     }
 
     func reloadPending(_ stack: CallStack) async -> () {
         let currency = balance.scopeInfo.currency
         transactions = await model.transactionsT(stack.push(), currency: 
currency)
         pendingTransactions = WalletModel.pendingTransactions(transactions)
-//        sectionID = UUID()
     }
 
     func reloadIncomplete(_ stack: CallStack) async -> () {
         let currency = balance.scopeInfo.currency
         transactions = await model.transactionsT(stack.push(), currency: 
currency)
         incompleteTransactions = 
WalletModel.incompleteTransactions(transactions)
-//        sectionID = UUID()
     }
 }
 
@@ -78,13 +75,14 @@ extension BalancesSectionView: View {
                     .accessibilityFont(.body)
                     .multilineTextAlignment(.leading)
             }
-            BalancesNavigationLinksView(stack: stack.push(),
-                                      balance: balance,
-                             amountToTransfer: $amountToTransfer,           // 
does still have the wrong currency
-                                      summary: $summary,
-                        completedTransactions: $completedTransactions,
-                              reloadAllAction: reloadCompleted,
-                              reloadOneAction: reloadOneAction)
+            BalancesNavigationLinksView(symLog: symLog,
+                                         stack: stack.push(),
+                                       balance: balance,
+                              amountToTransfer: $amountToTransfer,      // 
does still have the wrong currency
+                                       summary: $summary,
+                         completedTransactions: $completedTransactions,
+                               reloadAllAction: reloadCompleted,
+                               reloadOneAction: reloadOneAction)
             if pendingTransactions.count > 0 {
                 BalancesPendingRowView(symLog: symLog,
                                         stack: stack.push(),
@@ -195,11 +193,11 @@ fileprivate struct BalancesPendingRowView: View {
             VStack(spacing: 6) {
                 var rows = 0
                 if !pendingIncoming.isZero {
-                    PendingRowView(amount: pendingIncoming, incoming: true)
+                    PendingRowView(amount: pendingIncoming, incoming: true, 
shouldConfirm: false, needsKYC: false)     // TODO: !!!
                     let _ = (rows+=1)
                 }
                 if !pendingOutgoing.isZero {
-                    PendingRowView(amount: pendingOutgoing, incoming: false)
+                    PendingRowView(amount: pendingOutgoing, incoming: false, 
shouldConfirm: false, needsKYC: false)
                     let _ = (rows+=1)
                 }
                 if rows == 0 {
@@ -213,6 +211,7 @@ fileprivate struct BalancesPendingRowView: View {
 } // BalancesPendingRowView
 
 fileprivate struct BalancesNavigationLinksView: View {
+    let symLog: SymLogV?
     let stack: CallStack
     let balance: Balance
 //    let sectionCount: Int
@@ -226,6 +225,10 @@ fileprivate struct BalancesNavigationLinksView: View {
     @State private var buttonSelected: Int? = nil
 
     func selectAndUpdate(_ button: Int) {
+        let currency = balance.scopeInfo.currency
+        amountToTransfer.setCurrency(currency)
+        symLog?.log("balance.scopeInfo.currency: \(currency)")
+
         buttonSelected = button      // will trigger NavigationLink
         // while navigation animation runs, contact Exchange to update Fees
 //        Task { // runs on MainActor
@@ -237,14 +240,8 @@ fileprivate struct BalancesNavigationLinksView: View {
 //        }
     }
 
-    func setCurrency() -> String {
-        let currency = balance.scopeInfo.currency
-        amountToTransfer.setCurrency(currency)
-        return currency
-    }
-
     var body: some View {
-        let currency = setCurrency()                        // update currency 
in amountToTransfer
+        let currency = balance.scopeInfo.currency
         HStack(spacing: 0) {
             NavigationLink(destination: LazyView {
                 SendAmount(stack: stack.push(),
diff --git a/TalerWallet1/Views/Balances/PendingRowView.swift 
b/TalerWallet1/Views/Balances/PendingRowView.swift
index 9549c40..d6153f6 100644
--- a/TalerWallet1/Views/Balances/PendingRowView.swift
+++ b/TalerWallet1/Views/Balances/PendingRowView.swift
@@ -5,105 +5,70 @@
 import SwiftUI
 import taler_swift
 
-struct PendingRowContentV: View {
-    let titles: (String, String?)
-    let isHorizontal: Bool
-    let incoming: Bool
-
-    public static func width(titles: (String, String?), isHorizontal: Bool,
-                             sizeCategory: ContentSizeCategory) -> CGFloat {
-        let imageFont = TalerFont.uiFont(.largeTitle)
-        let uiFont = TalerFont.uiFont(.body)
-
-        let image = "++"
-        let imageWidth = image.widthOfString(usingUIFont: imageFont, 
sizeCategory) + 8.0     // spacing: 8
-        let (title1, title2) = titles
-        let title1Width = title1.widthOfString(usingUIFont: uiFont, 
sizeCategory)
-        var title2Width = 0.0
-        var totalWidth = title1Width
-        if let title2 {
-            title2Width = title2.widthOfString(usingUIFont: uiFont, 
sizeCategory)
-            let totalStr = title1 + " " + title2
-            totalWidth = totalStr.widthOfString(usingUIFont: uiFont, 
sizeCategory)
-        }
-
-//    let logStr = String(format: "image: %.2f   title: %.2f   total: %.2f", 
imageWidth, max(title1Width, title2Width), totalWidth)
-//    print(logStr)
-        return imageWidth + (isHorizontal ? totalWidth
-                                          : max(title1Width, title2Width))
-    }
-
-    var body: some View {
-        let pendingColor = WalletColors().pendingColor(incoming)
-        let imageName = incoming ? "plus.diamond"       //  goforward.plus   
circle
-                                 : "minus.diamond"      //
-        HStack(spacing: 8) {
-            Image(systemName: imageName)
-                .foregroundColor(pendingColor)
-                .accessibilityFont(.largeTitle)
-                .accessibility(hidden: true)
-
-            Text(title(titles, isHorizontal))
-                .lineLimit(4)
-                .accessibilityFont(.body)
-        }
-    }
-}
-
 /// This view shows a pending transaction row in a currency section
 struct PendingRowView: View {
     let amount: Amount
     let incoming: Bool
+    let shouldConfirm: Bool
+    let needsKYC: Bool
 
     @Environment(\.sizeCategory) var sizeCategory
     @EnvironmentObject private var controller: Controller
     @AppStorage("iconOnly") var iconOnly: Bool = false
 
     let inTitle0 = String(localized: "TitleIncoming_Short", defaultValue: 
"Incoming",
-                            comment: "Abbreviation of `Pending incoming´ in 
Balances")
-    let inTitle1 = String(localized: "TitleIncoming_Top", defaultValue: 
"Pending",
-                            comment: "Top (first half) of line `Pending 
incoming´ in Balances")
-    let inTitle2 = String(localized: "TitleIncoming_Bottom", defaultValue: 
"incoming",
-                            comment: "Bottom (second half) of line `Pending 
incoming´ in Balances")
-    let outTitle0 = String(localized: "TitleOutgoing_Short", defaultValue: 
"Outgoing",
-                             comment: "Abbreviation of `Pending outgoing´ in 
Balances")
-    let outTitle1 = String(localized: "TitleOutgoing_Top", defaultValue: 
"Pending",
-                             comment: "Top (first half) of line `Pending 
outgoing´ in Balances")
-    let outTitle2 = String(localized: "TitleOutgoing_Bottom", defaultValue: 
"outgoing",
-                             comment: "Bottom (second half) of line `Pending 
outgoing´ in Balances")
+                          comment: "Abbreviation of `Pending incoming´ in 
Balances")
+    let inTitle1 = String(localized: "TitleIncoming_Full", defaultValue: 
"Pending\tincoming",
+                          comment: "`Pending incoming´ in Balances - set 
exactly 1 \t for line break")
 
-    func needVStack(available: CGFloat, contentWidth: CGFloat, valueWidth: 
CGFloat) -> Bool {
-        if available > 20 {
-            let logStr = String(format: "available: %.2f   sum: %.2f = 
content: %.2f  + value: %.2f",
-                                available, contentWidth + valueWidth, 
contentWidth, valueWidth)
-            print(logStr)
-        }
-        return available < (contentWidth + valueWidth + 20)
-    }
+    let outTitle0 = String(localized: "TitleOutgoing_Short", defaultValue: 
"Outgoing",
+                           comment: "Abbreviation of `Pending outgoing´ in 
Balances")
+    let outTitle1 = String(localized: "TitleOutgoing_Full", defaultValue: 
"Pending\toutgoing",
+                           comment: "`Pending outgoing´ in Balances - set 
exactly 1 \t for line break")
 
     var body: some View {
         let pendingColor = WalletColors().pendingColor(incoming)
-        SingleAxisGeometryReader { width in
-            Group {
-                let currencyInfo = controller.info(for: amount.currencyStr, 
controller.currencyTicker)
-                let amountStr = amount.string(currencyInfo)
-                let amountWidth = amountStr.width(largeAmountFont: false, 
sizeCategory)
-                let inTitle = iconOnly ? (inTitle0, nil)
-                                       : (inTitle1, inTitle2)
-                let outTitle = iconOnly ? (outTitle0, nil)
-                                        : (outTitle1, outTitle2)
-                let title = incoming ? inTitle
-                                     : outTitle
-                let contentWidth = PendingRowContentV.width(titles: incoming ? 
inTitle : outTitle,
-                                                      isHorizontal: false, 
sizeCategory: sizeCategory)
+        let imageName = incoming ? "plus.diamond"       //  goforward.plus   
circle
+                                 : "minus.diamond"      //
+        let iconBadge = IconBadge(imageName: imageName, foreColor: 
pendingColor, shouldConfirm: shouldConfirm, needsKYC: needsKYC)
+
+        let inTitle = iconOnly ? inTitle0 : inTitle1
+        let outTitle = iconOnly ? outTitle0 : outTitle1
+        let pendingTitle = incoming ? inTitle : outTitle
 
-                let needVStack = needVStack(available: width, contentWidth: 
contentWidth, valueWidth: amountWidth)
-                AmountRowV(amountStr: amountStr, amountColor: pendingColor, 
largeAmountFont: false,
-                           fitsHorizontal: !needVStack, vertAlignment: 
.center) {
-                    // isHorizontal=true to try to fit "- Pending outgoing" + 
amount in 1 line
-                    PendingRowContentV(titles: title, isHorizontal: true, 
incoming: incoming)
+        let currencyInfo = controller.info(for: amount.currencyStr, 
controller.currencyTicker)
+        let amountText = Text(amount.string(currencyInfo))
+            .foregroundColor(pendingColor)
+            .accessibilityFont(.title2)
+            .monospacedDigit()
+
+        // this is the default view for iOS 15
+        let vStack = VStack {
+            Text(pendingTitle.tabbed(oneLine: true))
+            HStack {
+                iconBadge
+                Spacer(minLength: 0)
+                amountText
+            }
+        }
+
+        if #available(iOS 16.0, *) {
+            ViewThatFits(in: .horizontal) {
+                HStack {
+                    iconBadge
+                    Text(pendingTitle.tabbed(oneLine: false))
+                    Spacer(minLength: 0)
+                    amountText
+                }
+                vStack
+                VStack {
+                    Text(pendingTitle.tabbed(oneLine: true))
+                    iconBadge
+                    amountText
                 }
             }
+        } else {
+            vStack
         }
     }
 }
@@ -117,8 +82,8 @@ func PreviewCurrencyInfo(_ currency: String, digits: Int) -> 
CurrencyInfo {
                     fractionalNormalDigits: digits,
               fractionalTrailingZeroDigits: digits,
                               altUnitNames: [0 : unitName])
-    let formatter = CurrencyFormatter.formatter(scope: scope, specs: specs)
-    return CurrencyInfo(scope: scope, specs: specs, formatter: formatter)
+    let previewFormatter = CurrencyFormatter.formatter(scope: scope, specs: 
specs)
+    return CurrencyInfo(scope: scope, specs: specs, formatter: 
previewFormatter)
 }
 
 @MainActor
@@ -127,8 +92,8 @@ fileprivate struct Preview_Content: View {
         let test = Amount(currency: TESTCURRENCY, cent: 123)
         let demo = Amount(currency: DEMOCURRENCY, cent: 123456)
         List {
-            PendingRowView(amount: test, incoming: true)
-            PendingRowView(amount: demo, incoming: false)
+            PendingRowView(amount: test, incoming: true, shouldConfirm: true, 
needsKYC: false)
+            PendingRowView(amount: demo, incoming: false, shouldConfirm: 
false, needsKYC: true)
         }
     }
 }
diff --git a/TalerWallet1/Views/Balances/TwoRowButtons.swift 
b/TalerWallet1/Views/Balances/TwoRowButtons.swift
index 423f4fd..20d87c8 100644
--- a/TalerWallet1/Views/Balances/TwoRowButtons.swift
+++ b/TalerWallet1/Views/Balances/TwoRowButtons.swift
@@ -6,13 +6,14 @@ import SwiftUI
 import taler_swift
 
 extension View {
-    func title(_ titles: (String, String?), _ fitsSideBySide: Bool) -> String {
-        let delimiter = fitsSideBySide ? "\n" : " "
+    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
+            return (title1 + delimiter + title2, title1 + space + title2)
         } else {
-            return title1
+            return (title1, title1)
         }
     }
 }
@@ -24,18 +25,22 @@ struct TwoRowButtons: View {
     let sendDisabled: Bool
     let sendAction: () -> Void
     let recvAction: () -> Void
+
     @Environment(\.sizeCategory) var sizeCategory
-    
 
     var body: some View {
         Group {
-            Button(title(sendTitles, fitsSideBySide), action: sendAction)
+            let sendAccTitles = accessTitles(sendTitles, fitsSideBySide)
+            Button(sendAccTitles.0, action: sendAction)
+                .accessibilityLabel(Text(sendAccTitles.1))
                 .lineLimit(lineLimit)
                 .disabled(sendDisabled)
                 .buttonStyle(TalerButtonStyle(type: .bordered,
                                             dimmed: false,
                                            aligned: .center))
-            Button(title(recvTitles, fitsSideBySide), action: recvAction)
+            let recvAccTitles = accessTitles(recvTitles, fitsSideBySide)
+            Button(recvAccTitles.0, action: recvAction)
+                .accessibilityLabel(Text(recvAccTitles.1))
                 .lineLimit(lineLimit)
                 .disabled(false)
                 .buttonStyle(TalerButtonStyle(type: .bordered,
diff --git a/TalerWallet1/Views/Exchange/ManualWithdraw.swift 
b/TalerWallet1/Views/Exchange/ManualWithdraw.swift
index 505b55d..dc33b2c 100644
--- a/TalerWallet1/Views/Exchange/ManualWithdraw.swift
+++ b/TalerWallet1/Views/Exchange/ManualWithdraw.swift
@@ -14,6 +14,7 @@ struct ManualWithdraw: View {
     let exchange: Exchange
     @Binding var amountToTransfer: Amount
 
+    @EnvironmentObject private var controller: Controller
     @EnvironmentObject private var model: WalletModel
     @AppStorage("iconOnly") var iconOnly: Bool = false
 
@@ -26,19 +27,22 @@ struct ManualWithdraw: View {
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
-        let currency = exchange.currency ?? String(localized: "Unknown", 
comment: "unknown currency")
+        let currency = exchange.scopeInfo?.currency ?? exchange.currency ?? 
String(localized: "Unknown", comment: "unknown currency")
+        let currencyInfo = controller.info(for: currency, 
controller.currencyTicker)
         let navTitle = String(localized: "NavTitle_Withdraw (currency)", 
defaultValue: "Withdraw \(currency)")
 //        let agePicker = AgePicker(ageMenuList: $ageMenuList, selectedAge: 
$selectedAge)
 
-        ScrollView {
-            VStack {
+        ScrollView { VStack {
                 CurrencyInputView(amount: $amountToTransfer,
+                               available: nil,
                                    title: iconOnly ? String(localized: "How 
much:")
-                                                   : String(localized: "Amount 
to withdraw:"),
-                           shortcutLabel: String(localized: "Withdraw", 
comment: "VoiceOver: Withdraw $50,$25,$10,$5 shortcut buttons"))
+                                                   : String(localized: "Amount 
to withdraw:"))
                 let someCoins = SomeCoins(details: withdrawalAmountDetails)
-                QuiteSomeCoins(someCoins: someCoins, shouldShowFee: true,
-                               currency: currency, amountEffective: 
withdrawalAmountDetails?.amountEffective)
+                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)
@@ -70,9 +74,7 @@ struct ManualWithdraw: View {
                         }
                     }
                 } // disabled
-                Spacer()
-            }
-        }
+        } } // ScrollVStack
         .frame(maxWidth: .infinity, alignment: .leading)
         .padding(.horizontal)
         .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
diff --git a/TalerWallet1/Views/Exchange/QuiteSomeCoins.swift 
b/TalerWallet1/Views/Exchange/QuiteSomeCoins.swift
index f1e5589..8e046a2 100644
--- a/TalerWallet1/Views/Exchange/QuiteSomeCoins.swift
+++ b/TalerWallet1/Views/Exchange/QuiteSomeCoins.swift
@@ -14,8 +14,17 @@ struct SomeCoins {
     var quiteSome: Bool { numCoins > 199 }
     var tooMany:   Bool { numCoins > 999 }
 
-    let fee: String
-    var hasFee:    Bool { fee.count > 0 }
+    let fee: Amount?
+    func feeLabel(_ currencyInfo: CurrencyInfo?) -> String {
+       return if let fee {
+                invalid ? "Amount too small!"
+              : tooMany ? "Amount too big for a single withdrawal!"
+           : fee.isZero ? "No withdrawal fee"
+                        : "- \(fee.string(currencyInfo)) fee"
+       } else {
+           EMPTYSTRING
+       }
+    }
 }
 
 extension SomeCoins {
@@ -23,26 +32,26 @@ extension SomeCoins {
         do {
             if let details {
                 // Incoming: fee = raw - effective
-                let fee = try details.amountRaw - details.amountEffective
+                let aFee = try details.amountRaw - details.amountEffective
                 self.init(numCoins: details.numCoins ?? -1,    // either the 
number of coins, or unknown
-                               fee: fee.isZero ? "" : fee.readableDescription)
+                               fee: aFee)
                 return
             }
         } catch {}
-        self.init(numCoins: 0, fee:"")      // invalid
+        self.init(numCoins: 0, fee: nil)      // invalid
     }
 
     init(details: CheckPeerPullCreditResponse?) {
         do {
             if let details {
                 // Incoming: fee = raw - effective
-                let fee = try details.amountRaw - details.amountEffective
+                let aFee = try details.amountRaw - details.amountEffective
                 self.init(numCoins: details.numCoins ?? -1,    // either the 
number of coins, or unknown
-                               fee: fee.isZero ? "" : fee.readableDescription)
+                               fee: aFee)
                 return
             }
         } catch {}
-        self.init(numCoins: 0, fee:"")      // invalid
+        self.init(numCoins: 0, fee: nil)      // invalid
     }
 }
 // MARK: -
@@ -51,6 +60,7 @@ struct QuiteSomeCoins: View {
     let someCoins: SomeCoins
     let shouldShowFee: Bool
     let currency: String
+    let currencyInfo: CurrencyInfo?
     let amountEffective: Amount?
 
     var body: some View {
@@ -67,22 +77,21 @@ struct QuiteSomeCoins: View {
             }
         }
         if shouldShowFee {
-            Text(someCoins.invalid ? "Amount too small!"
-               : someCoins.tooMany ? "Amount too big for a single withdrawal!"
-                : someCoins.hasFee ? "- \(someCoins.fee) fee"
-                                   : "No withdrawal fee")
-            .foregroundColor((someCoins.invalid || someCoins.tooMany || 
someCoins.hasFee) ? .red : .primary)
-            .accessibilityFont(.body)
-//            .padding(4)
+            if let fee = someCoins.fee {
+                Text(someCoins.feeLabel(currencyInfo))
+                    .foregroundColor((someCoins.invalid || someCoins.tooMany 
|| !fee.isZero) ? .red : .primary)
+                    .accessibilityFont(.body)
+            }
         }
     }
 }
 // MARK: -
 struct QuiteSomeCoins_Previews: PreviewProvider {
     static var previews: some View {
-        QuiteSomeCoins(someCoins: SomeCoins(numCoins: 4, fee: "20 " + 
LONGCURRENCY),
+        QuiteSomeCoins(someCoins: SomeCoins(numCoins: 4, fee: Amount(currency: 
LONGCURRENCY, cent: 20) ),
                    shouldShowFee: true,
                         currency: LONGCURRENCY,
+                    currencyInfo: nil,
                  amountEffective: nil)
     }
 }
diff --git a/TalerWallet1/Views/HelperViews/AmountRowV.swift 
b/TalerWallet1/Views/HelperViews/AmountRowV.swift
index eda77f1..8c981f1 100644
--- a/TalerWallet1/Views/HelperViews/AmountRowV.swift
+++ b/TalerWallet1/Views/HelperViews/AmountRowV.swift
@@ -19,26 +19,23 @@ struct AmountRowV<Content: View>: View {
 
     var content: () -> Content
 
-
     var body: some View {
+        let text = Text(amountStr)
+            .foregroundColor(amountColor)
+            .accessibilityFont(largeAmountFont ? .title : .title2)
+            .monospacedDigit()
         if fitsHorizontal {
             HStack(alignment: vertAlignment, spacing: 0) {
                 content()
                 Spacer(minLength: 0)
-                Text(amountStr)
-                    .foregroundColor(amountColor)
-                    .accessibilityFont(largeAmountFont ? .title : .title2)
-                    .monospacedDigit()
+                text
             }
         } else {
             VStack(alignment: .leading, spacing: 0) {
                 content()
                 HStack {
                     Spacer(minLength: 0)
-                    Text(amountStr)
-                        .foregroundColor(amountColor)
-                        .accessibilityFont(largeAmountFont ? .title : .title2)
-                        .monospacedDigit()
+                    text
                 }
             }
         }
@@ -47,7 +44,7 @@ struct AmountRowV<Content: View>: View {
 // MARK: -
 #if  DEBUG
 
-struct SectionWithAmountRow: View {
+struct PreviewSectionWithAmountRow: View {
     @Environment(\.sizeCategory) var sizeCategory
     @Environment(\.colorSchemeContrast) private var colorSchemeContrast
 
@@ -92,6 +89,6 @@ struct SectionWithAmountRow: View {
 }
 
 #Preview {
-    SectionWithAmountRow()
+    PreviewSectionWithAmountRow()
 }
 #endif
diff --git a/TalerWallet1/Views/HelperViews/Buttons.swift 
b/TalerWallet1/Views/HelperViews/Buttons.swift
index f221ac6..9ab4a4b 100644
--- a/TalerWallet1/Views/HelperViews/Buttons.swift
+++ b/TalerWallet1/Views/HelperViews/Buttons.swift
@@ -93,7 +93,7 @@ struct ReloadButton : View  {
 
 struct TalerButtonStyle: ButtonStyle {
     @Environment(\.isEnabled) private var isEnabled: Bool
-    func disabled() -> Bool { !isEnabled }
+    var disabled: Bool { !isEnabled }
 
     enum TalerButtonStyleType {
         case plain
@@ -105,43 +105,32 @@ struct TalerButtonStyle: ButtonStyle {
     var dimmed: Bool = false
     var narrow: Bool = false
     var aligned: TextAlignment = .center
+    var badge: String = EMPTYSTRING
 
     public func makeBody(configuration: ButtonStyle.Configuration) -> some 
View {
+//        configuration.role = type == .prominent ? .primary : .normal         
 Only on macOS
             MyBigButton(//type: type,
                    foreColor: foreColor(type: type, pressed: 
configuration.isPressed),
                    backColor: backColor(type: type, pressed: 
configuration.isPressed),
                       dimmed: dimmed,
                configuration: configuration,
-                    disabled: disabled(),
+                    disabled: disabled,
                       narrow: narrow,
-                     aligned: aligned)
+                     aligned: aligned,
+                       badge: badge)
     }
 
     func foreColor(type: TalerButtonStyleType, pressed: Bool) -> Color {
-//        return if type == .plain {
-//            WalletColors().fieldForeground
-//        } else {
-//            WalletColors().buttonForeColor(pressed: pressed,
-//                                          disabled: disabled(),
-//                                         prominent: type == .prominent)
-//        }
         return type == .plain ? WalletColors().fieldForeground :      // 
primary text color
             WalletColors().buttonForeColor(pressed: pressed,
-                                          disabled: disabled(),
+                                          disabled: disabled,
                                          prominent: type == .prominent,
                                            balance: type == .balance)
     }
     func backColor(type: TalerButtonStyleType, pressed: Bool) -> Color {
-//        return if type == .plain {
-//            Color.clear
-//        } else {
-//            WalletColors().buttonBackColor(pressed: pressed,
-//                                           disabled: disabled(),
-//                                           prominent: type == .prominent)
-//        }
         return type == .plain && !pressed ? Color.clear :
             WalletColors().buttonBackColor(pressed: pressed,
-                                          disabled: disabled(),
+                                          disabled: disabled,
                                          prominent: type == .prominent,
                                            balance: type == .balance)
     }
@@ -168,28 +157,40 @@ struct TalerButtonStyle: ButtonStyle {
         let disabled: Bool
         let narrow: Bool
         let aligned: TextAlignment
+        var badge: String
 
         var body: some View {
             let aligned2: Alignment = (aligned == .center) ? Alignment.center
                                     : (aligned == .leading) ? Alignment.leading
                                     : Alignment.trailing
-            configuration.label
-                .multilineTextAlignment(aligned)
-                .accessibilityFont(.title3)
-//                                  narrow ? .title3 : .title2
-                .frame(minWidth: 0, maxWidth: narrow ? nil : .infinity, 
alignment: aligned2)
-                .padding(.vertical, 10)
-                .padding(.horizontal, 6)
-                .foregroundColor(foreColor)
-                .background(BackgroundView(color: backColor, dimmed: dimmed))
-                .contentShape(Rectangle())      // make sure the button can be 
pressed even if backgroundColor == clear
-                .scaleEffect(configuration.isPressed ? 0.95 : 1)
-                .animation(.spring(response: 0.1), value: 
configuration.isPressed)
-                .disabled(disabled)
+            let hasBadge = badge.count > 0
+            let buttonLabel = configuration.label
+                                .multilineTextAlignment(aligned)
+                                .accessibilityFont(.title3)         //   
narrow ? .title3 : .title2
+                                .frame(maxWidth: narrow ? nil : .infinity, 
alignment: aligned2)
+                                .padding(.vertical, 10)
+                                .padding(.horizontal, hasBadge ? 0 : 6)
+                                .foregroundColor(foreColor)
+                                .background(BackgroundView(color: backColor, 
dimmed: dimmed))
+                                .contentShape(Rectangle())      // make sure 
the button can be pressed even if backgroundColor == clear
+                                .scaleEffect(configuration.isPressed ? 0.95 : 
1)
+                                .animation(.spring(response: 0.1), value: 
configuration.isPressed)
+                                .disabled(disabled)
+            if !hasBadge {
+                buttonLabel
+            } else {
+                let badgeV = Image(systemName: badge)
+                                .accessibilityFont(.caption)
+                HStack(alignment: .top, spacing: 0) {
+                    badgeV.foregroundColor(.clear)
+                    buttonLabel
+                    badgeV.foregroundColor(.red)
+                }
+            }
         }
     }
 }
-
+// MARK: -
 #if DEBUG
 fileprivate struct ContentView_Previews: PreviewProvider {
     static var previews: some View {
diff --git a/TalerWallet1/Views/HelperViews/CurrencyField.swift 
b/TalerWallet1/Views/HelperViews/CurrencyField.swift
index 784c9dd..7b74114 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyField.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyField.swift
@@ -30,25 +30,25 @@ struct CurrencyField: View {
     @Binding var amount: Amount         // the `value´
     let currencyInfo: CurrencyInfo
 
-    private var currencyInputField: CurrencyInputField! = nil
+    private var currencyFieldRepresentable: CurrencyTextfieldRepresentable! = 
nil
 
-    public func becomeFirstResponder() -> Void {
-        currencyInputField.becomeFirstResponder()
+    public func becomeFirstResponder() -> Bool {
+        currencyFieldRepresentable.becomeFirstResponder()
     }
 
     public func resignFirstResponder() -> Void {
-        currencyInputField.resignFirstResponder()
+        currencyFieldRepresentable.resignFirstResponder()
     }
 
     func updateText(amount: Amount) {
-        currencyInputField.updateText(amount: amount)
+        currencyFieldRepresentable.updateText(amount: amount)
     }
 
     public init(amount: Binding<Amount>, currencyInfo: CurrencyInfo) {
         self._amount = amount
         self.currencyInfo = currencyInfo
-        self.currencyInputField = CurrencyInputField(amount: self.$amount,
-                                               currencyInfo: currencyInfo)
+        self.currencyFieldRepresentable = 
CurrencyTextfieldRepresentable(amount: self.$amount,
+                                                                   
currencyInfo: currencyInfo)
     }
 
     var body: some View {
@@ -61,11 +61,13 @@ struct CurrencyField: View {
             // Set as priority so CurrencyInputField size doesn't affect parent
             Text(amount.string(currencyInfo))
                 .layoutPriority(1)
+                // make the textfield use the whole width for tapping inside 
to become active
+                .frame(maxWidth: .infinity, alignment: .trailing)
+//                .background(.clear) this is not the problem of the white 
corners
 
             // Input text field to handle UI
-            currencyInputField
-                .accessibilityHidden(true)
-//                .textFieldStyle(.roundedBorder)
+            currencyFieldRepresentable
+//                .accessibilityHidden(true)
         }
     }
 }
@@ -86,7 +88,7 @@ class NoCaretTextField: UITextField {
 }
 
 @MainActor
-struct CurrencyInputField: UIViewRepresentable {
+struct CurrencyTextfieldRepresentable: UIViewRepresentable {
     @Binding var amount: Amount
     let currencyInfo: CurrencyInfo
 
@@ -96,12 +98,13 @@ struct CurrencyInputField: UIViewRepresentable {
         Coordinator(self)
     }
 
-    @MainActor public func becomeFirstResponder() -> Void {
+    @MainActor public func becomeFirstResponder() -> Bool {
         textField.becomeFirstResponder()
     }
 
     @MainActor public func resignFirstResponder() -> Void {
         textField.resignFirstResponder()
+        Self.endEditing()
     }
 
     func updateText(amount: Amount) {
@@ -113,17 +116,29 @@ struct CurrencyInputField: UIViewRepresentable {
     }
 
     func makeUIView(context: Context) -> NoCaretTextField {
+        textField.setContentCompressionResistancePriority(.defaultLow, for: 
.horizontal)
+
         // Assign delegate
         textField.delegate = context.coordinator
 
         // Set keyboard type
         textField.keyboardType = .numberPad
 
-        // Make visual components invisible
+        // Make visual components invisible...
         textField.tintColor = .clear
         textField.textColor = .clear
         textField.backgroundColor = .clear
+        // ... except for the bezel around the textfield
+        textField.borderStyle = .roundedRect
+//        textField.textFieldStyle(.roundedBorder)
 
+#if DEBUG
+        // Debugging: add a red border around the textfield
+        let myColor = UIColor(red: 0.9, green: 0.1, blue:0, alpha: 1.0)
+        textField.layer.masksToBounds = true
+        textField.layer.borderColor = myColor.cgColor
+    //    textField.layer.borderWidth = 2.0      //    <-   uncomment to show 
the border
+#endif
         // Add editingChanged event handler
         textField.addTarget(
             context.coordinator,
@@ -141,13 +156,13 @@ struct CurrencyInputField: UIViewRepresentable {
 
     class Coordinator: NSObject, UITextFieldDelegate {
         // Reference to currency input field
-        private var input: CurrencyInputField
+        private var textfieldRepresentable: CurrencyTextfieldRepresentable
 
         // Last valid text input string to be displayed
         private var lastValidInput: String? = ""
 
-        init(_ currencyTextField: CurrencyInputField) {
-            self.input = currencyTextField
+        init(_ representable: CurrencyTextfieldRepresentable) {
+            self.textfieldRepresentable = representable
         }
 
         func setValue(_ amount: Amount, textField: UITextField) {
@@ -155,12 +170,12 @@ struct CurrencyInputField: UIViewRepresentable {
             updateText(amount, textField: textField)
             // Update input value
 //    print(input.amount.description, " := ", amount.description)
-            input.amount = amount
+            textfieldRepresentable.amount = amount
         }
 
         func updateText(_ amount: Amount, textField: UITextField) {
             // Update field text and last valid input text
-            lastValidInput = amount.plainString(input.currencyInfo)
+            lastValidInput = 
amount.plainString(textfieldRepresentable.currencyInfo)
 //    print("lastValidInput: `\(lastValidInput)´")
             textField.text = lastValidInput
             let endPosition = textField.endOfDocument
@@ -171,18 +186,27 @@ struct CurrencyInputField: UIViewRepresentable {
             // If replacement string is empty, we can assume the backspace key 
was hit
             if string.isEmpty {
                 // Resign first responder when delete is hit when value is 0
-                if input.amount.isZero {
+                if textfieldRepresentable.amount.isZero {
                     textField.resignFirstResponder()
+//                    Self.endEditing()
                 } else {
                     // Remove trailing digit: divide value by 10
-                    let amount = input.amount.copy()
-                    amount.removeDigit(input.currencyInfo)
+                    let amount = textfieldRepresentable.amount.copy()
+                    amount.removeDigit(textfieldRepresentable.currencyInfo)
                     setValue(amount, textField: textField)
                 }
             }
             return true
         }
 
+        func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
+            return true
+        }
+
+        func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
+            return true
+        }
+
         @objc func editingChanged(textField: NoCaretTextField) {
             // Get a mutable copy of last text
             guard var oldText = lastValidInput else {
@@ -209,8 +233,8 @@ struct CurrencyInputField: UIViewRepresentable {
 
             // Multiply by 10 to shift numbers one position to the left, 
revert if an overflow occurs
             // Add the new trailing digit, revert if an overflow occurs
-            let amount = input.amount.copy()
-            amount.addDigit(digit, currencyInfo: input.currencyInfo)
+            let amount = textfieldRepresentable.amount.copy()
+            amount.addDigit(digit, currencyInfo: 
textfieldRepresentable.currencyInfo)
 
             // If new value has more digits than allowed by formatter, revert
 //            if input.formatter.maximumFractionDigits + 
input.formatter.maximumIntegerDigits < String(addValue).count {
diff --git a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift 
b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
index 7d6c045..8c3f7c2 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
@@ -5,24 +5,78 @@
 import SwiftUI
 import taler_swift
 
+fileprivate let shortcuts = [5000,2500,1000,500]        // TODO: adapt for ¥
+
+struct ShortcutButton: View {
+    let shortcut: Int
+    let currency: String
+    let currencyInfo: CurrencyInfo
+    let currencyField: CurrencyField
+    let available: Amount?
+    let action: (Int, CurrencyField) -> Void
+
+    func makeButton(with newShortcut: Int) -> ShortcutButton {
+        ShortcutButton(shortcut: newShortcut,
+                       currency: currency,
+                   currencyInfo: currencyInfo,
+                  currencyField: currencyField,
+                      available: available,
+                         action: action)
+    }
+
+    func isDisabled(shortie: Amount) -> Bool {
+        if let available {
+            do {
+                return try available < shortie
+            } catch {
+                return true
+            }
+        }
+        return false
+    }
+
+    var body: some View {
+        let shortie = Amount(currency: currency, cent: UInt64(shortcut))       
 // TODO: adapt for ¥
+        let title = shortie.string(currencyInfo)
+        let shortcutLabel = String(localized: "Shortcut", comment: "VoiceOver: 
$50,$25,$10,$5 shortcut buttons")
+        Button(action: { action(shortcut, currencyField)} ) {
+            Text(title)
+                .lineLimit(1)
+                .accessibilityFont(.callout)
+        }
+//            .frame(maxWidth: .infinity)
+            .disabled(isDisabled(shortie: shortie))
+            .buttonStyle(.bordered)
+            .accessibilityLabel("\(shortcutLabel) \(title)")
+    }
+}
+// MARK: -
 struct CurrencyInputView: View {
     @Binding var amount: Amount         // the `value´
+    let available: Amount?
     let title: String
-    let shortcutLabel: String
 
     @EnvironmentObject private var controller: Controller
     
-    @State var hasBeenShown = false
+    @State private var hasBeenShown = false
+    @State private var showKeyboard = 0
+    @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()
+    }
 
     var body: some View {
-        let shortcuts = [50,25,10,5]
         let currency = amount.currencyStr
         let currencyInfo = controller.info(for: currency, 
controller.currencyTicker)
         let currencyField = CurrencyField(amount: $amount, currencyInfo: 
currencyInfo)
-        VStack (alignment: .center) {
+        VStack (alignment: .center) {   // center shortcut buttons
             HStack {
                 Text(title)
-//                  .padding(.top)
                     .accessibilityFont(.title3)
                     .accessibilityAddTraits(.isHeader)
                     .accessibilityRemoveTraits(.isStaticText)
@@ -34,29 +88,72 @@ struct CurrencyInputView: View {
                 .background(WalletColors().fieldBackground)
                 .accessibilityFont(.title2)
                 .textFieldStyle(.roundedBorder)
-            HStack {
-                ForEach(shortcuts, id: \.self) { shortcut in
-                    let shortie = Amount(currency: currency, integer: 
UInt64(shortcut), fraction: 0)
-                    let title = shortie.string(currencyInfo)
-                    Button(title) {
-                        currencyField.updateText(amount: shortie)
-                        amount = shortie
-                    }.buttonStyle(.bordered)
-                        .accessibilityLabel("\(shortcutLabel) \(title)")
+                .onTapGesture {
+                    if useShortcut != 0 {
+                        amount = Amount.zero(currency: currency)
+                        useShortcut = 0
+                    }
+                    showKeyboard += 1
                 }
-            }
+            if #available(iOS 16.0, *) {
+                let shortcutButton = ShortcutButton(shortcut: 0,
+                                                    currency: currency,
+                                                currencyInfo: currencyInfo,
+                                               currencyField: currencyField,
+                                                   available: available,
+                                                      action: action)
+                ViewThatFits(in: .horizontal) {
+                    HStack {
+                        ForEach(shortcuts, id: \.self) {
+                            shortcutButton.makeButton(with: $0)
+                                .accessibilityAddTraits($0 == useShortcut ? 
.isSelected : [])
+                        }
+                    }
+                    VStack {
+                        let count = shortcuts.count
+                        let half = count / 2
+                        HStack {
+                            Spacer()
+                            ForEach(0..<half, id: \.self) { index in
+                                let thisShortcut = shortcuts[index]
+                                shortcutButton.makeButton(with: thisShortcut)
+                                    .accessibilityAddTraits(thisShortcut == 
useShortcut ? .isSelected : [])
+                                Spacer()
+                            }
+                        }
+                        HStack {
+                            Spacer()
+                            ForEach(half..<count, id: \.self) { index in
+                                let thisShortcut = shortcuts[index]
+                                shortcutButton.makeButton(with: thisShortcut)
+                                    .accessibilityAddTraits(thisShortcut == 
useShortcut ? .isSelected : [])
+                                Spacer()
+                            }
+                        }
+                    }
+                    VStack {
+                        ForEach(shortcuts, id: \.self) {
+                            shortcutButton.makeButton(with: $0)
+                                .accessibilityAddTraits($0 == useShortcut ? 
.isSelected : [])
+                        }
+                    }
+                }
+            } // iOS 16+ only
         }.onAppear {   // make CurrencyField show the keyboard after 0.4 
seconds
             if hasBeenShown {
 //                print("❗️Yikes: CurrencyInputView hasBeenShown")
             } else {
-//                print("❗️Yikes: First CurrencyInputView❗️")
+                print("❗️CurrencyInputView❗️")
                 DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
                     hasBeenShown = true
-                    currencyField.becomeFirstResponder()
+                    if !currencyField.becomeFirstResponder() {
+                        print("❗️Yikes❗️")
+                    }
                 }
             }
         }.onDisappear {
             currencyField.resignFirstResponder()
+            hasBeenShown = false
         }
     }
 }
@@ -70,8 +167,8 @@ fileprivate struct Previews: PreviewProvider {
         var body: some View {
 //            Preview_Content()
             CurrencyInputView(amount: $amountToTransfer,
-                               title: "Amount to withdraw:",
-                       shortcutLabel: "Withdraw")
+                           available: nil,
+                               title: "Amount to withdraw:")
                 .environmentObject(controller)
         }
     }
diff --git a/TalerWallet1/Views/HelperViews/SelectDays.swift 
b/TalerWallet1/Views/HelperViews/SelectDays.swift
index 081f924..3b3315c 100644
--- a/TalerWallet1/Views/HelperViews/SelectDays.swift
+++ b/TalerWallet1/Views/HelperViews/SelectDays.swift
@@ -34,8 +34,6 @@ struct SelectDays: View {
     }
 
     var body: some View {
-        let selectedStr = String(localized: "selected", comment: "VoiceOver 
hint which button is selected")
-
         Section {   // (alignment: .leading)
             Text("Expires in:")
                 .accessibilityLabel("Choose the expiration duration")
@@ -50,7 +48,7 @@ struct SelectDays: View {
                         Text("\(ONEDAY) Day", comment: "1 Day, might get 
plural (e.g. 2..3 Days), 4 letters max., abbreviate if longer")     // TODO: 
Plural
                     }
                 }.buttonStyle(TalerButtonStyle(type: (selected == ONEDAY) ? 
.prominent : .bordered, dimmed: true))
-                    .accessibilityValue((selected == ONEDAY) ? selectedStr : 
EMPTYSTRING)
+                    .accessibilityAddTraits(selected == ONEDAY ? .isSelected : 
[])
                     .disabled(!isEnabled)
 
                 Button(action: sevenDayAction) {
@@ -60,7 +58,7 @@ struct SelectDays: View {
                         Text("\(SEVENDAYS) Days", comment: "7 Days, always 
plural (3..9), 4 letters max., abbreviate if longer")
                     }
                 }.buttonStyle(TalerButtonStyle(type: (selected == SEVENDAYS) ? 
.prominent : .bordered, dimmed: true))
-                    .accessibilityValue((selected == SEVENDAYS) ? selectedStr 
: EMPTYSTRING)
+                    .accessibilityAddTraits(selected == SEVENDAYS ? 
.isSelected : [])
                     .disabled(!isEnabled || maxExpiration < SEVENDAYS)
 
                 Button(action: thirtyDayAction) {
@@ -70,7 +68,7 @@ struct SelectDays: View {
                         Text("\(THIRTYDAYS) Days", comment: "30 Days, always 
plural (10..30), 4 letters max., abbreviate if longer")
                     }
                 }.buttonStyle(TalerButtonStyle(type: (selected == THIRTYDAYS) 
? .prominent : .bordered, dimmed: true))
-                    .accessibilityValue((selected == THIRTYDAYS) ? selectedStr 
: EMPTYSTRING)
+                    .accessibilityAddTraits(selected == THIRTYDAYS ? 
.isSelected : [])
                     .disabled(!isEnabled || maxExpiration < THIRTYDAYS)
             } // 3 buttons
         }
diff --git a/TalerWallet1/Views/Main/MainView.swift 
b/TalerWallet1/Views/Main/MainView.swift
index a1639ed..839b546 100644
--- a/TalerWallet1/Views/Main/MainView.swift
+++ b/TalerWallet1/Views/Main/MainView.swift
@@ -94,11 +94,16 @@ extension MainView {
 #if TABBAR  // Taler Wallet
         @State private var selectedTab: Tab = .balances
         @State private var showKycAlert: Bool = false
+        @State private var kycURI: URL?
 
         private var openKycButton: some View {
             Button("KYC") {
                 showKycAlert = false
-//                UIApplication.shared.open(URL(string: 
UIApplication.openSettingsURLString)!)
+                if let kycURI {
+                    UIApplication.shared.open(kycURI)
+                } else {
+                    // YIKES!
+                }
             }
         }
         private var dismissAlertButton: some View {
@@ -168,7 +173,7 @@ extension MainView {
 //            } else {
                 let _ = Self._printChanges()
 //            }
-            let delay: UInt = 5
+            let delay: UInt = 0     // set to 5 to test delayed currency 
information
 #else
             let delay: UInt = 0
 #endif
@@ -242,19 +247,18 @@ extension MainView {
                   hamburgerAction: hamburgerAction)
             }
 #endif
-          } .onNotification(.TransactionStateTransition) { notification in
+          } .onNotification(.KYCrequired) { notification in
               // show an alert with the KYC link (button) which opens in Safari
                 if let transition = 
notification.userInfo?[TRANSACTIONTRANSITION] as? TransactionTransition {
-                    if transition.newTxState.major == .pending {
-                        if let newMinor = transition.newTxState.minor {
-                            if newMinor == .kyc {       // user did confirm on 
bank website
-                                logger.log(".onNotification(): KYC required")
-                                showKycAlert = true
-                            }
+                    if let kycString = transition.experimentalUserData {
+                        if let urlForKYC = URL(string: kycString) {
+                            logger.log(".onNotification(.KYCrequired): 
\(kycString)")
+                            kycURI = urlForKYC
+                            showKycAlert = true
                         }
+                    } else {
+                        // TODO: no KYC URI
                     }
-                } else { // should never happen
-                    logger.error("Yikes❗️ TransactionStateTransition without 
transition")
                 }
             }
             .alert("You need to pass a KYC procedure",
@@ -267,6 +271,11 @@ extension MainView {
                 logger.info(".onNotification(.BalanceChange) ==> reload")
                 shouldReloadBalances += 1
             }
+            .onNotification(.TransactionExpired) { notification in
+                // reload balances on receiving BalanceChange notification ...
+                logger.info(".onNotification(.TransactionExpired) ==> reload")
+                shouldReloadBalances += 1
+            }
             .onChange(of: balances) { newArray in
                 for balance in newArray {
                     let scope = balance.scopeInfo
diff --git a/TalerWallet1/Views/Peer2peer/SendDoneV.swift 
b/TalerWallet1/Views/Peer2peer/P2PReadyV.swift
similarity index 91%
rename from TalerWallet1/Views/Peer2peer/SendDoneV.swift
rename to TalerWallet1/Views/Peer2peer/P2PReadyV.swift
index d7fdd45..682e207 100644
--- a/TalerWallet1/Views/Peer2peer/SendDoneV.swift
+++ b/TalerWallet1/Views/Peer2peer/P2PReadyV.swift
@@ -7,10 +7,15 @@ import taler_swift
 import SymLog
 
 // Called when initiating a P2P transaction: Send coins or Send 
Request(Invoice)
-struct SendDoneV: View {
+struct P2PReadyV: View {
     private let symLog = SymLogV()
     let stack: CallStack
-    let navTitle = String(localized: "P2P Ready")
+    let amountToSend: Amount?
+    let amountToReceive: Amount?
+    let summary: String
+    let expireDays: UInt
+    @Binding var transactionStarted: Bool
+
     @EnvironmentObject private var model: WalletModel
 #if DEBUG
     @AppStorage("developerMode") var developerMode: Bool = true
@@ -19,12 +24,7 @@ struct SendDoneV: View {
 #endif
     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
 
-    let amountToSend: Amount?
-    let amountToReceive: Amount?
-    let summary: String
-    let expireDays: UInt
-    @Binding var transactionStarted: Bool
-
+    let navTitle = String(localized: "P2P Ready")
     @State private var transactionId: String? = nil
 
     func reloadOneAction(_ transactionId: String) async throws -> Transaction {
@@ -36,7 +36,7 @@ struct SendDoneV: View {
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
-        VStack {
+        Group {
             if let transactionId {
                 TransactionDetailView(stack: stack.push(),
                               transactionId: transactionId,
@@ -54,8 +54,14 @@ struct SendDoneV: View {
                 WithdrawProgressView(message: "Loading...")
             }
         }
-//        
.background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
         .navigationTitle(navTitle)
+        .onAppear {
+            DebugViewC.shared.setViewID(VIEW_P2P_READY, stack: stack.push())
+//            print("❗️ P2PReadyV onAppear")
+        }
+        .onDisappear {
+//            print("❗️ P2PReadyV onDisappear")
+        }
         .task {
             symLog.log(".task")
             do {
@@ -72,7 +78,7 @@ struct SendDoneV: View {
                     let terms = PeerContractTerms(amount: amountToSend,
                                                  summary: summary,
                                         purse_expiration: timestamp)
-                    // TODO: user might choose baseURL
+                    // TODO: let user choose baseURL
                     let response = try await model.initiatePeerPushDebitM(nil, 
terms: terms)
                     // will switch from WithdrawProgressView to 
TransactionDetailView
                     transactionId = response.transactionId
@@ -80,7 +86,7 @@ struct SendDoneV: View {
                     let terms = PeerContractTerms(amount: amountToReceive,
                                                  summary: summary,
                                         purse_expiration: timestamp)
-                    // TODO: user might choose baseURL
+                    // TODO: let user choose baseURL
                     let response = try await 
model.initiatePeerPullCreditM(nil, terms: terms)
                     // will switch from WithdrawProgressView to 
TransactionDetailView
                     transactionId = response.transactionId
diff --git a/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift 
b/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
new file mode 100644
index 0000000..aa6c13b
--- /dev/null
+++ b/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
@@ -0,0 +1,121 @@
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
+import SwiftUI
+import taler_swift
+import SymLog
+
+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 currencyInfo: CurrencyInfo
+    @Binding var summary: String
+    @Binding var expireDays: UInt
+
+    @AppStorage("iconOnly") var iconOnly: Bool = false
+
+    @State private var transactionStarted: Bool = false
+    @FocusState private var isFocused: Bool
+
+    var body: some View {
+#if DEBUG
+        let _ = Self._printChanges()
+        let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
+#endif
+        ScrollView { VStack (alignment: .leading, spacing: 6) {
+            if feeLabel.count > 0 {
+                HStack {
+                    Spacer()
+                    Text(feeLabel)
+                        .foregroundColor(.red)
+                        .accessibilityFont(.body)
+                }
+            }
+                if !iconOnly {
+                    Text("Enter subject:")    // Purpose
+                        .accessibilityFont(.title3)
+                        .accessibilityAddTraits(.isHeader)
+                        .accessibilityRemoveTraits(.isStaticText)
+                        .padding(.top)
+                }
+                Group { if #available(iOS 16.0, *) {
+                    TextField(iconOnly ? "Subject" : EMPTYSTRING, text: 
$summary, axis: .vertical)
+                        .focused($isFocused)
+                        .lineLimit(2...)
+                } else {
+                    TextField("Subject", text: $summary)
+                        .focused($isFocused)
+//                      .lineLimit(2...5)   // lineLimit' is only available in 
iOS 16.0 or newer
+                } } // Group for iOS16+ & iOS15
+                    .accessibilityFont(.title2)
+                    .foregroundColor(WalletColors().fieldForeground)     // 
text color
+                    .background(WalletColors().fieldBackground)
+                    .textFieldStyle(.roundedBorder)
+                    .onAppear {
+                        symLog.log("dispatching kbd...")
+                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
+                            isFocused = true        // make first responder - 
raise keybord
+                            symLog.log("...kbd isFocused")
+                        }
+                    }
+                HStack{
+                    Spacer()
+                    Text(verbatim: "\(summary.count)/100")
+                        .accessibilityFont(.body)
+                        .accessibilityValue("\(summary.count) characters of 
100")
+                } // maximum 100 characters
+
+                // TODO: compute max Expiration day from peerPushCheck to 
disable 30 (and even 7)
+                SelectDays(selected: $expireDays, maxExpiration: THIRTYDAYS)
+                    .disabled(false)
+                    .padding(.bottom)
+
+                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,
+                 transactionStarted: $transactionStarted)
+                }) {
+                    Text(buttonTitle)
+                }
+                .buttonStyle(TalerButtonStyle(type: .prominent))
+                .disabled(disabled)
+                .accessibilityHint(disabled ? "enabled when subject and 
expiration are set" : EMPTYSTRING)
+        }.padding(.horizontal) } // ScrollVStack
+        .navigationTitle(navTitle)
+        .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
+        .onAppear {
+            DebugViewC.shared.setViewID(VIEW_P2P_SUBJECT, stack: stack.push())
+//            print("❗️ P2PSubjectV onAppear")
+        }
+        .onDisappear {
+//            print("❗️ P2PSubjectV onDisappear")
+        }
+    }
+}
+// MARK: -
+#if DEBUG
+//struct SendPurpose_Previews: PreviewProvider {
+//    static var previews: some View {
+//        @State var summary: String = ""
+//        @State var expireDays: UInt = 0
+//        let amount = Amount(currency: LONGCURRENCY, integer: 10, fraction: 0)
+//        SendPurpose(amountAvailable: amount,
+//                   amountToTransfer: 543,
+//                                fee: "0,43",
+//                            summary: $summary,
+//                         expireDays: $expireDays)
+//    }
+//}
+#endif
diff --git a/TalerWallet1/Views/Peer2peer/RequestPayment.swift 
b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
index a41b72e..a16f3c8 100644
--- a/TalerWallet1/Views/Peer2peer/RequestPayment.swift
+++ b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
@@ -14,6 +14,7 @@ struct RequestPayment: View {
     @Binding var amountToTransfer: Amount
     @Binding var summary: String
 
+    @EnvironmentObject private var controller: Controller
     @EnvironmentObject private var model: WalletModel
     @AppStorage("iconOnly") var iconOnly: Bool = false
 
@@ -26,53 +27,67 @@ struct RequestPayment: View {
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
         let currency = amountToTransfer.currencyStr
-        let navTitle = String(localized: "Request Money", comment: "Dialog 
Title")
+        let currencyInfo = controller.info(for: currency, 
controller.currencyTicker)
+        let currencySymbol = currencyInfo.specs.altUnitNames?[0] ?? currency
+        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'")
 
-        ScrollView { VStack {
+        let _ = symLog.log("currency: \(currency)")
+        ScrollView { VStack(alignment: .trailing) {
             CurrencyInputView(amount: $amountToTransfer,
+                           available: nil,
                                title: iconOnly ? String(localized: "How much:")
-                                               : String(localized: "Amount to 
request:"),
-                       shortcutLabel: String(localized: "Request", comment: 
"VoiceOver: Request $50,$25,$10,$5 shortcut buttons"))
-
+                                               : String(localized: "Amount to 
request:"))
+            .padding(.top)
             let someCoins = SomeCoins(details: peerPullCheck)
-            QuiteSomeCoins(someCoins: someCoins, shouldShowFee: true,
+            QuiteSomeCoins(someCoins: someCoins,
+                       shouldShowFee: true,     // always true since the 
requester pays fees
                             currency: currency,
+                        currencyInfo: currencyInfo,
                      amountEffective: peerPullCheck?.amountEffective)
 
-            HStack {
-                let disabled = amountToTransfer.isZero || someCoins.invalid || 
someCoins.tooMany
+            let disabled = amountToTransfer.isZero || someCoins.invalid || 
someCoins.tooMany
 
-                NavigationLink(destination: LazyView {
-                    RequestPurpose(stack: stack.push(),
-                        amountToTransfer: amountToTransfer,
-                                     fee: someCoins.fee,
-                                 summary: $summary,
-                              expireDays: $expireDays)
-//                        { deactivateAction() }
-                }) {
-                    Text("Request \(amountToTransfer.readableDescription)")    
 // TODO: formatter
-                }
+            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") }
                 .buttonStyle(TalerButtonStyle(type: .prominent))
                 .disabled(disabled)
-            }
-            Spacer()
-        } }
+//            Spacer()
+        } } // ScrollVStack
         .frame(maxWidth: .infinity, alignment: .leading)
         .padding(.horizontal)
         .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
         .navigationTitle(navTitle)
-        .onAppear {   // make CurrencyField show the keyboard
-            DebugViewC.shared.setViewID(VIEW_REQUEST_P2P, stack: stack.push())
+        .onAppear {
+            DebugViewC.shared.setViewID(VIEW_P2P_REQUEST, stack: stack.push())
             symLog.log("❗️Yikes \(navTitle) onAppear")
         }
         .onDisappear {
             symLog.log("❗️Yikes \(navTitle) onDisappear")
         }
         .task(id: amountToTransfer.value) {
-            if !amountToTransfer.isZero {
+            if amountToTransfer.isZero {
+//                fee = EMPTYSTRING
+            } else {
                 do {
                     let ppCheck = try await 
model.checkPeerPullCreditM(amountToTransfer, exchangeBaseUrl: nil)
-                    peerPullCheck = ppCheck
+                    peerPullCheck = ppCheck     // redraw
                     // TODO: set from exchange
 //                  agePicker.setAges(ages: 
peerPushCheck?.ageRestrictionOptions)
                 } catch {    // TODO: error
diff --git a/TalerWallet1/Views/Peer2peer/RequestPurpose.swift 
b/TalerWallet1/Views/Peer2peer/RequestPurpose.swift
deleted file mode 100644
index 8562c52..0000000
--- a/TalerWallet1/Views/Peer2peer/RequestPurpose.swift
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
- * See LICENSE.md
- */
-import SwiftUI
-import taler_swift
-import SymLog
-
-struct RequestPurpose: View {
-    private let symLog = SymLogV(0)
-    let stack: CallStack
-
-    let amountToTransfer: Amount
-    let fee: String
-    @Binding var summary: String
-    @Binding var expireDays: UInt
-
-    @EnvironmentObject private var controller: Controller
-    @AppStorage("iconOnly") var iconOnly: Bool = false
-    let navTitle = String(localized: "NavTitle_Request_Subject",
-                       defaultValue: "Request", comment: "NavTitle for 
entering the subject for Request-Payment")
-
-    @State private var transactionStarted: Bool = false
-    @FocusState private var isFocused: Bool
-
-    var body: some View {
-        let currencyInfo = controller.info(for: amountToTransfer.currencyStr, 
controller.currencyTicker)
-        VStack (spacing: 6) {
-            Text(amountToTransfer.string(currencyInfo))
-            Text("+ \(fee) payment fee")
-                .foregroundColor(.red)
-            VStack(alignment: .leading, spacing: 6) {
-                if !iconOnly {
-                    Text("Subject:")
-                        .accessibilityFont(.title3)
-                        .padding(.top)
-                }
-                TextField("Subject", text: $summary)
-                    .accessibilityFont(.title)
-                    .foregroundColor(WalletColors().fieldForeground)     // 
text color
-                    .background(WalletColors().fieldBackground)
-                    .textFieldStyle(.roundedBorder)
-                    .focused($isFocused)
-                    .onAppear {
-                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
-                            isFocused = true        // make first responder - 
raise keybord
-                        }
-                    }
-
-                HStack{
-                    Spacer()
-                    Text(verbatim: "\(summary.count)/100")
-                } // maximum 100 characters
-
-                SelectDays(selected: $expireDays, maxExpiration: THIRTYDAYS)
-                    .disabled(false)
-                    .padding(.bottom)
-
-                let disabled = (expireDays == 0) || (summary.count < 1)
-                NavigationLink(destination: LazyView {
-                    SendDoneV(stack: stack.push(),
-                       amountToSend: nil,
-                    amountToReceive: amountToTransfer,
-                            summary: summary,
-                         expireDays: expireDays,
-                 transactionStarted: $transactionStarted)
-                }) {
-                    Text("Request \(amountToTransfer.readableDescription)")    
 // TODO: formatter
-//                        .accessibilityFont(buttonFont)
-                }
-                .buttonStyle(TalerButtonStyle(type: .prominent))
-                .disabled(disabled)
-                .accessibilityHint(disabled ? "enabled when subject and 
expiration are set" : EMPTYSTRING)
-
-                Spacer()
-            }
-            .frame(maxWidth: .infinity, alignment: .leading)
-            .padding(.horizontal)
-        }
-        .navigationTitle(navTitle)
-        .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
-        .onAppear {
-            DebugViewC.shared.setViewID(VIEW_REQUEST_PURPOSE, stack: 
stack.push())
-//            print("❗️ RequestPurpose onAppear")
-        }
-        .onDisappear {
-//            print("❗️ RequestPurpose onDisappear")
-        }
-    }
-
-}
-// MARK: -
-#if DEBUG
-//struct RequestPurpose_Previews: PreviewProvider {
-//    static var previews: some View {
-//        let scopeInfo = ScopeInfo(type: .exchange, exchangeBaseUrl: 
DEMOEXCHANGE, currency: LONGCURRENCY)
-//        @State var summary: String = "pUrPoSe"
-//        @State var expireDays: UInt = 0
-//        RequestPurpose(scopeInfo: scopeInfo,
-//                 amountToReceive: 5,
-//                             fee: "fee",
-//                         summary: $summary,
-//                      expireDays: $expireDays)
-//    }
-//}
-#endif
diff --git a/TalerWallet1/Views/Peer2peer/SendAmount.swift 
b/TalerWallet1/Views/Peer2peer/SendAmount.swift
index 7437004..b63da48 100644
--- a/TalerWallet1/Views/Peer2peer/SendAmount.swift
+++ b/TalerWallet1/Views/Peer2peer/SendAmount.swift
@@ -22,7 +22,7 @@ struct SendAmount: View {
     @State var peerPushCheck: CheckPeerPushDebitResponse? = nil
     @State private var expireDays: UInt = SEVENDAYS
     @State private var insufficient: Bool = false
-    @State private var fee: String = ""
+    @State private var feeStr: String = EMPTYSTRING
 
     private func fee(ppCheck: CheckPeerPushDebitResponse?) -> Amount? {
         do {
@@ -35,57 +35,69 @@ struct SendAmount: View {
         return nil
     }
     
-    var feeLabel: String { String(localized: " + \(fee) payment fee") }
+    var feeLabel: String { feeStr.count > 0 ? String(localized: "+ \(feeStr) 
send fee") : EMPTYSTRING }
 
     var body: some View {
 #if DEBUG
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
-        let currency = amountAvailable.currencyStr
+        let currency = amountToTransfer.currencyStr
         let currencyInfo = controller.info(for: currency, 
controller.currencyTicker)
-        let navTitle = String(localized: "Send \(currency)", comment: "Send 
currency, Dialog Title")
+        let currencySymbol = currencyInfo.specs.altUnitNames?[0] ?? currency
+        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 current = amountToTransfer.string(currencyInfo)
+        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.")
-        ScrollView {
-            VStack(alignment: .trailing) {
-//                let _ = print("available: \(available)")
-                Text("Available:\t\(available)")
-                    .accessibilityFont(.title3)
-                    .padding(.bottom, 2)
-                CurrencyInputView(amount: $amountToTransfer,
-                                   title: iconOnly ? String(localized: "How 
much:")
-                                                   : String(localized: "Amount 
to send:"),
-                           shortcutLabel: String(localized: "Send", comment: 
"VoiceOver: Send $50,$25,$10,$5 shortcut buttons"))
-                let disabled = insufficient || amountToTransfer.isZero
-                Text(insufficient ? insufficientLabel
-                                  : feeLabel)
-                    .accessibilityFont(.body)
-                    .foregroundColor(.red)
-                    .padding(4)
 
-                NavigationLink(destination: LazyView {
-                    SendPurpose(stack: stack.push(),
-                      amountAvailable: amountAvailable,
-                     amountToTransfer: amountToTransfer,
-                                  fee: fee,
-                              summary: $summary,
-                           expireDays: $expireDays)
-                }) {
-                    Text("Next")
-                }   .buttonStyle(TalerButtonStyle(type: .prominent))
-                    .disabled(disabled)
-                Spacer()
-            }
-        }
+        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
+            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") }
+                .buttonStyle(TalerButtonStyle(type: .prominent))
+                .disabled(disabled)
+//            Spacer()
+        } } // ScrollVStack
         .frame(maxWidth: .infinity, alignment: .leading)
         .padding(.horizontal)
         .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
         .navigationTitle(navTitle)
-        .onAppear {   // make CurrencyField show the keyboard
-            DebugViewC.shared.setViewID(VIEW_SEND_P2P, stack: stack.push())
+        .onAppear {
+            DebugViewC.shared.setViewID(VIEW_P2P_SEND, stack: stack.push())
             symLog.log("❗️Yikes SendAmount onAppear")
         }
         .onDisappear {
@@ -94,24 +106,26 @@ struct SendAmount: View {
         .task(id: amountToTransfer.value) {
             do {
                 insufficient = try amountToTransfer > amountAvailable
-                print("current: \(current)")
+                print("amountStr: \(amountVoiceOver)")
             } catch {
                 print("Yikes❗️ insufficient failed❗️")
                 insufficient = true
             }
 
             if insufficient {
-                announce(this: "\(current), \(insufficientLabel2)")
-            } else if !amountToTransfer.isZero {
+                announce(this: "\(amountVoiceOver), \(insufficientLabel2)")
+            } else if amountToTransfer.isZero {
+                feeStr = EMPTYSTRING
+            } else {
                 do {
                     let ppCheck = try await 
model.checkPeerPushDebitM(amountToTransfer)
                     peerPushCheck = ppCheck
                 // TODO: set from exchange
 //                agePicker.setAges(ages: peerPushCheck?.ageRestrictionOptions)
                     if let feeAmount = fee(ppCheck: peerPushCheck) {
-                        fee = feeAmount.string(currencyInfo)
-                    } else { fee = "" }
-                    announce(this: "\(current), \(feeLabel)")
+                        feeStr = feeAmount.string(currencyInfo)
+                    } else { feeStr = EMPTYSTRING }
+                    announce(this: "\(amountVoiceOver), \(feeLabel)")
                 } catch {    // TODO: error
                     symLog.log(error.localizedDescription)
                     peerPushCheck = nil
diff --git a/TalerWallet1/Views/Peer2peer/SendPurpose.swift 
b/TalerWallet1/Views/Peer2peer/SendPurpose.swift
deleted file mode 100644
index a06f25b..0000000
--- a/TalerWallet1/Views/Peer2peer/SendPurpose.swift
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
- * See LICENSE.md
- */
-import SwiftUI
-import taler_swift
-import SymLog
-
-struct SendPurpose: View {
-    private let symLog = SymLogV(0)
-    let stack: CallStack
-
-    let amountAvailable: Amount
-    let amountToTransfer: Amount
-    let fee: String
-    @Binding var summary: String
-    @Binding var expireDays: UInt
-    @AppStorage("iconOnly") var iconOnly: Bool = false
-    let navTitle = String(localized: "NavTitle_Send_Subject", defaultValue: 
"Subject", comment: "NavTitle for entering the subject for Send-Money")
-
-    @State private var transactionStarted: Bool = false
-    @FocusState private var isFocused: Bool
-
-    var body: some View {
-        VStack (spacing: 6) {
-            Text(amountToTransfer.readableDescription)              // TODO: 
curreny formatter
-            Text("+ \(fee) payment fee")
-                .accessibilityFont(.body)
-                .foregroundColor(.red)
-            VStack(alignment: .leading, spacing: 6) {
-                if !iconOnly {
-                    Text("Subject:")    // Purpose
-                        .accessibilityFont(.title2)
-                        .padding(.top)
-                }
-                if #available(iOS 16.0, *) {
-                    TextField("Subject", text: $summary, axis: .vertical)
-                        .accessibilityFont(.title2)
-                        .lineLimit(2...)
-                        .foregroundColor(WalletColors().fieldForeground)     
// text color
-                        .background(WalletColors().fieldBackground)
-                        .textFieldStyle(.roundedBorder)
-                        .focused($isFocused)
-                        .onAppear {
-                            DispatchQueue.main.asyncAfter(deadline: .now() + 
0.4) {
-                                isFocused = true        // make first 
responder - raise keybord
-                            }
-                        }
-                } else {
-                    TextField("Subject", text: $summary)
-                        .accessibilityFont(.title)
-//                        .lineLimit(2...5)   // lineLimit' is only available 
in iOS 16.0 or newer
-                        .foregroundColor(WalletColors().fieldForeground)     
// text color
-                        .background(WalletColors().fieldBackground)
-                        .textFieldStyle(.roundedBorder)
-                        .focused($isFocused)
-                        .onAppear {
-                            DispatchQueue.main.asyncAfter(deadline: .now() + 
0.4) {
-                                isFocused = true        // make first 
responder - raise keybord
-                            }
-                        }
-                } // #available
-
-                HStack{
-                    Spacer()
-                    Text(verbatim: "\(summary.count)/100")
-                        .accessibilityFont(.body)
-                        .accessibilityValue("\(summary.count) characters of 
100")
-                } // maximum 100 characters
-
-                // TODO: compute max Expiration day from peerPushCheck to 
disable 30 (and even 7)
-                SelectDays(selected: $expireDays, maxExpiration: THIRTYDAYS)
-                    .disabled(false)
-                    .padding(.bottom)
-
-                let disabled = (expireDays == 0) || (summary.count < 1)    // 
TODO: check amountAvailable
-                NavigationLink(destination: LazyView {
-                    SendDoneV(stack: stack.push(),
-                       amountToSend: amountToTransfer,
-                    amountToReceive: nil,
-                            summary: summary,
-                         expireDays: expireDays,
-                 transactionStarted: $transactionStarted)
-                }) {
-                    Text("Send \(amountToTransfer.readableDescription) now", 
comment: "amountToTransfer")  // TODO: currency formatter
-                }
-                .buttonStyle(TalerButtonStyle(type: .prominent))
-                .disabled(disabled)
-                .accessibilityHint(disabled ? "enabled when subject and 
expiration are set" : EMPTYSTRING)
-
-                Spacer()
-            }
-            .textFieldStyle(.roundedBorder)
-            .frame(maxWidth: .infinity, alignment: .leading)
-            .padding(.horizontal)
-        }
-        .navigationTitle(navTitle)
-        .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
-        .onAppear {
-            DebugViewC.shared.setViewID(VIEW_SEND_PURPOSE, stack: stack.push())
-//            print("❗️ SendPurpose onAppear")
-        }
-        .onDisappear {
-//            print("❗️ SendPurpose onDisappear")
-        }
-        .task {
-            symLog.log(".task")
-            do {
-
-            } catch {    // TODO: error
-                symLog.log(error.localizedDescription)
-            }
-        }
-    }
-
-}
-// MARK: -
-#if DEBUG
-//struct SendPurpose_Previews: PreviewProvider {
-//    static var previews: some View {
-//        @State var summary: String = ""
-//        @State var expireDays: UInt = 0
-//        let amount = Amount(currency: LONGCURRENCY, integer: 10, fraction: 0)
-//        SendPurpose(amountAvailable: amount,
-//                   amountToTransfer: 543,
-//                                fee: "0,43",
-//                            summary: $summary,
-//                         expireDays: $expireDays)
-//    }
-//}
-#endif
diff --git a/TalerWallet1/Views/Settings/Pending/PendingOpView.swift 
b/TalerWallet1/Views/Settings/Pending/PendingOpView.swift
deleted file mode 100644
index ddfbfcd..0000000
--- a/TalerWallet1/Views/Settings/Pending/PendingOpView.swift
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
- * See LICENSE.md
- */
-import SwiftUI
-import taler_swift
-
-struct PendingOpView: View {
-    var pendingOp: PendingOperation
-    @State var polling: Bool = false
-    @State var liveliness: Bool = false
-    @State var isDue: Bool = false
-
-    var body: some View {
-        Section {
-            Group {
-                if let baseURL = pendingOp.exchangeBaseUrl {
-                    Text(baseURL)
-                }
-                Text(pendingOp.id)
-                    .accessibilityFont(.caption)
-                let isLongPolling = "isLongPolling"
-                Toggle(isLongPolling, isOn: $polling)
-                    .disabled(true)
-                let givesLifeness = "givesLifeness"
-                Toggle(givesLifeness, isOn: $liveliness)
-                    .disabled(true)
-                let isDue = "isDue"
-                Toggle(isDue, isOn: $isDue)
-                    .disabled(true)
-                let (dateString, date) = TalerDater.dateString(from: 
pendingOp.timestampDue)
-                Text(dateString)
-            }
-            .accessibilityFont(.body)
-        } header: {
-            Text(pendingOp.type)
-                .accessibilityFont(.title2)
-        }
-//        .textCase(nil)    // don't capitalize
-        .onAppear {
-            polling = pendingOp.isLongpolling
-            liveliness = pendingOp.givesLifeness
-            isDue = pendingOp.isDue
-        }
-    }
-}
-// MARK: -
-#if DEBUG
-struct PendingOpView_Previews: PreviewProvider {
-    static var pending1 = PendingOperation(type: "exchange-check-refresh",
-                                exchangeBaseUrl: DEMOEXCHANGE,
-                                             id: "exchange-update:" + 
DEMOEXCHANGE,
-                                  isLongpolling: false,
-                                  givesLifeness: true,
-                                          isDue: false,
-                                   timestampDue: Timestamp(from:1700000000000))
-    static var previews: some View {
-        List {
-            PendingOpView(pendingOp: pending1)
-        }
-    }
-}
-#endif
diff --git a/TalerWallet1/Views/Settings/Pending/PendingOpsListView.swift 
b/TalerWallet1/Views/Settings/Pending/PendingOpsListView.swift
deleted file mode 100644
index f16288a..0000000
--- a/TalerWallet1/Views/Settings/Pending/PendingOpsListView.swift
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
- * See LICENSE.md
- */
-import SwiftUI
-import SymLog
-
-struct PendingOpsListView: View {
-    let navTitle = String(localized: "Pending")
-    let stack: CallStack
-
-    @EnvironmentObject private var model: WalletModel
-
-    @State var pendingOperations: [PendingOperation] = []
-
-    var body: some View {
-        let reloadAction = model.getPendingOperationsM
-        Content(stack: stack.push(), pendingOperations: $pendingOperations, 
reloadAction: reloadAction)
-            .navigationTitle(navTitle)
-            .task {
-                pendingOperations = await reloadAction()
-            }
-    }
-}
-// MARK: -
-extension PendingOpsListView {
-    struct Content: View {
-        let stack: CallStack
-        @Binding var pendingOperations: [PendingOperation]
-        var reloadAction: () async -> [PendingOperation]
-        @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
-        var body: some View {
-//            ScrollViewReader { scrollView in
-                List(pendingOperations, id: \.self) { pendingOp in
-                    PendingOpView(pendingOp: pendingOp)
-                }
-                .listStyle(myListStyle.style).anyView
-                .onAppear() {
-                    DebugViewC.shared.setViewID(VIEW_PENDING, stack: 
stack.push())
-                }
-                .refreshable {
-                    pendingOperations = await reloadAction()
-                }
-//            }
-        }
-    }
-}
diff --git a/TalerWallet1/Views/Settings/SettingsItem.swift 
b/TalerWallet1/Views/Settings/SettingsItem.swift
index 61220e7..4c4bc72 100644
--- a/TalerWallet1/Views/Settings/SettingsItem.swift
+++ b/TalerWallet1/Views/Settings/SettingsItem.swift
@@ -83,11 +83,11 @@ struct SettingsFont: View {
     let value: Int
     let action: (Int) -> Void
 
-    @State private var selected = 0
+    @State private var selectedFont = 0
     let fonts = [String(localized: "Standard iOS Font"), 
"Atkinson-Hyperlegible", "Nunito"]
 
     var body: some View {
-        Picker(title, selection: $selected, content: {
+        Picker(title, selection: $selectedFont, content: {
             ForEach(0..<fonts.count, id: \.self, content: { index in
                 Text(fonts[index]).tag(index)
             })
@@ -95,10 +95,10 @@ struct SettingsFont: View {
             .accessibilityFont(.title2)
             .pickerStyle(.menu)
             .onAppear() {
-                withAnimation { selected = value }
+                withAnimation { selectedFont = value }
             }
-            .onChange(of: selected) { selected in
-                action(selected)
+            .onChange(of: selectedFont) { selectedF in
+                action(selectedF)
             }
     }
 }
diff --git a/TalerWallet1/Views/Settings/SettingsView.swift 
b/TalerWallet1/Views/Settings/SettingsView.swift
index ae34f61..5451179 100644
--- a/TalerWallet1/Views/Settings/SettingsView.swift
+++ b/TalerWallet1/Views/Settings/SettingsView.swift
@@ -99,17 +99,23 @@ struct SettingsView: View {
                     SettingsItem(name: aboutStr, id1: "about",
                           description: hideDescriptions ? nil : 
String(localized: "More info about this app...")) {}
                 }
+#if DEBUG
+                Text("https://bank.taler.grothoff.org/";)
+#endif
 //                SettingsToggle(name: String(localized: "More Contrast"), 
value: $moreContrast, id1: "contrast",
 //                               description: hideDescriptions ? nil : 
String(localized: "If you don't like grey"))
                 if controller.hapticCapability.supportsHaptics {
                     SettingsToggle(name: String(localized: "Haptics"), value: 
$useHaptics, id1: "haptics",
                             description: hideDescriptions ? nil : 
String(localized: "Vibration Feedback"))
+                    .id("playHaptics")
                 }
                 SettingsSpeaker(name: String(localized: "Play Payment 
Sounds"), value: $playSounds,
                          description: hideDescriptions ? nil : 
String(localized: "When a transaction finished"))
                 .id("playSounds")
                 SettingsFont(title: String(localized: "Font:"), value: 
talerFont, action: redraw)
+                    .id("font")
                 SettingsStyle(title: String(localized: "Liststyle:"), 
myListStyle: $myListStyle)
+                    .id("liststyle")
                 SettingsToggle(name: String(localized: "Minimalistic"), value: 
$iconOnly, id1: "minimal",
                         description: hideDescriptions ? nil : 
String(localized: "Omit text where possible")) {
                     hideDescriptions = iconOnly //withAnimation { 
hideDescriptions = iconOnly }
@@ -120,16 +126,6 @@ struct SettingsView: View {
                         withAnimation { showDevelopItems = developerMode }
                     }
                     if showDevelopItems {  // show or hide the following items
-                        NavigationLink {        // whole row like in a 
tableView
-                            LazyView { PendingOpsListView(stack: stack.push()) 
}
-                        } label: {
-                            SettingsItem(name: String("Pending Operations"), 
id1: "pending",
-                                  description: hideDescriptions ? nil : 
String("Transactions not yet done...")) {}
-                        }.id("pending_L")
-                        SettingsToggle(name: String("Set 2 seconds delay"),
-                                      value: $developDelay.onChange({ delay in
-                                        walletCore.developDelay = delay}), 
id1: "delay",
-                                description: hideDescriptions ? nil : 
String("After each wallet-core action"))
                         SettingsItem(name: String("Withdraw \(DEMOCURRENCY)"), 
id1: "demo1with",
                               description: hideDescriptions ? nil : 
String("Get money for testing")) {
                             let title = "Withdraw"
@@ -146,7 +142,7 @@ struct SettingsView: View {
                             }
                             .buttonStyle(.bordered)
                             .disabled(withDrawDisabled)
-                        }.id("demoWithdraw")
+                        }.id("demo1withdraw")
                         SettingsItem(name: String("Withdraw \(TESTCURRENCY)"), 
id1: "test1with",
                               description: hideDescriptions ? nil : 
String("Get money for testing")) {
                             let title = "Withdraw"
@@ -163,7 +159,12 @@ struct SettingsView: View {
                             }
                             .buttonStyle(.bordered)
                             .disabled(withDrawDisabled)
-                        }.id("testWithdraw")
+                        }.id("test1withdraw")
+                        SettingsToggle(name: String("Set 2 seconds delay"),
+                                       value: $developDelay.onChange({ delay in
+                            walletCore.developDelay = delay}), id1: "delay",
+                                       description: hideDescriptions ? nil : 
String("After each wallet-core action"))
+                            .id("delay")
                         SettingsItem(name: String("Run Integration Test"), 
id1: "demo1test",
                               description: hideDescriptions ? nil : 
String("Perform basic test transactions")) {
                             let title = "Demo 1"
@@ -180,7 +181,7 @@ struct SettingsView: View {
                             }
                             .buttonStyle(.bordered)
                             .disabled(checkDisabled)
-                        }
+                        }.id("demo1runTest")
                         SettingsItem(name: String("Run Integration Test"), 
id1: "test1test",
                               description: hideDescriptions ? nil : "Perform 
basic test transactions") {
                             let title = "Test 1"
@@ -197,7 +198,7 @@ struct SettingsView: View {
                             }
                             .buttonStyle(.bordered)
                             .disabled(checkDisabled)
-                        }
+                        }.id("test1runTest")
                         SettingsItem(name: String("Run Integration Test V2"), 
id1: "demo2test",
                               description: hideDescriptions ? nil : 
String("Perform more test transactions")) {
                             let title = "Demo 2"
@@ -214,7 +215,7 @@ struct SettingsView: View {
                             }
                             .buttonStyle(.bordered)
                             .disabled(checkDisabled)
-                        }
+                        }.id("demo2runTest")
                         SettingsItem(name: String("Run Integration Test V2"), 
id1: "test2test",
                               description: hideDescriptions ? nil : 
String("Perform more test transactions")) {
                             let title = "Test 2"
@@ -231,7 +232,7 @@ struct SettingsView: View {
                             }
                             .buttonStyle(.bordered)
                             .disabled(checkDisabled)
-                        }
+                        }.id("test2runTest")
                         SettingsItem(name: String("Save Logfile"), id1: "save",
                               description: hideDescriptions ? nil : 
String("Help debugging wallet-core")) {
                             Button("Save") {
@@ -240,7 +241,7 @@ struct SettingsView: View {
                             }
                             .buttonStyle(.bordered)
                             .disabled(true)
-                        }
+                        }.id("saveLog")
                         SettingsItem(name: String("Reset Wallet"), id1: 
"reset",
                               description: hideDescriptions ? nil : 
String("Throw away all your money")) {
                             Button("Reset") {
@@ -248,7 +249,7 @@ struct SettingsView: View {
                             }
                             .buttonStyle(.bordered)
                             .disabled(didReset)
-                        }
+                        }.id("resetWallet")
                     }
                 }
             }
diff --git 
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift 
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
index a8e5b2d..56773b4 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
@@ -17,6 +17,7 @@ struct WithdrawURIView: View {
     // the URL from the bank website
     let url: URL
 
+    @EnvironmentObject private var controller: Controller
     @EnvironmentObject private var model: WalletModel
     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
 
@@ -32,6 +33,7 @@ struct WithdrawURIView: View {
                     let raw = withdrawalAmountDetails.amountRaw
                     let effective = withdrawalAmountDetails.amountEffective
                     let currency = raw.currencyStr
+                    let currencyInfo = controller.info(for: currency, 
controller.currencyTicker)
                     let fee = try! Amount.diff(raw, effective)
                     let outColor = WalletColors().transactionColor(false)
                     let inColor = WalletColors().transactionColor(true)
@@ -46,8 +48,9 @@ struct WithdrawURIView: View {
                                    baseURL: exchangeBaseUrl)
                     let someCoins = SomeCoins(details: withdrawalAmountDetails)
                     QuiteSomeCoins(someCoins: someCoins,
-                               shouldShowFee: true,           // TODO: set to 
false if we never charge withdrawal fees
-                                    currency: raw.currencyStr,
+                               shouldShowFee: true,       // TODO: set to 
false if we never charge withdrawal fees
+                                    currency: currency,
+                                currencyInfo: currencyInfo,
                              amountEffective: effective)
                 }
                 .listStyle(myListStyle.style).anyView
@@ -83,13 +86,10 @@ struct WithdrawURIView: View {
             do { // TODO: cancelled
                 symLog.log(".task")
                 let withdrawUriInfo = try await 
model.loadWithdrawalDetailsForUriM(url.absoluteString)
-                let amount = withdrawUriInfo.amount
-                if let baseURL = withdrawUriInfo.defaultExchangeBaseUrl {
-                    exchangeBaseUrl = baseURL
-                } else if let first = withdrawUriInfo.possibleExchanges.first {
-                    exchangeBaseUrl = first.exchangeBaseUrl
-                }
+                exchangeBaseUrl = withdrawUriInfo.defaultExchangeBaseUrl ??
+                                  
withdrawUriInfo.possibleExchanges.first?.exchangeBaseUrl
                 if let exchangeBaseUrl {
+                    let amount = withdrawUriInfo.amount
                     let details = try await 
model.loadWithdrawalDetailsForAmountM(exchangeBaseUrl, amount: amount)
                     withdrawalAmountDetails = details
 //                  agePicker.setAges(ages: details?.ageRestrictionOptions)
diff --git a/TalerWallet1/Views/Transactions/ManualDetailsV.swift 
b/TalerWallet1/Views/Transactions/ManualDetailsV.swift
index 44fc7cf..e4400b9 100644
--- a/TalerWallet1/Views/Transactions/ManualDetailsV.swift
+++ b/TalerWallet1/Views/Transactions/ManualDetailsV.swift
@@ -16,10 +16,10 @@ struct ManualDetailsV: View {
             let payto = paytoUris[0]
             let payURL = URL(string: payto)
             let iban = payURL?.iban ?? "unknown IBAN"
-            let amount = common.amountRaw.readableDescription
+            let amountStr = common.amountRaw.readableDescription        // 
TODO: formatter
             Group {
-                Text(iconOnly ? "Transfer \(amount) to the Exchange."
-                              : "You need to transfer \(amount) from your 
regular bank account to the Exchange.")
+                Text(iconOnly ? "Transfer \(amountStr) to the Exchange."
+                              : "You need to transfer \(amountStr) from your 
regular bank account to the Exchange.")
                 Text(iconOnly ? "Step 1: Copy+Paste this subject:"
                               : "Step 1: Copy this code and paste it into the 
subject/purpose field in your banking app or bank website:")
                         .multilineTextAlignment(.leading)
@@ -55,8 +55,8 @@ struct ManualDetailsV: View {
                 }   .padding(.leading)
                     .padding(.top, -8)
                     .listRowSeparator(.hidden)
-                Text(iconOnly ? "Step 3: Transfer \(amount)."
-                              : "Step 3: Finish the wire transfer of \(amount) 
in your banking app or website, then this withdrawal will proceed 
automatically.")
+                Text(iconOnly ? "Step 3: Transfer \(amountStr)."
+                              : "Step 3: Finish the wire transfer of 
\(amountStr) in your banking app or website, then this withdrawal will proceed 
automatically.")
                     .multilineTextAlignment(.leading)
                     .listRowSeparator(.visible)
                 Text(iconOnly ? "Or use this PayTo-Link:"
diff --git a/TalerWallet1/Views/Transactions/TransactionDetailView.swift 
b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
index 31e8780..661d354 100644
--- a/TalerWallet1/Views/Transactions/TransactionDetailView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
@@ -22,20 +22,10 @@ extension Transaction {             // for Dummys
 }
 // MARK: -
 struct TransactionDetailView: View {
-    private let symLog = SymLogV(0)
+    private let symLog = SymLogV()
     let stack: CallStack
-    @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
-//    @AppStorage("moreContrast") var moreContrast: Bool = false
-#if DEBUG
-    @AppStorage("developerMode") var developerMode: Bool = true
-#else
-    @AppStorage("developerMode") var developerMode: Bool = false
-#endif
-    @Environment(\.colorSchemeContrast) private var colorSchemeContrast
-
     let transactionId: String
     let reloadAction: ((_ transactionId: String) async throws -> Transaction)
-
     let navTitle: String?
     let doneAction: (() -> Void)?
     let abortAction: ((_ transactionId: String) async throws -> Void)?
@@ -44,6 +34,15 @@ struct TransactionDetailView: View {
     let suspendAction: ((_ transactionId: String) async throws -> Void)?
     let resumeAction: ((_ transactionId: String) async throws -> Void)?
 
+    @Environment(\.colorSchemeContrast) private var colorSchemeContrast
+    @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
+//    @AppStorage("moreContrast") var moreContrast: Bool = false
+#if DEBUG
+    @AppStorage("developerMode") var developerMode: Bool = true
+#else
+    @AppStorage("developerMode") var developerMode: Bool = false
+#endif
+
     @State var transaction: Transaction = Transaction(dummyCurrency: 
DEMOCURRENCY)
     @State var viewId = UUID()
 
@@ -56,6 +55,48 @@ struct TransactionDetailView: View {
         return nil
     }
 
+    func loadTransaction() async {
+        do {
+            let reloadedTransaction = try await reloadAction(transactionId)
+            symLog.log("reloaded transaction: 
\(reloadedTransaction.common.txState.major)")
+            withAnimation() { transaction = reloadedTransaction; viewId = 
UUID() }      // redraw
+        } catch {
+            symLog.log(error.localizedDescription)
+            withAnimation() { transaction = Transaction(dummyCurrency: 
DEMOCURRENCY); viewId = UUID() }
+        }
+    }
+
+    @discardableResult
+    func checkDismiss(_ notification: Notification, _ logStr: String = "") -> 
Bool {
+        if let doneAction {
+            if let transition = notification.userInfo?[TRANSACTIONTRANSITION] 
as? TransactionTransition {
+                if transition.transactionId == 
transaction.common.transactionId {       // is the transition for THIS 
transaction?
+                    symLog.log(logStr)
+                    doneAction()        // if this view is in a sheet then 
dissmiss the sheet
+                    return true
+                }
+            }
+        }
+        return false
+    }
+
+    func checkReload(_ notification: Notification, _ logStr: String = "") {
+        if let transition = notification.userInfo?[TRANSACTIONTRANSITION] as? 
TransactionTransition {
+            if transition.transactionId == transactionId {       // is the 
transition for THIS transaction?
+                let newMajor = transition.newTxState.major
+                Task { // runs on MainActor
+                       // flush the screen first, then reload
+                    withAnimation() { transaction = Transaction(dummyCurrency: 
DEMOCURRENCY); viewId = UUID() }
+                    symLog.log("newState: \(newMajor), reloading transaction")
+                    await loadTransaction()
+                }
+            }
+        } else { // Yikes - should never happen
+// TODO:      logger.warning("Can't get notification.userInfo as 
TransactionTransition")
+            symLog.log(notification.userInfo as Any)
+        }
+    }
+
     var body: some View {
 #if DEBUG
         let _ = Self._printChanges()
@@ -109,68 +150,36 @@ struct TransactionDetailView: View {
                 } } // Delete button
             }.id(viewId)    // change viewId to enforce a draw update
             .listStyle(myListStyle.style).anyView
-                .safeAreaInset(edge: .bottom) {
-                    if let doneAction {
-                        Button(transaction.shouldConfirm ? "Confirm later" : 
"Done", action: doneAction)
-                            .buttonStyle(TalerButtonStyle(type: 
transaction.shouldConfirm ? .bordered : .prominent))
-                            .padding(.horizontal)
-                    }
-                }
-        }.onNotification(.TransactionStateTransition) { notification in
-            if let transition = notification.userInfo?[TRANSACTIONTRANSITION] 
as? TransactionTransition {
-                if transition.transactionId == common.transactionId {       // 
is the transition for THIS transaction?
-                    let newMajor = transition.newTxState.major
-                    let newMinor = transition.newTxState.minor
-                    if let doneAction {
-                        if newMajor == .done {
-                            symLog.log("newTxState.major == done  => dismiss 
sheet")
-// TODO:                           logger.info("newTxState.major == done  => 
dismiss sheet")
-                            doneAction()        // if this view is in a sheet 
this action will dissmiss it
-                        } else if newMajor == .expired {
-                            symLog.log("newTxState.major == expired  => 
dismiss sheet")
-// TODO:                           logger.info("newTxState.major == expired  
=> dismiss sheet")
-                            doneAction()        // if this view is in a sheet 
this action will dissmiss it
-                        } else if newMajor == .pending {
-                            if let newMinor {
-                                if newMinor == .exchangeWaitReserve { // user 
did confirm on bank website
-                                    symLog.log("newTxState.minor == 
exchangeWaitReserve => dismiss sheet")
-                                    doneAction()        // if this view is in 
a sheet this action will dissmiss it
-                                } else if newMinor == .withdrawCoins { // 
coin-withdrawal has started
-                                    symLog.log("newTxState.minor == 
withdrawCoins => dismiss sheet")
-                                    doneAction()        // if this view is in 
a sheet this action will dissmiss it
-                                } else {
-                                    symLog.log("ignoring newTxState: 
\(newMajor):\(newMinor)")
-                                }
-                            }
-                        } else {
-                            symLog.log("ignoring newTxState.major: 
\(newMajor)")
-                        }
-                    } else { Task { // runs on MainActor
-                        do {
-                            symLog.log("newState: \(newMajor), reloading 
transaction")
-                            withAnimation() { transaction = 
Transaction(dummyCurrency: DEMOCURRENCY); viewId = UUID() }
-                            let reloadedTransaction = try await 
reloadAction(common.transactionId)
-                            symLog.log("reloaded transaction: 
\(reloadedTransaction.common.txState.major)")
-                            withAnimation() { transaction = 
reloadedTransaction; viewId = UUID() }      // redraw
-                      } catch {
-                          symLog.log(error.localizedDescription)
-                    }}}
+            .safeAreaInset(edge: .bottom) {
+                if let doneAction {
+                    Button(transaction.shouldConfirm ? "Confirm later" : 
"Done", action: doneAction)
+                        .buttonStyle(TalerButtonStyle(type: 
transaction.shouldConfirm ? .bordered : .prominent))
+                        .padding(.horizontal)
                 }
-            } else { // Yikes - should never happen
-// TODO:                logger.warning("Can't get notification.userInfo as 
TransactionTransition")
-                symLog.log(notification.userInfo as Any)
             }
+        } // Group
+        .onNotification(.TransactionExpired) { notification in
+            // TODO: Alert user that this tx just expired
+            if checkDismiss(notification, "newTxState.major == expired  => 
dismiss sheet") {
+    // TODO:                  logger.info("newTxState.major == expired  => 
dismiss sheet")
+            }
+        }
+        .onNotification(.TransactionDone) { notification in
+            checkDismiss(notification, "newTxState.major == done  => dismiss 
sheet")
+        }
+        .onNotification(.DismissSheet) { notification in
+            checkDismiss(notification, "exchangeWaitReserve or withdrawCoins  
=> dismiss sheet")
+        }
+        .onNotification(.PendingReady) { notification in
+            checkReload(notification, "pending ready ==> reload for talerURI")
+        }
+        .onNotification(.TransactionStateTransition) { notification in
+            checkReload(notification, "some transition ==> reload")
         }
         .navigationTitle(navTitle ?? navTitle2)
         .task {
-            do {
-                symLog.log("task - load transaction")
-                let reloadedTransaction = try await reloadAction(transactionId)
-                withAnimation() { transaction = reloadedTransaction; viewId = 
UUID() }      // redraw
-            } catch {
-                symLog.log(error)
-                withAnimation() { transaction = Transaction(dummyCurrency: 
DEMOCURRENCY); viewId = UUID() }
-            }
+            symLog.log("task - load transaction")
+            await loadTransaction()
         }
         .onAppear {
             symLog.log("onAppear")
@@ -218,13 +227,27 @@ struct TransactionDetailView: View {
                                                             
.listRowSeparator(.hidden)
                                                     }
                                                     Link("Confirm with bank", 
destination: destination)
-                                                        
.buttonStyle(TalerButtonStyle(type: .prominent, narrow: false, aligned: 
.center))
-                                                        .padding(.horizontal)  
                                                  
+                                                        
.buttonStyle(TalerButtonStyle(type: .prominent, badge: CONFIRM_BANK))
+                                                }
+                                            }
+                                        }
+                                    } else 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))
                                                 }
                                             }
                                         }
                                     }
-                            }
+                            } // switch
                         } // ManualDetails or Confirm with bank
                         ThreeAmountsSheet(common: common, topAbbrev: 
String(localized: "Chosen:"),
                                         topTitle: String(localized: "Chosen 
amount to withdraw:"),
@@ -261,12 +284,18 @@ struct TransactionDetailView: View {
                             .padding(.bottom)
                         // TODO: isSendCoins should show QR only while not yet 
expired  - either set timer or wallet-core should do so and send a 
state-changed notification
                         if pending {
-                            QRCodeDetails(transaction: transaction)
-                            if hasDone {
-                                Text("QR code and link can also be scanned or 
copied / shared from Transactions later.")
+                            if transaction.isPendingReady {
+                                QRCodeDetails(transaction: transaction)
+                                if hasDone {
+                                    Text("QR code and link can also be scanned 
or copied / shared from Transactions later.")
+                                        .multilineTextAlignment(.leading)
+                                        .accessibilityFont(.subheadline)
+                                        .padding(.top)
+                                }
+                            } else {
+                                Text("This transaction is not yet ready...")
                                     .multilineTextAlignment(.leading)
                                     .accessibilityFont(.subheadline)
-                                    .padding(.top)
                             }
                         }
                         let colon = ":"
diff --git a/TalerWallet1/Views/Transactions/TransactionRowView.swift 
b/TalerWallet1/Views/Transactions/TransactionRowView.swift
index e3d71ab..b618c66 100644
--- a/TalerWallet1/Views/Transactions/TransactionRowView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionRowView.swift
@@ -5,11 +5,33 @@
 import SwiftUI
 import taler_swift
 
+struct IconBadge: View {
+    let imageName: String
+    let foreColor:Color
+    let shouldConfirm: Bool
+    let needsKYC: Bool
+
+    var body: some View {
+        let badgeName = needsKYC ? NEEDS_KYC
+                                 : CONFIRM_BANK
+        HStack(alignment: .top, spacing: -8) {      // TODO: adapt spacing to 
dynamic fontsize
+            Image(systemName: imageName)
+                .foregroundColor(foreColor)
+                .accessibilityFont(.largeTitle)
+                .accessibility(hidden: true)
+            Image(systemName: badgeName)
+                .accessibilityFont(.caption)
+                .foregroundColor((needsKYC || shouldConfirm) ? .red : .clear)
+        }.accessibilityHidden(true)
+    }
+}
 struct TransactionRowContentV: View {
     var centerTop: String
     var centerBottom: String
     let isHorizontal: Bool
     let incoming: Bool
+    let shouldConfirm: Bool
+    let needsKYC: Bool
     let foreColor:Color
 
     public static func width(titles: (String, String?), isHorizontal: Bool,
@@ -39,12 +61,9 @@ struct TransactionRowContentV: View {
     var body: some View {
         let imageName = incoming ? "plus.circle.fill"
                                  : "minus.circle.fill"
+        let iconBadge = IconBadge(imageName: imageName, foreColor: foreColor, 
shouldConfirm: shouldConfirm, needsKYC: needsKYC)
         HStack(spacing: 8) {
-            Image(systemName: imageName)
-                .foregroundColor(foreColor)
-                .accessibilityFont(.largeTitle)
-                .accessibility(hidden: true)
-
+            iconBadge
             VStack(alignment: .leading) {
                 Text(centerTop)
                     .accessibilityFont(.headline)
@@ -72,6 +91,8 @@ struct TransactionRowView: View {
         let common = transaction.common
         let amount = common.amountEffective
         let pending = transaction.isPending
+        let needsKYC = transaction.isPendingKYC
+        let shouldConfirm = transaction.shouldConfirm
         let done = transaction.isDone
         let details = transaction.detailsToShow()
         let keys = details.keys
@@ -95,9 +116,13 @@ struct TransactionRowView: View {
                            fitsHorizontal: !needVStack, vertAlignment: 
.center) {
 
                     TransactionRowContentV(centerTop: 
transaction.localizedType,
-                                           centerBottom: dateString, 
isHorizontal: true, incoming: incoming, foreColor: foreColor)
+                                        centerBottom: dateString, 
isHorizontal: true,
+                                            incoming: incoming,  
shouldConfirm: shouldConfirm,
+                                            needsKYC: needsKYC,      
foreColor: foreColor)
                 }
                     .accessibilityElement(children: .combine)
+                    .accessibilityValue(needsKYC ? ". Needs K Y C" :
+                                   shouldConfirm ? ". Needs bank confirmation" 
: EMPTYSTRING)
                     .accessibilityHint("Will go to detail view.")
             }
         }
diff --git a/TestFlight/WhatToTest.en-US.txt b/TestFlight/WhatToTest.en-US.txt
index 81f22ce..75405e3 100644
--- a/TestFlight/WhatToTest.en-US.txt
+++ b/TestFlight/WhatToTest.en-US.txt
@@ -1,4 +1,25 @@
 
+Version 0.9.3 (27)
+
+• New feature: Badges for KYC and Confirm with bank (for bank-integrated 
withdrawals)
+    start a withdrawal from the bank website, but then don't "Confirm with 
bank" immediately but press "Confirm later"
+    request some money, tap on Done without sharing or scanning the talerURI 
QR.
+    Notice the difference in the Pending list
+• KYC in pending transactions accessible from details
+
+- 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+.
+
+
+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
+- The whole textfield can now be used to bring up the keyboard in P2P
+
+
 Version 0.9.3 (25)
 
 • New feature: Shortcuts Buttons (50,25,10,5) for P2P + Withdrawal
diff --git a/taler-swift/Sources/taler-swift/Amount.swift 
b/taler-swift/Sources/taler-swift/Amount.swift
index 1751b20..0d5a9ba 100644
--- a/taler-swift/Sources/taler-swift/Amount.swift
+++ b/taler-swift/Sources/taler-swift/Amount.swift
@@ -127,11 +127,12 @@ public final class Amount: Codable, Hashable, @unchecked 
Sendable, CustomStringC
         if fraction == 0 {
             return String(integer)
         } else {
-            var frac = fraction
+            var frac = UInt64(fraction)
+            let base = UInt64(fractionalBase())
             var fracStr = ""
             while (frac > 0) {
-                fracStr += String(frac / (fractionalBase() / 10))
-                frac = (frac * 10) % fractionalBase()
+                fracStr += String(frac / ( base / 10))
+                frac = (frac * 10) % base
             }
             return "\(integer)\(Self.decimalSeparator)\(fracStr)"
         }

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