[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.
- [taler-taler-ios] branch master updated (e79881c -> b01070b),
gnunet <=
- [taler-taler-ios] 02/30: Use "nu" (instead of "te") for Null, gnunet, 2023/11/19
- [taler-taler-ios] 04/30: cleanup, debugging, gnunet, 2023/11/19
- [taler-taler-ios] 05/30: ScrollVStack, cleanup, gnunet, 2023/11/19
- [taler-taler-ios] 01/30: Bugfix: Arithmetic error, gnunet, 2023/11/19
- [taler-taler-ios] 06/30: Notifications, gnunet, 2023/11/19
- [taler-taler-ios] 19/30: tabbed button strings, gnunet, 2023/11/19
- [taler-taler-ios] 18/30: cleanup, gnunet, 2023/11/19
- [taler-taler-ios] 07/30: KYC, gnunet, 2023/11/19
- [taler-taler-ios] 03/30: Dismiss keyboard, gnunet, 2023/11/19
- [taler-taler-ios] 23/30: unify P2P subject, gnunet, 2023/11/19