[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-taler-ios] branch master updated (7d32de8 -> 8b2cfb1)
From: |
gnunet |
Subject: |
[taler-taler-ios] branch master updated (7d32de8 -> 8b2cfb1) |
Date: |
Tue, 19 Sep 2023 03:44:19 +0200 |
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 7d32de8 Font Settings
new 551285d Haptics
new 66e81ea move to HelperViews
new c39cd35 talerFonts
new dbf95e6 Font
new 78bb026 Terms of Service
new 7a069e5 Black
new 2c61b55 DebugView static font size
new 112dfef Binding+onChange
new 5c15352 move AgePicker
new 241f614 ExchangeTosStatus
new 44ea233 AccessibleFont
new 43d27dc Italic
new 275d69e Comment
new 42d0f27 ToSButton
new 9f7b1e5 Speed up Sidebar
new f8cbf4d ScreenSize
new d948bb6 Bargraph shows the last transactions visually
new f54e0f8 MainActor for Swift 6
new cb08027 Sendable for Swift 6
new 880bc04 Wording: don't use "encrypt"
new 5360255 test.taler.net now uses KUDOS (instead of TESTKUDOS)
new 027354f CallStack
new 75df49f logging
new f1ee0e8 more CallStack debugging
new ec15415 simplify
new dc8b888 fix warning
new e46e434 simplify log
new 8b2cfb1 !!! Temporarily add back old cases to ExchangeTosStatus to be
able to run with older wallet-core version !!!
The 28 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:
GNU_Taler Info.plist | 2 +
TalerWallet.xcodeproj/project.pbxproj | 52 +++-
TalerWallet1/Backend/Transaction.swift | 24 +-
TalerWallet1/Backend/WalletCore.swift | 14 +-
TalerWallet1/Controllers/Controller.swift | 3 +
TalerWallet1/Controllers/DebugViewC.swift | 2 +-
TalerWallet1/Controllers/PublicConstants.swift | 3 +-
TalerWallet1/Controllers/TalerWallet1App.swift | 2 +-
TalerWallet1/Fonts/Nunito/Nunito-Black.ttf | Bin 0 -> 131568 bytes
TalerWallet1/Fonts/Nunito/Nunito-BlackItalic.ttf | Bin 0 -> 134488 bytes
...boardResponder.swift => Binding+onChange.swift} | 46 +--
TalerWallet1/Helper/CallStack.swift | 89 ++++++
TalerWallet1/Helper/Controller+playSound.swift | 7 +-
TalerWallet1/Helper/Font+Taler.swift | 317 +++++++++++++++------
TalerWallet1/Helper/KeyboardResponder.swift | 1 +
TalerWallet1/Helper/UIScreen+screenSize.swift | 11 +
TalerWallet1/Helper/View+Notification.swift | 2 +-
TalerWallet1/Helper/View+dismissTop.swift | 2 +-
TalerWallet1/Helper/View+flippedDirection.swift | 21 ++
TalerWallet1/Model/Model+Balances.swift | 6 +-
TalerWallet1/Model/Model+Exchange.swift | 26 +-
TalerWallet1/Model/Model+Transactions.swift | 6 +-
TalerWallet1/Model/Model+Withdraw.swift | 3 +
TalerWallet1/Model/WalletModel.swift | 2 +-
TalerWallet1/Views/Balances/BalanceRowView.swift | 17 +-
TalerWallet1/Views/Balances/BalancesListView.swift | 33 ++-
.../Views/Balances/BalancesSectionView.swift | 100 ++++---
TalerWallet1/Views/Balances/PendingRowView.swift | 14 +-
.../Views/Balances/UncompletedRowView.swift | 2 +-
TalerWallet1/Views/Exchange/ExchangeListView.swift | 18 +-
.../Views/Exchange/ExchangeSectionView.swift | 33 ++-
TalerWallet1/Views/Exchange/ManualWithdraw.swift | 40 ++-
.../Views/Exchange/ManualWithdrawDone.swift | 35 +--
TalerWallet1/Views/Exchange/QuiteSomeCoins.swift | 10 +-
.../{Helper => Views/HelperViews}/AgePicker.swift | 3 +-
TalerWallet1/Views/HelperViews/AmountView.swift | 6 +-
TalerWallet1/Views/HelperViews/BarGraph.swift | 87 ++++++
TalerWallet1/Views/HelperViews/Buttons.swift | 29 +-
TalerWallet1/Views/HelperViews/CopyShare.swift | 3 +
TalerWallet1/Views/HelperViews/CurrencyField.swift | 6 +-
.../Views/HelperViews/CurrencyInputView.swift | 4 +-
.../Views/HelperViews/LaunchAnimationView.swift | 2 +-
.../Views/HelperViews/QRCodeDetailView.swift | 5 +-
.../Views/HelperViews/TextFieldAlert.swift | 5 +-
TalerWallet1/Views/HelperViews/ToSButtonView.swift | 35 +++
.../Views/HelperViews/TransactionButton.swift | 4 +-
TalerWallet1/Views/Main/MainView.swift | 45 ++-
TalerWallet1/Views/Main/SideBarView.swift | 20 +-
TalerWallet1/Views/Main/WalletEmptyView.swift | 2 +-
TalerWallet1/Views/Payment/PayTemplateView.swift | 5 +-
TalerWallet1/Views/Payment/PaymentView.swift | 9 +-
TalerWallet1/Views/Peer2peer/PaymentPurpose.swift | 21 +-
TalerWallet1/Views/Peer2peer/RequestPayment.swift | 14 +-
TalerWallet1/Views/Peer2peer/SendAmount.swift | 24 +-
TalerWallet1/Views/Peer2peer/SendDone.swift | 31 +-
TalerWallet1/Views/Peer2peer/SendPurpose.swift | 27 +-
.../Views/Settings/Pending/PendingOpView.swift | 35 +--
TalerWallet1/Views/Settings/SettingsItem.swift | 25 +-
TalerWallet1/Views/Settings/SettingsView.swift | 30 +-
.../Views/Sheets/P2P_Sheets/P2pAcceptDone.swift | 26 +-
.../Views/Sheets/P2P_Sheets/P2pPayURIView.swift | 8 +-
.../Sheets/P2P_Sheets/P2pReceiveURIView.swift | 22 +-
TalerWallet1/Views/Sheets/QRSheet.swift | 5 +-
TalerWallet1/Views/Sheets/ShareSheet.swift | 2 +-
TalerWallet1/Views/Sheets/Sheet.swift | 9 +-
TalerWallet1/Views/Sheets/URLSheet.swift | 15 +-
.../Views/Transactions/ManualDetails.swift | 91 +++---
TalerWallet1/Views/Transactions/ThreeAmounts.swift | 4 +-
.../Views/Transactions/TransactionDetailView.swift | 23 +-
.../Views/Transactions/TransactionRowView.swift | 10 +-
.../Views/Transactions/TransactionsEmptyView.swift | 4 +-
.../Views/Transactions/TransactionsListView.swift | 22 +-
.../WithdrawAcceptDone.swift | 27 +-
.../WithdrawAcceptView.swift | 2 +-
.../WithdrawProgressView.swift | 2 +-
.../WithdrawBankIntegrated/WithdrawTOSView.swift | 13 +-
.../WithdrawBankIntegrated/WithdrawURIView.swift | 21 +-
Taler_Wallet Info.plist | 2 +
taler-swift/Sources/taler-swift/Amount.swift | 12 +-
taler-swift/Sources/taler-swift/Time.swift | 2 +-
80 files changed, 1157 insertions(+), 585 deletions(-)
create mode 100644 TalerWallet1/Fonts/Nunito/Nunito-Black.ttf
create mode 100644 TalerWallet1/Fonts/Nunito/Nunito-BlackItalic.ttf
copy TalerWallet1/Helper/{KeyboardResponder.swift => Binding+onChange.swift}
(53%)
create mode 100644 TalerWallet1/Helper/CallStack.swift
create mode 100644 TalerWallet1/Helper/UIScreen+screenSize.swift
create mode 100644 TalerWallet1/Helper/View+flippedDirection.swift
rename TalerWallet1/{Helper => Views/HelperViews}/AgePicker.swift (94%)
create mode 100644 TalerWallet1/Views/HelperViews/BarGraph.swift
create mode 100644 TalerWallet1/Views/HelperViews/ToSButtonView.swift
diff --git a/GNU_Taler Info.plist b/GNU_Taler Info.plist
index 5c6173c..b6dac8f 100644
--- a/GNU_Taler Info.plist
+++ b/GNU_Taler Info.plist
@@ -32,6 +32,8 @@
<string>Atkinson-Hyperlegible-Italic-102.otf</string>
<string>Nunito-Regular.ttf</string>
<string>Nunito-Bold.ttf</string>
+ <string>Nunito-Black.ttf</string>
+ <string>Nunito-BlackItalic.ttf</string>
<string>Nunito-BoldItalic.ttf</string>
<string>Nunito-Italic.ttf</string>
</array>
diff --git a/TalerWallet.xcodeproj/project.pbxproj
b/TalerWallet.xcodeproj/project.pbxproj
index aa9e2a0..4d6133d 100644
--- a/TalerWallet.xcodeproj/project.pbxproj
+++ b/TalerWallet.xcodeproj/project.pbxproj
@@ -130,6 +130,8 @@
4E3EAEA32AA12582009F1BE8 /* Nunito-Italic.ttf in Resources */ =
{isa = PBXBuildFile; fileRef = 4E3EAEA02AA12582009F1BE8 /* Nunito-Italic.ttf
*/; };
4E3EAEA42AA12582009F1BE8 /* Nunito-BoldItalic.ttf in Resources
*/ = {isa = PBXBuildFile; fileRef = 4E3EAEA12AA12582009F1BE8 /*
Nunito-BoldItalic.ttf */; };
4E3EAEA52AA12582009F1BE8 /* Nunito-BoldItalic.ttf in Resources
*/ = {isa = PBXBuildFile; fileRef = 4E3EAEA12AA12582009F1BE8 /*
Nunito-BoldItalic.ttf */; };
+ 4E3EAEA82AA70157009F1BE8 /* Binding+onChange.swift in Sources
*/ = {isa = PBXBuildFile; fileRef = 4E3EAEA72AA70157009F1BE8 /*
Binding+onChange.swift */; };
+ 4E3EAEA92AA70157009F1BE8 /* Binding+onChange.swift in Sources
*/ = {isa = PBXBuildFile; fileRef = 4E3EAEA72AA70157009F1BE8 /*
Binding+onChange.swift */; };
4E40E0BE29F25ABB00B85369 /* SendAmount.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E40E0BD29F25ABB00B85369 /* SendAmount.swift */;
};
4E50B3502A1BEE8000F9F01C /* ManualWithdraw.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4E50B34F2A1BEE8000F9F01C /*
ManualWithdraw.swift */; };
4E53A33729F50B7B00830EC2 /* CurrencyField.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E53A33629F50B7B00830EC2 /* CurrencyField.swift
*/; };
@@ -137,6 +139,16 @@
4E578E942A4822D500F21F1C /* P2pPayURIView.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E578E932A4822D500F21F1C /* P2pPayURIView.swift
*/; };
4E5A88F52A38A4FD00072618 /* QRCodeDetailView.swift in Sources
*/ = {isa = PBXBuildFile; fileRef = 4E5A88F42A38A4FD00072618 /*
QRCodeDetailView.swift */; };
4E5A88F72A3B9E5B00072618 /* WithdrawAcceptDone.swift in Sources
*/ = {isa = PBXBuildFile; fileRef = 4E5A88F62A3B9E5B00072618 /*
WithdrawAcceptDone.swift */; };
+ 4E605D902AA8B407002FB9A7 /* Nunito-Black.ttf in Resources */ =
{isa = PBXBuildFile; fileRef = 4E605D8E2AA8B407002FB9A7 /* Nunito-Black.ttf */;
};
+ 4E605D912AA8B407002FB9A7 /* Nunito-Black.ttf in Resources */ =
{isa = PBXBuildFile; fileRef = 4E605D8E2AA8B407002FB9A7 /* Nunito-Black.ttf */;
};
+ 4E605D922AA8B407002FB9A7 /* Nunito-BlackItalic.ttf in Resources
*/ = {isa = PBXBuildFile; fileRef = 4E605D8F2AA8B407002FB9A7 /*
Nunito-BlackItalic.ttf */; };
+ 4E605D932AA8B407002FB9A7 /* Nunito-BlackItalic.ttf in Resources
*/ = {isa = PBXBuildFile; fileRef = 4E605D8F2AA8B407002FB9A7 /*
Nunito-BlackItalic.ttf */; };
+ 4E605DAF2AADDD13002FB9A7 /* UIScreen+screenSize.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4E605DAE2AADDD13002FB9A7 /*
UIScreen+screenSize.swift */; };
+ 4E605DB02AADDD13002FB9A7 /* UIScreen+screenSize.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4E605DAE2AADDD13002FB9A7 /*
UIScreen+screenSize.swift */; };
+ 4E605DB72AB05E48002FB9A7 /* View+flippedDirection.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4E605DB62AB05E48002FB9A7 /*
View+flippedDirection.swift */; };
+ 4E605DB82AB05E48002FB9A7 /* View+flippedDirection.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4E605DB62AB05E48002FB9A7 /*
View+flippedDirection.swift */; };
+ 4E605DBA2AB05FB6002FB9A7 /* BarGraph.swift in Sources */ = {isa
= PBXBuildFile; fileRef = 4E605DB92AB05FB6002FB9A7 /* BarGraph.swift */; };
+ 4E605DBB2AB05FB6002FB9A7 /* BarGraph.swift in Sources */ = {isa
= PBXBuildFile; fileRef = 4E605DB92AB05FB6002FB9A7 /* BarGraph.swift */; };
4E6EDD852A3615BE0031D520 /* ManualDetails.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E6EDD842A3615BE0031D520 /* ManualDetails.swift
*/; };
4E6EDD872A363D8D0031D520 /* ListStyle.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E6EDD862A363D8D0031D520 /* ListStyle.swift */;
};
4E753A062A0952F8002D9328 /* DebugViewC.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E753A052A0952F7002D9328 /* DebugViewC.swift */;
};
@@ -217,11 +229,15 @@
4ECB62802A0BA6DF004ABBB7 /* Model+P2P.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4ECB627F2A0BA6DF004ABBB7 /* Model+P2P.swift */;
};
4ECB62822A0BB01D004ABBB7 /* SelectDays.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */;
};
4ED2F94B2A278F5100453B40 /* ThreeAmounts.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4ED2F94A2A278F5100453B40 /* ThreeAmounts.swift
*/; };
+ 4EDBDCD92AB787CB00925C02 /* CallStack.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EDBDCD82AB787CB00925C02 /* CallStack.swift */;
};
+ 4EDBDCDA2AB787CB00925C02 /* CallStack.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EDBDCD82AB787CB00925C02 /* CallStack.swift */;
};
4EEC157329F8242800D46A03 /* QRGeneratorView.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4EEC157229F8242800D46A03 /*
QRGeneratorView.swift */; };
4EEC157629F8ECBF00D46A03 /* CodeScanner in Frameworks */ = {isa
= PBXBuildFile; productRef = 4EEC157529F8ECBF00D46A03 /* CodeScanner */; };
4EEC157829F9032900D46A03 /* Sheet.swift in Sources */ = {isa =
PBXBuildFile; fileRef = 4EEC157729F9032900D46A03 /* Sheet.swift */; };
4EEC157A29F9427F00D46A03 /* QRSheet.swift in Sources */ = {isa
= PBXBuildFile; fileRef = 4EEC157929F9427F00D46A03 /* QRSheet.swift */; };
4EF840A72A0B85F400EE0D47 /* CopyShare.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EF840A62A0B85F400EE0D47 /* CopyShare.swift */;
};
+ 4EFA39602AA7946B00742548 /* ToSButtonView.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EFA395F2AA7946B00742548 /* ToSButtonView.swift
*/; };
+ 4EFA39612AA7946B00742548 /* ToSButtonView.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EFA395F2AA7946B00742548 /* ToSButtonView.swift
*/; };
ABC13AA32859962800D23185 /* taler-swift in Frameworks */ = {isa
= PBXBuildFile; productRef = ABC13AA22859962800D23185 /* taler-swift */; };
ABE97B1D286D82BF00580772 /* AnyCodable in Frameworks */ = {isa
= PBXBuildFile; productRef = ABE97B1C286D82BF00580772 /* AnyCodable */; };
/* End PBXBuildFile section */
@@ -283,6 +299,7 @@
4E3EAE9B2AA12467009F1BE8 /* Nunito-Bold.ttf */ = {isa =
PBXFileReference; lastKnownFileType = file; path = "Nunito-Bold.ttf";
sourceTree = "<group>"; };
4E3EAEA02AA12582009F1BE8 /* Nunito-Italic.ttf */ = {isa =
PBXFileReference; lastKnownFileType = file; path = "Nunito-Italic.ttf";
sourceTree = "<group>"; };
4E3EAEA12AA12582009F1BE8 /* Nunito-BoldItalic.ttf */ = {isa =
PBXFileReference; lastKnownFileType = file; path = "Nunito-BoldItalic.ttf";
sourceTree = "<group>"; };
+ 4E3EAEA72AA70157009F1BE8 /* Binding+onChange.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= "Binding+onChange.swift"; sourceTree = "<group>"; };
4E40E0BD29F25ABB00B85369 /* SendAmount.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= SendAmount.swift; sourceTree = "<group>"; };
4E50B34F2A1BEE8000F9F01C /* ManualWithdraw.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= ManualWithdraw.swift; sourceTree = "<group>"; };
4E53A33629F50B7B00830EC2 /* CurrencyField.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= CurrencyField.swift; sourceTree = "<group>"; };
@@ -290,6 +307,11 @@
4E578E932A4822D500F21F1C /* P2pPayURIView.swift */ = {isa =
PBXFileReference; lastKnownFileType = sourcecode.swift; path =
P2pPayURIView.swift; sourceTree = "<group>"; };
4E5A88F42A38A4FD00072618 /* QRCodeDetailView.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= QRCodeDetailView.swift; sourceTree = "<group>"; };
4E5A88F62A3B9E5B00072618 /* WithdrawAcceptDone.swift */ = {isa
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift;
path = WithdrawAcceptDone.swift; sourceTree = "<group>"; };
+ 4E605D8E2AA8B407002FB9A7 /* Nunito-Black.ttf */ = {isa =
PBXFileReference; lastKnownFileType = file; path = "Nunito-Black.ttf";
sourceTree = "<group>"; };
+ 4E605D8F2AA8B407002FB9A7 /* Nunito-BlackItalic.ttf */ = {isa =
PBXFileReference; lastKnownFileType = file; path = "Nunito-BlackItalic.ttf";
sourceTree = "<group>"; };
+ 4E605DAE2AADDD13002FB9A7 /* UIScreen+screenSize.swift */ = {isa
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift;
path = "UIScreen+screenSize.swift"; sourceTree = "<group>"; };
+ 4E605DB62AB05E48002FB9A7 /* View+flippedDirection.swift */ =
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType =
sourcecode.swift; path = "View+flippedDirection.swift"; sourceTree = "<group>";
};
+ 4E605DB92AB05FB6002FB9A7 /* BarGraph.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= BarGraph.swift; sourceTree = "<group>"; };
4E6EDD842A3615BE0031D520 /* ManualDetails.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= ManualDetails.swift; sourceTree = "<group>"; };
4E6EDD862A363D8D0031D520 /* ListStyle.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= ListStyle.swift; sourceTree = "<group>"; };
4E753A042A08E720002D9328 /* transactions.json */ = {isa =
PBXFileReference; lastKnownFileType = text.json; path = transactions.json;
sourceTree = "<group>"; };
@@ -372,10 +394,12 @@
4ECB627F2A0BA6DF004ABBB7 /* Model+P2P.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= "Model+P2P.swift"; sourceTree = "<group>"; };
4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= SelectDays.swift; sourceTree = "<group>"; };
4ED2F94A2A278F5100453B40 /* ThreeAmounts.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= ThreeAmounts.swift; sourceTree = "<group>"; };
+ 4EDBDCD82AB787CB00925C02 /* CallStack.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= CallStack.swift; sourceTree = "<group>"; };
4EEC157229F8242800D46A03 /* QRGeneratorView.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= QRGeneratorView.swift; sourceTree = "<group>"; };
4EEC157729F9032900D46A03 /* Sheet.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= Sheet.swift; sourceTree = "<group>"; };
4EEC157929F9427F00D46A03 /* QRSheet.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= QRSheet.swift; sourceTree = "<group>"; };
4EF840A62A0B85F400EE0D47 /* CopyShare.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= CopyShare.swift; sourceTree = "<group>"; };
+ 4EFA395F2AA7946B00742548 /* ToSButtonView.swift */ = {isa =
PBXFileReference; lastKnownFileType = sourcecode.swift; path =
ToSButtonView.swift; sourceTree = "<group>"; };
AB710490285995B6008B04F0 /* taler-swift */ = {isa =
PBXFileReference; lastKnownFileType = text; path = "taler-swift"; sourceTree =
SOURCE_ROOT; };
D14AFD1D24D232B300C51073 /* Taler_Wallet.app */ = {isa =
PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0;
path = Taler_Wallet.app; sourceTree = BUILT_PRODUCTS_DIR; };
D14AFD3324D232B500C51073 /* TalerTests.xctest */ = {isa =
PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path
= TalerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -450,6 +474,8 @@
4E3EAE9B2AA12467009F1BE8 /* Nunito-Bold.ttf */,
4E3EAEA12AA12582009F1BE8 /*
Nunito-BoldItalic.ttf */,
4E3EAEA02AA12582009F1BE8 /* Nunito-Italic.ttf
*/,
+ 4E605D8E2AA8B407002FB9A7 /* Nunito-Black.ttf */,
+ 4E605D8F2AA8B407002FB9A7 /*
Nunito-BlackItalic.ttf */,
);
path = Nunito;
sourceTree = "<group>";
@@ -531,17 +557,20 @@
4EB095052989CB7C0043A8A1 /* Helper */ = {
isa = PBXGroup;
children = (
- 4E97968F2A3765ED006F73BC /* AgePicker.swift */,
4E363CBD2A23CB2100D7E98C /*
AnyTransition+backslide.swift */,
+ 4EDBDCD82AB787CB00925C02 /* CallStack.swift */,
4E16E12229F3BB99008B9C86 /*
CurrencyFormatter.swift */,
4EAD117529F672FA008EDD0B /*
KeyboardResponder.swift */,
4E363CC12A2621C200D7E98C /*
LocalizedAlertError.swift */,
4E578E912A481D8600F21F1C /*
Controller+playSound.swift */,
4EB095062989CB7C0043A8A1 /* TalerDater.swift */,
4EB095072989CB7C0043A8A1 /* TalerStrings.swift
*/,
+ 4E3EAEA72AA70157009F1BE8 /*
Binding+onChange.swift */,
4E3EAE8B2AA0933C009F1BE8 /* Font+Taler.swift */,
4EB095082989CB7C0043A8A1 /*
View+dismissTop.swift */,
+ 4E605DB62AB05E48002FB9A7 /*
View+flippedDirection.swift */,
4E3B4BC62A429F2A00CC88B8 /*
View+Notification.swift */,
+ 4E605DAE2AADDD13002FB9A7 /*
UIScreen+screenSize.swift */,
4E363CBB2A237E0900D7E98C /* URL+id+iban.swift
*/,
4E9320422A14F6EA00A87B0E /* WalletColors.swift
*/,
4E8E25322A1CD39700A27BFA /*
EqualIconWidthDomain.swift */,
@@ -684,6 +713,8 @@
4EB095462989CBFE0043A8A1 /* HelperViews */ = {
isa = PBXGroup;
children = (
+ 4E97968F2A3765ED006F73BC /* AgePicker.swift */,
+ 4E605DB92AB05FB6002FB9A7 /* BarGraph.swift */,
4EB095472989CBFE0043A8A1 /* Buttons.swift */,
4EF840A62A0B85F400EE0D47 /* CopyShare.swift */,
4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */,
@@ -697,6 +728,7 @@
4EB095492989CBFE0043A8A1 /* AmountView.swift */,
4EB0954A2989CBFE0043A8A1 /* LoadingView.swift
*/,
4EB095432989CBFE0043A8A1 /*
LaunchAnimationView.swift */,
+ 4EFA395F2AA7946B00742548 /* ToSButtonView.swift
*/,
);
path = HelperViews;
sourceTree = "<group>";
@@ -928,8 +960,10 @@
4E3EAE812A990778009F1BE8 /*
Atkinson-Hyperlegible-BoldItalic-102.otf in Resources */,
4E3EAE832A990778009F1BE8 /*
Atkinson-Hyperlegible-Italic-102.otf in Resources */,
4E3EAE9C2AA12467009F1BE8 /* Nunito-Regular.ttf
in Resources */,
+ 4E605D922AA8B407002FB9A7 /*
Nunito-BlackItalic.ttf in Resources */,
4E3EAE9E2AA12467009F1BE8 /* Nunito-Bold.ttf in
Resources */,
4E3EAEA42AA12582009F1BE8 /*
Nunito-BoldItalic.ttf in Resources */,
+ 4E605D902AA8B407002FB9A7 /* Nunito-Black.ttf in
Resources */,
4E3EAEA22AA12582009F1BE8 /* Nunito-Italic.ttf
in Resources */,
4E3EAE822A990778009F1BE8 /*
payment_received.m4a in Resources */,
4E3EAE852A990778009F1BE8 /* payment_sent.m4a in
Resources */,
@@ -947,8 +981,10 @@
4E8C17232A6509BB005B2392 /*
Atkinson-Hyperlegible-BoldItalic-102.otf in Resources */,
4E8C17212A6509BB005B2392 /*
Atkinson-Hyperlegible-Italic-102.otf in Resources */,
4E3EAE9D2AA12467009F1BE8 /* Nunito-Regular.ttf
in Resources */,
+ 4E605D932AA8B407002FB9A7 /*
Nunito-BlackItalic.ttf in Resources */,
4E3EAE9F2AA12467009F1BE8 /* Nunito-Bold.ttf in
Resources */,
4E3EAEA52AA12582009F1BE8 /*
Nunito-BoldItalic.ttf in Resources */,
+ 4E605D912AA8B407002FB9A7 /* Nunito-Black.ttf in
Resources */,
4E3EAEA32AA12582009F1BE8 /* Nunito-Italic.ttf
in Resources */,
4E2254972A822B8100E41D29 /*
payment_received.m4a in Resources */,
4E2254982A822B8100E41D29 /* payment_sent.m4a in
Resources */,
@@ -986,6 +1022,7 @@
4E3EAE242A990778009F1BE8 /*
QRGeneratorView.swift in Sources */,
4E3EAE252A990778009F1BE8 /*
WithdrawAcceptDone.swift in Sources */,
4E3EAE262A990778009F1BE8 /* Transaction.swift
in Sources */,
+ 4E605DB72AB05E48002FB9A7 /*
View+flippedDirection.swift in Sources */,
4E3EAE272A990778009F1BE8 /* WalletColors.swift
in Sources */,
4E3EAE282A990778009F1BE8 /*
BalancesListView.swift in Sources */,
4E3EAE292A990778009F1BE8 /*
WalletBackendError.swift in Sources */,
@@ -997,6 +1034,7 @@
4E3EAE2E2A990778009F1BE8 /*
QRCodeDetailView.swift in Sources */,
4E3EAE2F2A990778009F1BE8 /*
TransactionsEmptyView.swift in Sources */,
4E3EAE302A990778009F1BE8 /*
UncompletedRowView.swift in Sources */,
+ 4E605DAF2AADDD13002FB9A7 /*
UIScreen+screenSize.swift in Sources */,
4E3EAE312A990778009F1BE8 /* SendAmount.swift in
Sources */,
4E3EAE332A990778009F1BE8 /*
EqualIconWidthDomain.swift in Sources */,
4E3EAE342A990778009F1BE8 /*
SuperScriptDigits.swift in Sources */,
@@ -1024,8 +1062,10 @@
4E3EAE4A2A990778009F1BE8 /*
PaymentPurpose.swift in Sources */,
4E3EAE4B2A990778009F1BE8 /* ShareSheet.swift in
Sources */,
4E3EAE4C2A990778009F1BE8 /* AmountView.swift in
Sources */,
+ 4E605DBA2AB05FB6002FB9A7 /* BarGraph.swift in
Sources */,
4E3EAE4D2A990778009F1BE8 /* P2pAcceptDone.swift
in Sources */,
4E3EAE4E2A990778009F1BE8 /*
AnyTransition+backslide.swift in Sources */,
+ 4EFA39602AA7946B00742548 /* ToSButtonView.swift
in Sources */,
4E3EAE4F2A990778009F1BE8 /*
BalanceRowButtons.swift in Sources */,
4E3EAE502A990778009F1BE8 /*
Model+Transactions.swift in Sources */,
4E3EAE512A990778009F1BE8 /*
Controller+playSound.swift in Sources */,
@@ -1061,9 +1101,11 @@
4E3EAE6F2A990778009F1BE8 /* TalerStrings.swift
in Sources */,
4E3EAE702A990778009F1BE8 /*
CurrencyInputView.swift in Sources */,
4E3EAE712A990778009F1BE8 /* URL+id+iban.swift
in Sources */,
+ 4EDBDCD92AB787CB00925C02 /* CallStack.swift in
Sources */,
4E3EAE722A990778009F1BE8 /*
RequestPayment.swift in Sources */,
4E3EAE732A990778009F1BE8 /* SettingsItem.swift
in Sources */,
4E3EAE742A990778009F1BE8 /*
BalanceRowView.swift in Sources */,
+ 4E3EAEA82AA70157009F1BE8 /*
Binding+onChange.swift in Sources */,
4E3EAE752A990778009F1BE8 /* DebugViewC.swift in
Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1082,6 +1124,7 @@
4EEC157329F8242800D46A03 /*
QRGeneratorView.swift in Sources */,
4E5A88F72A3B9E5B00072618 /*
WithdrawAcceptDone.swift in Sources */,
4EB095222989CBCB0043A8A1 /* Transaction.swift
in Sources */,
+ 4E605DB82AB05E48002FB9A7 /*
View+flippedDirection.swift in Sources */,
4E9320432A14F6EA00A87B0E /* WalletColors.swift
in Sources */,
4EB0955D2989CBFE0043A8A1 /*
BalancesListView.swift in Sources */,
4EB095212989CBCB0043A8A1 /*
WalletBackendError.swift in Sources */,
@@ -1093,6 +1136,7 @@
4E5A88F52A38A4FD00072618 /*
QRCodeDetailView.swift in Sources */,
4E87C8732A31CB7F001C6406 /*
TransactionsEmptyView.swift in Sources */,
4E87C8752A34B411001C6406 /*
UncompletedRowView.swift in Sources */,
+ 4E605DB02AADDD13002FB9A7 /*
UIScreen+screenSize.swift in Sources */,
4E40E0BE29F25ABB00B85369 /* SendAmount.swift in
Sources */,
4E8E25332A1CD39700A27BFA /*
EqualIconWidthDomain.swift in Sources */,
4EBA563F2A7FD9390084948B /*
SuperScriptDigits.swift in Sources */,
@@ -1120,8 +1164,10 @@
4E9320472A164BC700A87B0E /*
PaymentPurpose.swift in Sources */,
4E753A082A0B6A5F002D9328 /* ShareSheet.swift in
Sources */,
4EB0956C2989CBFE0043A8A1 /* AmountView.swift in
Sources */,
+ 4E605DBB2AB05FB6002FB9A7 /* BarGraph.swift in
Sources */,
4E3B4BC32A42252300CC88B8 /* P2pAcceptDone.swift
in Sources */,
4E363CBE2A23CB2100D7E98C /*
AnyTransition+backslide.swift in Sources */,
+ 4EFA39612AA7946B00742548 /* ToSButtonView.swift
in Sources */,
4EB065442A4CD1A80039B91D /*
BalanceRowButtons.swift in Sources */,
4EB095592989CBFE0043A8A1 /*
Model+Transactions.swift in Sources */,
4E578E922A481D8600F21F1C /*
Controller+playSound.swift in Sources */,
@@ -1157,9 +1203,11 @@
4EB0950A2989CB7C0043A8A1 /* TalerStrings.swift
in Sources */,
4EA551252A2C923600FEC9A8 /*
CurrencyInputView.swift in Sources */,
4E363CBC2A237E0900D7E98C /* URL+id+iban.swift
in Sources */,
+ 4EDBDCDA2AB787CB00925C02 /* CallStack.swift in
Sources */,
4E9320452A1645B600A87B0E /*
RequestPayment.swift in Sources */,
4EB095502989CBFE0043A8A1 /* SettingsItem.swift
in Sources */,
4EB0955C2989CBFE0043A8A1 /*
BalanceRowView.swift in Sources */,
+ 4E3EAEA92AA70157009F1BE8 /*
Binding+onChange.swift in Sources */,
4E753A062A0952F8002D9328 /* DebugViewC.swift in
Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1334,6 +1382,7 @@
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
};
name = Debug;
@@ -1388,6 +1437,7 @@
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
+ SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES;
};
diff --git a/TalerWallet1/Backend/Transaction.swift
b/TalerWallet1/Backend/Transaction.swift
index 7f2aac5..823e1d7 100644
--- a/TalerWallet1/Backend/Transaction.swift
+++ b/TalerWallet1/Backend/Transaction.swift
@@ -142,7 +142,7 @@ enum TransactionType: String, Codable {
var isIncoming : Bool { isP2pIncoming || isWithdrawal || isRefund ||
isReward }
}
-struct TransactionCommon: Decodable {
+struct TransactionCommon: Decodable, Sendable {
var type: TransactionType
var txState: TransactionState
var amountEffective: Amount
@@ -222,7 +222,7 @@ struct WithdrawalTransactionDetails: Decodable {
var withdrawalDetails: WithdrawalDetails
}
-struct WithdrawalTransaction {
+struct WithdrawalTransaction : Sendable{
var common: TransactionCommon
var details: WithdrawalTransactionDetails
}
@@ -237,7 +237,7 @@ struct PaymentTransactionDetails: Decodable {
var info: OrderShortInfo
}
-struct PaymentTransaction {
+struct PaymentTransaction : Sendable{
var common: TransactionCommon
var details: PaymentTransactionDetails
}
@@ -250,7 +250,7 @@ struct RefundTransactionDetails: Decodable {
var info: OrderShortInfo? // TODO: is this still here?
}
-struct RefundTransaction {
+struct RefundTransaction : Sendable{
var common: TransactionCommon
var details: RefundTransactionDetails
}
@@ -260,7 +260,7 @@ struct RewardTransactionDetails: Decodable {
var exchangeBaseUrl: String
}
-struct RewardTransaction {
+struct RewardTransaction : Sendable{
var common: TransactionCommon
var details: RewardTransactionDetails
}
@@ -285,32 +285,32 @@ struct RefreshTransactionDetails: Decodable {
var refreshOutputAmount: Amount
}
-struct RefreshTransaction {
+struct RefreshTransaction : Sendable{
var common: TransactionCommon
var details: RefreshTransactionDetails
}
-struct P2pShortInfo: Codable {
+struct P2pShortInfo: Codable, Sendable {
var summary: String
var expiration: Timestamp
}
-struct P2PTransactionDetails: Codable {
+struct P2PTransactionDetails: Codable, Sendable {
var exchangeBaseUrl: String
var talerUri: String? // only if we initiated the transaction
var info: P2pShortInfo
}
-struct P2PTransaction {
+struct P2PTransaction : Sendable{
var common: TransactionCommon
var details: P2PTransactionDetails
}
-struct DummyTransaction {
+struct DummyTransaction : Sendable{
var common: TransactionCommon
}
-enum Transaction: Decodable, Hashable, Identifiable {
+enum Transaction: Decodable, Hashable, Identifiable, Sendable {
case dummy (DummyTransaction)
case withdrawal (WithdrawalTransaction)
case payment (PaymentTransaction)
@@ -446,7 +446,7 @@ enum Transaction: Decodable, Hashable, Identifiable {
func detailsToShow() -> Dictionary<String, String> {
var result: [String:String] = [:]
switch self {
- case .dummy(let dummyTransaction):
+ case .dummy(_): // let dummyTransaction
break
case .withdrawal(let withdrawalTransaction):
result[EXCHANGEBASEURL] =
withdrawalTransaction.details.exchangeBaseUrl
diff --git a/TalerWallet1/Backend/WalletCore.swift
b/TalerWallet1/Backend/WalletCore.swift
index f2f0460..a3b8dbb 100644
--- a/TalerWallet1/Backend/WalletCore.swift
+++ b/TalerWallet1/Backend/WalletCore.swift
@@ -151,8 +151,7 @@ extension WalletCore {
private func postNotification(_ aName: NSNotification.Name,
object anObject: Any? = nil,
userInfo: [AnyHashable: Any]? = nil) {
- Task {
- if let userInfo { symLog.log(userInfo) } else { symLog.log(aName) }
+ Task { // runs on MainActor
await postNotificationM(aName, object: anObject, userInfo:
userInfo)
logger.log("Notification sent: \(aName.rawValue)")
}
@@ -182,7 +181,7 @@ extension WalletCore {
logger.log("❗️ \(pendingOp, privacy: .public): \(id, privacy:
.public)") // this is a new pendingOp I haven't seen before
}
}
- private func handleStateTransition(_ jsonData: Data) throws {
+ @MainActor private func handleStateTransition(_ jsonData: Data) throws {
do {
let decoded = try JSONDecoder().decode(TransactionTransition.self,
from: jsonData)
if decoded.newTxState != decoded.oldTxState {
@@ -206,7 +205,7 @@ extension WalletCore {
}
}
- private func handleNotification(_ anyCodable: AnyCodable?) throws {
+ @MainActor private func handleNotification(_ anyCodable: AnyCodable?)
throws {
guard let anyPayload = anyCodable else { throw
WalletBackendError.deserializationError }
do {
let jsonData = try JSONEncoder().encode(anyPayload)
@@ -214,12 +213,15 @@ extension WalletCore {
switch payload.type {
case Notification.Name.TransactionStateTransition.rawValue:
+ symLog.log(anyPayload)
try handleStateTransition(jsonData)
case Notification.Name.PendingOperationProcessed.rawValue:
try handlePendingProcessed(payload)
case Notification.Name.BalanceChange.rawValue:
+ symLog.log(anyPayload)
postNotification(.BalanceChange)
case Notification.Name.ExchangeAdded.rawValue:
+ symLog.log(anyPayload)
postNotification(.ExchangeAdded)
case Notification.Name.ReserveNotYetFound.rawValue:
if let reservePub = payload.reservePub {
@@ -256,7 +258,7 @@ print("\n❗️ WalletCore.swift:226 Notification: ",
anyPayload, "\n") /
}
/// here not only responses, but also notifications from wallet-core will
be received
- func handleMessage(message: String) {
+ @MainActor func handleMessage(message: String) {
do {
var asyncDelay = 0
if let delay: Bool = developDelay { // Settings: 2 seconds delay
@@ -332,7 +334,7 @@ print("\n❗️ WalletCore.swift:226 Notification: ",
anyPayload, "\n") /
// MARK: - async / await function
extension WalletCore {
/// send async requests to wallet-core
- func sendFormattedRequest<T: WalletBackendFormattedRequest> (request: T)
async throws -> (T.Response, UInt) {
+ func sendFormattedRequest<T: WalletBackendFormattedRequest> (_ request: T)
async throws -> (T.Response, UInt) {
let reqData = WalletBackendRequest(operation: request.operation(),
args: AnyEncodable(request.args()))
return try await withCheckedThrowingContinuation { continuation in
diff --git a/TalerWallet1/Controllers/Controller.swift
b/TalerWallet1/Controllers/Controller.swift
index 0dbd42c..35c2c6a 100644
--- a/TalerWallet1/Controllers/Controller.swift
+++ b/TalerWallet1/Controllers/Controller.swift
@@ -7,6 +7,7 @@ import AVFoundation
import SwiftUI
import SymLog
import os.log
+import CoreHaptics
enum BackendState {
case none
@@ -32,8 +33,10 @@ class Controller: ObservableObject {
private let symLog = SymLogC()
@Published var backendState: BackendState = .none // only used for
launch animation
+ @AppStorage("useHaptics") var useHaptics: Bool = false // extension
mustn't define this, so it must be here
@AppStorage("playSounds") var playSounds: Int = 0 // extension
mustn't define this, so it must be here
@AppStorage("talerFont") var talerFont: Int = 0 // extension
mustn't define this, so it must be here
+ let hapticCapability = CHHapticEngine.capabilitiesForHardware()
let logger = Logger (subsystem: "net.taler.gnu", category: "Controller")
let player = AVQueuePlayer()
diff --git a/TalerWallet1/Controllers/DebugViewC.swift
b/TalerWallet1/Controllers/DebugViewC.swift
index 89209f9..b1dee53 100644
--- a/TalerWallet1/Controllers/DebugViewC.swift
+++ b/TalerWallet1/Controllers/DebugViewC.swift
@@ -124,8 +124,8 @@ struct DebugViewV: View {
Spacer()
}
Text(viewIDString)
- .font(.caption2)
.foregroundColor(.red)
+ .font(.system(size: 11)) // no accessibilityFont
.monospacedDigit()
Spacer()
}
diff --git a/TalerWallet1/Controllers/PublicConstants.swift
b/TalerWallet1/Controllers/PublicConstants.swift
index 1be1113..e6a807e 100644
--- a/TalerWallet1/Controllers/PublicConstants.swift
+++ b/TalerWallet1/Controllers/PublicConstants.swift
@@ -25,7 +25,8 @@ public let TESTEXCHANGE = HTTPS + "exchange.test.taler.net"
public let ARS_AGE_EXCHANGE = HTTPS + "exchange-age.taler.ar"
public let ARS_EXP_EXCHANGE = HTTPS + "exchange-expensive.taler.ar"
public let DEMOCURRENCY = "KUDOS"
-public let TESTCURRENCY = "TESTKUDOS"
+//public let TESTCURRENCY = "TESTKUDOS"
+public let TESTCURRENCY = "KUDOS"
//public let LONGCURRENCY = "gold-pressed Latinum" // 20
characters, with dash and space
public let LONGCURRENCY = "GOLDLATINUM" // 11
characters, no dash, no space
diff --git a/TalerWallet1/Controllers/TalerWallet1App.swift
b/TalerWallet1/Controllers/TalerWallet1App.swift
index d46b6d3..4adf008 100644
--- a/TalerWallet1/Controllers/TalerWallet1App.swift
+++ b/TalerWallet1/Controllers/TalerWallet1App.swift
@@ -37,7 +37,7 @@ struct TalerWallet1App: App {
var body: some Scene {
WindowGroup {
- MainView(soundPlayed: $soundPlayed)
+ MainView(stack: CallStack("App"), soundPlayed: $soundPlayed)
.environmentObject(debugViewC) // change viewID / sheetID
.environmentObject(viewState) // popToRoot
.environmentObject(controller)
diff --git a/TalerWallet1/Fonts/Nunito/Nunito-Black.ttf
b/TalerWallet1/Fonts/Nunito/Nunito-Black.ttf
new file mode 100644
index 0000000..1081731
Binary files /dev/null and b/TalerWallet1/Fonts/Nunito/Nunito-Black.ttf differ
diff --git a/TalerWallet1/Fonts/Nunito/Nunito-BlackItalic.ttf
b/TalerWallet1/Fonts/Nunito/Nunito-BlackItalic.ttf
new file mode 100644
index 0000000..e512048
Binary files /dev/null and b/TalerWallet1/Fonts/Nunito/Nunito-BlackItalic.ttf
differ
diff --git a/TalerWallet1/Helper/KeyboardResponder.swift
b/TalerWallet1/Helper/Binding+onChange.swift
similarity index 53%
copy from TalerWallet1/Helper/KeyboardResponder.swift
copy to TalerWallet1/Helper/Binding+onChange.swift
index c5d9cde..7bf8a5f 100644
--- a/TalerWallet1/Helper/KeyboardResponder.swift
+++ b/TalerWallet1/Helper/Binding+onChange.swift
@@ -1,5 +1,5 @@
// MIT License
-// Copyright © Nicolai Harbo
+// Copyright © Paul Hudson
//
// Permission is hereby granted, free of charge, to any person obtaining a
copy of this software
// and associated documentation files (the "Software"), to deal in the
Software without restriction,
@@ -16,30 +16,30 @@
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
//
-import Combine
-import UIKit
+import SwiftUI
-public final class KeyboardResponder: ObservableObject {
-
- @Published public var keyboardHeight: CGFloat = 0
- var showCancellable: AnyCancellable?
- var hideCancellable: AnyCancellable?
-
- public init() {
- showCancellable = NotificationCenter.default.publisher(for:
UIResponder.keyboardWillShowNotification)
- .map { notification in
-
(notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as?
NSValue)?.cgRectValue.height ?? 0.0
+/// Pass the handler directly to the Binding
+extension Binding {
+ func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> {
+ Binding (
+ get: { self.wrappedValue },
+ set: { newValue in
+ self.wrappedValue = newValue
+ handler(newValue)
}
- .receive(on: DispatchQueue.main)
- .sink(receiveValue: { height in
-// print("keyboard height: \(height)")
- self.keyboardHeight = height
- })
+ )
+ }
+}
- hideCancellable = NotificationCenter.default.publisher(for:
UIResponder.keyboardWillHideNotification)
- .receive(on: DispatchQueue.main)
- .sink(receiveValue: { _ in
- self.keyboardHeight = 0
- })
+#if false
+// use like this:
+struct BindingView: View {
+ @State private var rating = 0.0
+ var body: some View {
+ Slider (value: $rating.onChange(sliderChanged))
+ }
+ func sliderChanged(_ value: Double) {
+ print ("Rating changed to \(value)")
}
}
+#endif
diff --git a/TalerWallet1/Helper/CallStack.swift
b/TalerWallet1/Helper/CallStack.swift
new file mode 100644
index 0000000..fb5d0ec
--- /dev/null
+++ b/TalerWallet1/Helper/CallStack.swift
@@ -0,0 +1,89 @@
+//
+// Copyright © 2018-2023 Marc Stibane
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
copy of this software
+// and associated documentation files (the "Software"), to deal in the
Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to
whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING
+// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+//
+import Foundation
+
+struct CallStackItem {
+#if DEBUG
+ let file: String
+ let function: String
+#endif
+ let message: String
+}
+
+#if DEBUG
+extension CallStackItem: Identifiable {
+ var id: String { file }
+}
+extension CallStackItem: Equatable {
+ static func == (lhs: CallStackItem, rhs: CallStackItem) -> Bool {
+ lhs.file == rhs.file
+ }
+}
+#endif
+
+
+struct CallStack {
+ private var stack = [CallStackItem]()
+ func peek() -> CallStackItem? { stack.first }
+ func push(item: CallStackItem) -> CallStack {
+ return CallStack(stack: [item] + stack)
+ }
+}
+
+#if DEBUG
+fileprivate func filePath2Name(_ file: String) -> String {
+ let filePath = NSString(string: file)
+ return filePath.lastPathComponent
+}
+#endif
+
+extension CallStack {
+#if DEBUG
+ init(_ message: String = "",
+ funcName: String = #function,
+ filePath: String = #file,
+ line: UInt = #line) {
+ let item = CallStackItem(file: filePath2Name(filePath) + ":\(line)",
function: funcName, message: message)
+ self.stack = [item]
+ }
+#else
+ init(_ message: String = "") {
+ let item = CallStackItem(message: message)
+ self.storage = [item]
+ }
+#endif
+#if DEBUG
+ public func push(_ message: String = "",
+ funcName: String = #function,
+ filePath: String = #file,
+ line: UInt = #line) -> CallStack {
+ let item = CallStackItem(file: filePath2Name(filePath) + ":\(line)",
function: funcName, message: message)
+ return push(item: item)
+ }
+#else
+ public func push(_ message: String = "") -> CallStack {
+ let item = CallStackItem(message: message)
+ return push(item: item)
+ }
+#endif
+}
+
+//extension CallStack: Equatable {
+// static func == (lhs: CallStack, rhs: CallStack) -> Bool { lhs.storage ==
rhs.storage }
+//}
diff --git a/TalerWallet1/Helper/Controller+playSound.swift
b/TalerWallet1/Helper/Controller+playSound.swift
index 31aca38..bf6a784 100644
--- a/TalerWallet1/Helper/Controller+playSound.swift
+++ b/TalerWallet1/Helper/Controller+playSound.swift
@@ -4,12 +4,14 @@
*/
import Foundation
import AVFoundation
+import UIKit
extension Controller {
/// 0 = failure, 1 = received, 2 = sent
- func playSound(_ number: Int) {
+ @MainActor func playSound(_ number: Int) {
var soundID: SystemSoundID = 0
+ let notificationGenerator = useHaptics ?
UINotificationFeedbackGenerator() : nil
if number > 9 {
soundID = UInt32(number)
} else {
@@ -34,5 +36,8 @@ extension Controller {
AudioServicesPlaySystemSound(soundID);
}
}
+ if let notificationGenerator {
+ notificationGenerator.notificationOccurred(number == 0 ? .error :
.success)
+ }
}
}
diff --git a/TalerWallet1/Helper/Font+Taler.swift
b/TalerWallet1/Helper/Font+Taler.swift
index bc0511a..ef4e612 100644
--- a/TalerWallet1/Helper/Font+Taler.swift
+++ b/TalerWallet1/Helper/Font+Taler.swift
@@ -6,108 +6,251 @@ import SwiftUI
// Use enums for multiple font types and functions for the set custom font.
-fileprivate let ATKINSON = "AtkinsonHyperlegible-"
-fileprivate let NUNITO = "Nunito-"
-
-fileprivate let REGULAR = "Regular"
-fileprivate let BOLD = "Bold"
-fileprivate let BOLDITALIC = "BoldItalic"
-fileprivate let ITALIC = "Italic"
-
-extension Font {
- enum TalerFont {
- case regular
- case bold
- case boldItalic
- case italic
- case custom(String)
-
- var value: String {
- switch self {
- case .regular: return REGULAR
- case .bold: return BOLD
- case .boldItalic: return BOLDITALIC
- case .italic: return ITALIC
-
- case .custom(let name):
- return name
- }
+fileprivate let ATKINSON = "AtkinsonHyperlegible-"
+fileprivate let NUNITO = "Nunito-"
+
+fileprivate let REGULAR = "Regular"
+fileprivate let BOLD = "Bold"
+fileprivate let BLACK = "Black"
+fileprivate let BLACKITALIC = "BlackItalic"
+fileprivate let BOLDITALIC = "BoldItalic"
+fileprivate let ITALIC = "Italic"
+
+struct TalerFont {
+ static var atkinson: Font {
+ Font.custom(ATKINSON + REGULAR, size: 24, relativeTo: .title2)
+ }
+ static var nunito: Font {
+ Font.custom(NUNITO + REGULAR, size: 24, relativeTo: .title2)
+ }
+ static var nunitoItalic: Font {
+ Font.custom(NUNITO + ITALIC, size: 24, relativeTo: .title2)
+ }
+
+ static func atkinson(size: CGFloat, relativeTo style: UIFont.TextStyle) ->
UIFont {
+ if let font = UIFont(name: ATKINSON + REGULAR, size: size) {
+ let fontMetrics = UIFontMetrics(forTextStyle: style)
+ return fontMetrics.scaledFont(for: font)
+ } else {
+ return UIFont.preferredFont(forTextStyle: style)
+ }
+ }
+ static func nunito(size: CGFloat, relativeTo: UIFont.TextStyle) -> UIFont {
+ if let font = UIFont(name: NUNITO + REGULAR, size: size) {
+ let fontMetrics = UIFontMetrics(forTextStyle: relativeTo)
+ return fontMetrics.scaledFont(for: font)
+ } else {
+ return UIFont.preferredFont(forTextStyle: relativeTo)
+ }
+ }
+ static func nunitoItalic(size: CGFloat, relativeTo: UIFont.TextStyle) ->
UIFont {
+ if let font = UIFont(name: NUNITO + ITALIC, size: size) {
+ let fontMetrics = UIFontMetrics(forTextStyle: relativeTo)
+ return fontMetrics.scaledFont(for: font)
+ } else {
+ return UIFont.preferredFont(forTextStyle: relativeTo)
+ }
+ }
+
+ static func talerFont(_ talerFont: Int, size: CGFloat, relativeTo style:
UIFont.TextStyle) -> UIFont {
+ if talerFont != 0 {
+ let uiFont: UIFont = (talerFont == 1) ? TalerFont.atkinson(size:
size, relativeTo: style)
+ : (talerFont == 2) ? TalerFont.nunito(size:
size, relativeTo: style)
+ :
TalerFont.nunitoItalic(size: size, relativeTo: style)
+ return uiFont
+ } else {
+ return UIFont.preferredFont(forTextStyle: style)
}
}
+}
- static func talerFont(_ type: TalerFont, size: CGFloat = 17) -> Font {
- return .custom(type.value, size: size)
+struct AccessibleFont {
+ var regular: Font
+ var bold: Font
+ static var talerFont: Int {
+ if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"]
== "1" {
+ 3
+ } else {
+ Controller.shared.talerFont
+ }
}
- static func talerFontName(_ index: Int) -> String {
- if index == 1 {
- return ATKINSON
+ init(_ base: String, size: CGFloat, relativeTo: Font.TextStyle, isBold:
Bool = false) {
+ if AccessibleFont.talerFont == 0 {
+ self.regular = .system(relativeTo)
+ self.bold = .system(relativeTo).bold() // why is this allowed
here (iOS-15) ??? should give compiler error
+ // bold() for Font
needs iOS-16
+ // Text has a function
bold(), needs iOS-13, but that shouldn't matter here
+ } else if isBold && AccessibleFont.talerFont >= 2 {
+ // Nunito has Black Variants, but AtkinsonHyperlegible doesn't
+ self.regular = Font.custom(base + (AccessibleFont.talerFont == 2 ?
BOLD : BOLDITALIC), size: size, relativeTo: relativeTo)
+ self.bold = Font.custom(base + (AccessibleFont.talerFont == 2 ?
BLACK : BLACKITALIC), size: size, relativeTo: relativeTo)
} else {
- return NUNITO
+ self.regular = Font.custom(base + (AccessibleFont.talerFont > 2 ?
ITALIC : REGULAR), size: size, relativeTo: relativeTo)
+ self.bold = Font.custom(base + (AccessibleFont.talerFont > 2 ?
BOLDITALIC : BOLD), size: size, relativeTo: relativeTo)
+ }
+ }
+
+ init(regular: Font, bold: Font) {
+ self.regular = regular
+ self.bold = bold
+ }
+
+ func value(_ legibilityWeight: LegibilityWeight?) -> Font {
+ switch legibilityWeight {
+ case .bold: // should increase Font.Weight by 2
+ // ultraLight => light
+ // thin => regular
+ // light => medium
+ // regular => semibold
+ // medium => bold
+ // semibold => heavy
+ // bold => black
+ return bold
+ default:
+ return regular
}
}
+}
+
+extension AccessibleFont {
+ static var fontName: String {
+ (talerFont == 1) ? ATKINSON
+ : NUNITO
+ }
- static var talerLargeTitle: Font {
- Controller.shared.talerFont == 0 ? .largeTitle :
- .custom(talerFontName(Controller.shared.talerFont) + REGULAR,
size: 38, relativeTo: .largeTitle)
- } // 34 -> 38
- static var talerTitle: Font {
- Controller.shared.talerFont == 0 ? .title :
- .custom(talerFontName(Controller.shared.talerFont) + REGULAR,
size: 31, relativeTo: .title)
- } // 28 -> 31
- static var talerTitle2: Font {
- Controller.shared.talerFont == 0 ? .title2 :
- .custom(talerFontName(Controller.shared.talerFont) + REGULAR,
size: 25, relativeTo: .title2)
- } // 22 -> 25
- static var talerTitle3: Font {
- Controller.shared.talerFont == 0 ? .title3 :
- .custom(talerFontName(Controller.shared.talerFont) + REGULAR,
size: 23, relativeTo: .title3)
- } // 20 -> 23
- static var talerHeadline: Font {
- Controller.shared.talerFont == 0 ? .headline :
- .custom(talerFontName(Controller.shared.talerFont) + BOLD, size:
19, relativeTo: .headline)
- } // 17 bold -> 19 bold
- static var talerBody: Font {
- Controller.shared.talerFont == 0 ? .body :
- .custom(talerFontName(Controller.shared.talerFont) + REGULAR,
size: 19, relativeTo: .body)
- } // 17 -> 19
- static var talerCallout: Font {
- Controller.shared.talerFont == 0 ? .callout :
- .custom(talerFontName(Controller.shared.talerFont) + REGULAR,
size: 18, relativeTo: .callout)
- } // 16 -> 18
- static var talerSubheadline: Font {
- Controller.shared.talerFont == 0 ? .subheadline :
- .custom(talerFontName(Controller.shared.talerFont) + REGULAR,
size: 17, relativeTo: .subheadline)
- } // 15 -> 17
- static var talerFootnote: Font {
- Controller.shared.talerFont == 0 ? .footnote :
- .custom(talerFontName(Controller.shared.talerFont) + REGULAR,
size: 15, relativeTo: .footnote)
- } // 13 -> 15
- static var talerCaption: Font {
- Controller.shared.talerFont == 0 ? .caption :
- .custom(talerFontName(Controller.shared.talerFont) + REGULAR,
size: 13, relativeTo: .caption)
- } // 12 -> 13
- static var talerCaption2: Font {
- Controller.shared.talerFont == 0 ? .caption2 :
- .custom(talerFontName(Controller.shared.talerFont) + REGULAR,
size: 12, relativeTo: .caption2)
- } // 11 -> 12
+ static var largeTitle: AccessibleFont { AccessibleFont(fontName, size:
38, relativeTo: .largeTitle) } // 34 -> 38
+ static var title: AccessibleFont { AccessibleFont(fontName, size:
31, relativeTo: .title) } // 28 -> 31
+ static var title2: AccessibleFont { AccessibleFont(fontName, size:
25, relativeTo: .title2) } // 22 -> 25
+ static var title3: AccessibleFont { AccessibleFont(fontName, size:
23, relativeTo: .title3) } // 20 -> 23
+ static var headline: AccessibleFont { AccessibleFont(fontName, size:
19, relativeTo: .headline, isBold: true) } // 17 bold -> 19 bold
+ static var body: AccessibleFont { AccessibleFont(fontName, size:
19, relativeTo: .body) } // 17 -> 19
+ static var callout: AccessibleFont { AccessibleFont(fontName, size:
18, relativeTo: .callout) } // 16 -> 18
+ static var subheadline: AccessibleFont { AccessibleFont(fontName, size:
17, relativeTo: .subheadline) } // 15 -> 17
+ static var footnote: AccessibleFont { AccessibleFont(fontName, size:
15, relativeTo: .footnote) } // 13 -> 15
+ static var caption: AccessibleFont { AccessibleFont(fontName, size:
13, relativeTo: .caption) } // 12 -> 13
+// static var caption2: AccessibleFont { AccessibleFont(fontName, size:
12, relativeTo: .caption2) } // 11 -> 12
}
+struct AccessibilityFontViewModifier: ViewModifier {
+ @Environment(\.legibilityWeight) private var legibilityWeight
+
+ var font: AccessibleFont
+
+ func body(content: Content) -> some View {
+ content.font(font.value(legibilityWeight))
+ }
+}
+
+extension View {
+ func accessibilityFont(_ font: AccessibleFont) -> some View {
+ return self.modifier(AccessibilityFontViewModifier(font: font))
+ }
+}
+// MARK: -
+/// This works on-the-fly to update NavigationTitles when you change the font
+struct NavigationBarBuilder: UIViewControllerRepresentable {
+ var build: (UINavigationController) -> Void = { _ in }
+
+ func makeUIViewController(context:
UIViewControllerRepresentableContext<NavigationBarBuilder>) -> UIViewController
{
+ UIViewController()
+ }
+
+ func updateUIViewController(_ uiViewController: UIViewController,
+ context:
UIViewControllerRepresentableContext<NavigationBarBuilder>) {
+ if let navigationController = uiViewController.navigationController {
+ self.build(navigationController)
+ }
+ }
+}
+
+/// This works only once. Each following call does nothing - including
(re-)setting to nil
+@MainActor
+struct TalerNavBar: ViewModifier {
+ let talerFont: Int
+
+ static func setNavBarFonts(talerFont: Int) -> Void {
+ let navBarAppearance = UINavigationBar.appearance()
+ navBarAppearance.titleTextAttributes = nil
+ navBarAppearance.largeTitleTextAttributes = nil
+ if talerFont != 0 {
+ navBarAppearance.titleTextAttributes = [.font:
TalerFont.talerFont(talerFont, size: 24, relativeTo: .title2)]
+ navBarAppearance.largeTitleTextAttributes = [.font:
TalerFont.talerFont(talerFont, size: 38, relativeTo: .largeTitle)]
+ }
+ }
+
+ init(_ talerFont: Int) {
+ self.talerFont = talerFont
+ TalerNavBar.setNavBarFonts(talerFont: talerFont)
+ }
+
+ func body(content: Content) -> some View {
+ let _ = TalerNavBar.setNavBarFonts(talerFont: talerFont)
+ content
+ }
+
+}
+
+extension View {
+ @MainActor func talerNavBar(talerFont: Int) -> some View {
+ self.modifier(TalerNavBar(talerFont))
+ }
+}
+
+
+#if false
+//init() {
+// NavigationBarConfigurator.configureTitles()
+//}
+struct NavigationBarConfigurator {
+ static func configureTitles() {
+ let appearance = UINavigationBarAppearance()
+ let design = UIFontDescriptor.SystemDesign.rounded
+ if let descriptorWithDesign =
UIFontDescriptor.preferredFontDescriptor(withTextStyle: .largeTitle)
+ .withDesign(design),
+ let descriptorWithTraits =
descriptorWithDesign.withSymbolicTraits(.traitBold) {
+ let font = UIFont(descriptor: descriptorWithTraits, size: 34)
+ appearance.largeTitleTextAttributes = [.font: font,
.foregroundColor: UIColor.label]
+ }
+ if let smallTitleDescriptorWithDesign =
UIFontDescriptor.preferredFontDescriptor(withTextStyle: .headline)
.withDesign(design) {
+ let smallTitleFont = UIFont(descriptor:
smallTitleDescriptorWithDesign, size: 24)
+ appearance.titleTextAttributes = [.font:smallTitleFont,
.foregroundColor: UIColor.label]
+ }
+ UINavigationBar.appearance().standardAppearance = appearance
+ }
+}
+#endif
+// MARK: -
struct ContentViewFonts: View {
+ // let myWeight: Font.Weight
var body: some View {
VStack {
- Text("Text demo")
- .font(.talerFont(.regular))
- Text("Text demo")
- .font(.talerFont(.italic))
- Text("Text demo")
- .font(.talerFont(.bold))
- Text("Text demo")
- .font(.talerFont(.boldItalic))
-
- Text("Text demo")
- .font(.talerBody)
+ HStack {
+ Text("title a")
+ Text("bold").bold()
+ }
+ .accessibilityFont(.title)
+ .padding()
+ HStack {
+ Text("title2 a")
+ Text("italic").italic()
+ Text("bold").bold()
+ }
+ .accessibilityFont(.title2)
+ .padding()
+ Text("headline")
+ .accessibilityFont(.headline)
+ .padding(.top)
+ Text("headline bold")
+ .bold()
+ .accessibilityFont(.headline)
+ .padding(.bottom)
+ Text("title2 bold italic")
+ .bold()
+ .italic()
+ .accessibilityFont(.title2)
+ .padding()
}
}
}
diff --git a/TalerWallet1/Helper/KeyboardResponder.swift
b/TalerWallet1/Helper/KeyboardResponder.swift
index c5d9cde..c8e496d 100644
--- a/TalerWallet1/Helper/KeyboardResponder.swift
+++ b/TalerWallet1/Helper/KeyboardResponder.swift
@@ -19,6 +19,7 @@
import Combine
import UIKit
+@MainActor
public final class KeyboardResponder: ObservableObject {
@Published public var keyboardHeight: CGFloat = 0
diff --git a/TalerWallet1/Helper/UIScreen+screenSize.swift
b/TalerWallet1/Helper/UIScreen+screenSize.swift
new file mode 100644
index 0000000..3fe327e
--- /dev/null
+++ b/TalerWallet1/Helper/UIScreen+screenSize.swift
@@ -0,0 +1,11 @@
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
+import SwiftUI
+
+extension UIScreen {
+ static let screenWidth = UIScreen.main.bounds.size.width
+ static let screenHeight = UIScreen.main.bounds.size.height
+ static let screenSize = UIScreen.main.bounds.size
+}
diff --git a/TalerWallet1/Helper/View+Notification.swift
b/TalerWallet1/Helper/View+Notification.swift
index 00760bf..a68ddb7 100644
--- a/TalerWallet1/Helper/View+Notification.swift
+++ b/TalerWallet1/Helper/View+Notification.swift
@@ -64,7 +64,7 @@ extension View {
}
}
- func onAppEnteredBackground(
+ @MainActor func onAppEnteredBackground(
perform action: @escaping () -> Void
) -> some View {
onNotification(
diff --git a/TalerWallet1/Helper/View+dismissTop.swift
b/TalerWallet1/Helper/View+dismissTop.swift
index 40f0084..46ea8c7 100644
--- a/TalerWallet1/Helper/View+dismissTop.swift
+++ b/TalerWallet1/Helper/View+dismissTop.swift
@@ -22,7 +22,7 @@ import SwiftUI
/// A presented sheet (SwiftUI view) doesn't always close when calling
"dismiss()" provided by @Environment(\.dismiss),
/// so we are walking the view stack to find the top presentedViewController
(UIKit) and dismiss it.
extension View {
- public func dismissTop(animated: Bool = true) {
+ @MainActor public func dismissTop(animated: Bool = true) {
let windows = UIApplication.shared.connectedScenes.compactMap {
($0 as? UIWindowScene)?.keyWindow // TODO: iPad might have
more than 1 window
}
diff --git a/TalerWallet1/Helper/View+flippedDirection.swift
b/TalerWallet1/Helper/View+flippedDirection.swift
new file mode 100644
index 0000000..35a8b70
--- /dev/null
+++ b/TalerWallet1/Helper/View+flippedDirection.swift
@@ -0,0 +1,21 @@
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
+import SwiftUI
+
+struct FlippedDirection: ViewModifier {
+ @Environment(\.layoutDirection) var layoutDirection
+
+ func body(content: Content) -> some View {
+ let isLeft = layoutDirection == .leftToRight
+ content
+ .environment(\.layoutDirection, isLeft ? .rightToLeft :
.leftToRight)
+ }
+}
+
+extension View {
+ func flippedDirection() -> some View {
+ self.modifier(FlippedDirection())
+ }
+}
diff --git a/TalerWallet1/Model/Model+Balances.swift
b/TalerWallet1/Model/Model+Balances.swift
index f1a651c..3c5c585 100644
--- a/TalerWallet1/Model/Model+Balances.swift
+++ b/TalerWallet1/Model/Model+Balances.swift
@@ -8,7 +8,7 @@ fileprivate let ASYNCDELAY: UInt = 0 //set e.g to 6 or 9
seconds for debugging
// MARK: -
/// A currency balance
-struct Balance: Decodable, Hashable {
+struct Balance: Decodable, Hashable, Sendable {
var available: Amount
var scopeInfo: ScopeInfo
var requiresUserInput: Bool
@@ -36,14 +36,14 @@ fileprivate struct Balances: WalletBackendFormattedRequest {
struct Args: Encodable {} // no arguments needed
- struct Response: Decodable { // list of balances
+ struct Response: Decodable, Sendable { // list of balances
var balances: [Balance]
}
}
// MARK: -
extension WalletModel {
/// fetch Balances from Wallet-Core. No networking involved
- @MainActor func balancesM()
+ @MainActor func balancesM(_ stack: CallStack)
async -> [Balance] { // M for MainActor
do {
let request = Balances()
diff --git a/TalerWallet1/Model/Model+Exchange.swift
b/TalerWallet1/Model/Model+Exchange.swift
index f52ee75..304ef06 100644
--- a/TalerWallet1/Model/Model+Exchange.swift
+++ b/TalerWallet1/Model/Model+Exchange.swift
@@ -7,22 +7,38 @@ import taler_swift
import SymLog
fileprivate let ASYNCDELAY: UInt = 0 //set e.g to 6 or 9 seconds for
debugging
+enum ExchangeEntryStatus: String, Codable {
+ case preset
+ case ephemeral
+ case used
+}
+
+enum ExchangeUpdateStatus: String, Codable {
+ case initial
+ case initialUpdate = "initial(update)"
+ case suspended
+ case failed
+ case outdatedUpdate = "outdated(update)"
+ case ready
+ case readyUpdate = "ready(update)"
+}
// MARK: -
/// The result from wallet-core's ListExchanges
struct Exchange: Codable, Hashable, Identifiable {
static func == (lhs: Exchange, rhs: Exchange) -> Bool {
return lhs.exchangeBaseUrl == rhs.exchangeBaseUrl &&
- lhs.exchangeStatus == rhs.exchangeStatus &&
- lhs.permanent == rhs.permanent
+ lhs.tosStatus == rhs.tosStatus &&
+ lhs.exchangeEntryStatus == rhs.exchangeEntryStatus &&
+ lhs.exchangeUpdateStatus == rhs.exchangeUpdateStatus
}
var exchangeBaseUrl: String
var currency: String?
var paytoUris: [String]
- var tosStatus: String
- var exchangeStatus: String
+ var tosStatus: ExchangeTosStatus
+ var exchangeEntryStatus: ExchangeEntryStatus
+ var exchangeUpdateStatus: ExchangeUpdateStatus
var ageRestrictionOptions: [Int]
- var permanent: Bool
var lastUpdateErrorInfo: ExchangeError?
var id: String {
diff --git a/TalerWallet1/Model/Model+Transactions.swift
b/TalerWallet1/Model/Model+Transactions.swift
index 0795255..f3fb35b 100644
--- a/TalerWallet1/Model/Model+Transactions.swift
+++ b/TalerWallet1/Model/Model+Transactions.swift
@@ -108,7 +108,7 @@ struct ResumeTransaction: WalletBackendFormattedRequest {
// MARK: -
extension WalletModel {
/// ask wallet-core for its list of transactions filtered by searchString
- func transactionsT(currency: String? = nil, searchString: String? = nil)
+ func transactionsT(_ stack: CallStack, currency: String? = nil,
searchString: String? = nil)
async -> [Transaction] { // might
be called from a background thread itself
do {
let request = GetTransactions(currency: currency, search:
searchString)
@@ -119,9 +119,9 @@ extension WalletModel {
}
}
/// fetch transactions from Wallet-Core. No networking involved
- @MainActor func transactionsMA(currency: String? = nil, searchString:
String? = nil)
+ @MainActor func transactionsMA(_ stack: CallStack, currency: String? =
nil, searchString: String? = nil)
async -> [Transaction] { // M for MainActor
- return await transactionsT(currency: currency, searchString:
searchString)
+ return await transactionsT(stack.push(), currency: currency,
searchString: searchString)
}
/// abort the specified transaction from Wallet-Core. No networking
involved
diff --git a/TalerWallet1/Model/Model+Withdraw.swift
b/TalerWallet1/Model/Model+Withdraw.swift
index 754c9dd..7b8124c 100644
--- a/TalerWallet1/Model/Model+Withdraw.swift
+++ b/TalerWallet1/Model/Model+Withdraw.swift
@@ -72,6 +72,9 @@ enum ExchangeTosStatus: String, Codable {
case changed = "changed"
case notFound = "not-found"
case unknown = "unknown"
+ case pending
+ case proposed
+// case accepted
}
struct ExchangeTermsOfService: Decodable {
var currentEtag: String
diff --git a/TalerWallet1/Model/WalletModel.swift
b/TalerWallet1/Model/WalletModel.swift
index eea87e4..508fbf9 100644
--- a/TalerWallet1/Model/WalletModel.swift
+++ b/TalerWallet1/Model/WalletModel.swift
@@ -24,7 +24,7 @@ class WalletModel: ObservableObject {
#endif
let sendTime = Date.now
do {
- let (response, id) = try await
WalletCore.shared.sendFormattedRequest(request: request)
+ let (response, id) = try await
WalletCore.shared.sendFormattedRequest(request)
#if !DEBUG
let timeUsed = Date.now - sendTime
logger.log("received: \(request.operation(), privacy: .public)
(\(id, privacy: .public)) after \(timeUsed.milliseconds, privacy: .public) ms")
diff --git a/TalerWallet1/Views/Balances/BalanceRowView.swift
b/TalerWallet1/Views/Balances/BalanceRowView.swift
index 89a5a15..b2b0a31 100644
--- a/TalerWallet1/Views/Balances/BalanceRowView.swift
+++ b/TalerWallet1/Views/Balances/BalanceRowView.swift
@@ -15,16 +15,13 @@ struct BalanceButton: View {
var body: some View {
Button(action: rowAction) {
VStack(alignment: .trailing, spacing: 0) {
- HStack(alignment: .firstTextBaseline, spacing: 0) {
- Text("B", comment: "the first letter of Balance - or leave
empty")
- .font(.title2)
- Text("alance", comment: "the remaining letters of Balance
- or all if you left B empty")
- .font(.footnote).bold()
- }
+ Text("Balance", comment: "Balance in main view")
+ .bold() // in iOS-15 defined for Text, but not for
Font (only iOS-16)
Text(verbatim: "\(amount.valueStr)") // TODO:
CurrencyFormatter?
- .font(.title)
+ .accessibilityFont(.title)
.monospacedDigit()
}
+ .accessibilityFont(.subheadline)
} .disabled(false)
.accessibilityElement(children:
/*@START_MENU_TOKEN@*/.ignore/*@END_MENU_TOKEN@*/)
.accessibilityLabel("Balance \(amount.readableDescription)") //
TODO: CurrencyFormatter!
@@ -42,7 +39,7 @@ struct BalanceRowView: View {
func needVStack(_ amount: Amount) -> Bool {
// Sizes: 320 (SE), 375 (X, Xs, 12, 13 mini), 390 (12,13,14), 414
(Plus, Max), 428 (Pro Max)
- guard 350 < UIScreen.main.bounds.width else {return true} // always
for iPhone SE 1st Gen
+ guard 350 < UIScreen.screenWidth else {return true} // always for
iPhone SE 1st Gen
var count = amount.currencyStr.count
// print(sizeCategory)
switch sizeCategory {
@@ -89,9 +86,9 @@ struct BalanceRowView: View {
struct BalanceRowView_Previews: PreviewProvider {
static var previews: some View {
List {
- BalanceRowView(amount: try! Amount(fromString: "TestKUDOS" +
":1234.56"),
+ BalanceRowView(amount: try! Amount(fromString: TESTCURRENCY +
":1234.56"),
sendAction: {}, recvAction: {}, rowAction: {})
- BalanceRowView(amount: try! Amount(fromString: "KUDOS" +
":1234.56"),
+ BalanceRowView(amount: try! Amount(fromString: DEMOCURRENCY +
":1234.56"),
sendAction: {}, recvAction: {}, rowAction: {})
}
}
diff --git a/TalerWallet1/Views/Balances/BalancesListView.swift
b/TalerWallet1/Views/Balances/BalancesListView.swift
index 934acb2..f1b091e 100644
--- a/TalerWallet1/Views/Balances/BalancesListView.swift
+++ b/TalerWallet1/Views/Balances/BalancesListView.swift
@@ -10,6 +10,7 @@ import AVFoundation
/// This view shows the list of balances / currencies, each in its own section
struct BalancesListView: View {
private let symLog = SymLogV()
+ let stack: CallStack
let navTitle: String
let hamburgerAction: () -> Void
@@ -69,10 +70,12 @@ struct BalancesListView: View {
})
}
- private func reloadAction() async -> Int {
- let reloaded = await model.balancesM()
+ /// runs on MainActor if called in some Task {}
+ @discardableResult
+ private func reloadAction(_ stack: CallStack) async -> Int {
+ let reloaded = await model.balancesM(stack.push())
let count = reloaded.count
- balances = reloaded
+ balances = reloaded // redraw
return count
}
@@ -81,7 +84,7 @@ struct BalancesListView: View {
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
- Content(symLog: symLog, balances: $balances,
+ Content(symLog: symLog, stack: stack.push(), balances: $balances,
centsToTransfer: $centsToTransfer, summary: $summary,
reloadAction: reloadAction)
.navigationTitle(navTitle)
@@ -91,14 +94,13 @@ struct BalancesListView: View {
if balances.isEmpty {
WalletEmptyView()
.refreshable { // already async
- symLog.log("refreshing")
- let count = await reloadAction()
+ symLog.log("empty refreshing")
+ let count = await reloadAction(stack.push("empty
refreshing"))
if count > 0 {
// postNotificationM(.BalanceReloaded)
NotificationCenter.default.post(name:
.BalanceReloaded, object: nil)
}
}
-
}
}
.alert("Scanning QR-codes requires access to the camera",
@@ -110,12 +112,12 @@ struct BalancesListView: View {
DebugViewC.shared.setViewID(VIEW_BALANCES)
}
.sheet(isPresented: $showQRScanner) {
- let sheet = AnyView(QRSheet())
+ let sheet = AnyView(QRSheet(stack: stack.push()))
Sheet(sheetView: sheet)
} // sheet
.task {
symLog.log(".task getBalances")
- _ = await reloadAction()
+ await reloadAction(stack.push(".task"))
} // task
}
}
@@ -123,11 +125,13 @@ struct BalancesListView: View {
extension BalancesListView {
struct Content: View {
let symLog: SymLogV?
+ let stack: CallStack
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
@Binding var balances: [Balance]
@Binding var centsToTransfer: UInt64
@Binding var summary: String
- var reloadAction: () async -> Int
+// @discardableResult
+ var reloadAction: (_ stack: CallStack) async -> Int
@State private var isActive = true
@State private var shouldReload = false
@@ -140,14 +144,15 @@ extension BalancesListView {
Group { // necessary for .backslide transition (bug in SwiftUI)
let count = balances.count
List(balances, id: \.self) { balance in
- BalancesSectionView(balance: balance,
+ BalancesSectionView(stack: stack.push(),
+ balance: balance,
sectionCount: count,
centsToTransfer: $centsToTransfer,
summary: $summary)
}
.refreshable { // already async
symLog?.log("refreshing")
- let count = await reloadAction()
+ let count = await reloadAction(stack.push("refreshing"))
if count > 0 {
// postNotificationM(.BalanceReloaded)
NotificationCenter.default.post(name:
.BalanceReloaded, object: nil)
@@ -160,7 +165,7 @@ extension BalancesListView {
if shouldReload {
shouldReload = false
symLog?.log(".onAppear ==> shouldReload was true,
reloading now")
- Task { await reloadAction() }
+ Task { await reloadAction(stack.push("shouldReload")) } //
runs on MainActor
}
}
.onDisappear() {
@@ -171,7 +176,7 @@ extension BalancesListView {
// doesn't need to be received on main thread because we just
reload in a background task anyway
if isActive {
symLog?.log(".onNotification(.BalanceChange) ==> reload")
- Task { await reloadAction() }
+ Task { await reloadAction(stack.push(".BalanceChange")) }
} else {
symLog?.log(".onNotification(.BalanceChange) ==> reload
postponed, shouldReload = true")
shouldReload = true
diff --git a/TalerWallet1/Views/Balances/BalancesSectionView.swift
b/TalerWallet1/Views/Balances/BalancesSectionView.swift
index 6423dbe..3bd555f 100644
--- a/TalerWallet1/Views/Balances/BalancesSectionView.swift
+++ b/TalerWallet1/Views/Balances/BalancesSectionView.swift
@@ -15,7 +15,8 @@ import SymLog
/// optional: Suspended / Aborting / Aborted / Expired
struct BalancesSectionView: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let balance: Balance
let sectionCount: Int
@Binding var centsToTransfer: UInt64
@@ -65,17 +66,17 @@ struct BalancesSectionView: View {
#endif
let currency = balance.available.currencyStr
let reloadCompleted = {
- transactions = await model.transactionsT(currency: currency)
+ transactions = await model.transactionsT(stack.push(), currency:
currency)
completedTransactions =
WalletModel.completedTransactions(transactions)
// sectionID = UUID()
}
let reloadPending = {
- transactions = await model.transactionsT(currency: currency)
+ transactions = await model.transactionsT(stack.push(), currency:
currency)
pendingTransactions = WalletModel.pendingTransactions(transactions)
// sectionID = UUID()
}
let reloadUncompleted = {
- transactions = await model.transactionsT(currency: currency)
+ transactions = await model.transactionsT(stack.push(), currency:
currency)
uncompletedTransactions =
WalletModel.uncompletedTransactions(transactions)
// sectionID = UUID()
}
@@ -83,15 +84,17 @@ struct BalancesSectionView: View {
Section {
if "KUDOS" == currency && !balance.available.isZero {
Text("You can spend these KUDOS in the [Demo
Shop](https://shop.demo.taler.net), or send them to another wallet.")
+ .accessibilityFont(.body)
.multilineTextAlignment(.leading)
}
- NavigationLinksView(balance: balance,
- centsToTransfer: $centsToTransfer,
- summary: $summary,
-// buttonSelected: $buttonSelected,
- completedTransactions: $completedTransactions,
- reloadAllAction: reloadCompleted,
- reloadOneAction: reloadOneAction)
+ NavigationLinksView(stack: stack.push(),
+ balance: balance,
+ centsToTransfer: $centsToTransfer,
+ summary: $summary,
+// buttonSelected: $buttonSelected,
+ completedTransactions: $completedTransactions,
+ reloadAllAction: reloadCompleted,
+ reloadOneAction: reloadOneAction)
let hasPending = pendingTransactions.count > 0
if hasPending {
let (pendingIncoming, pendingOutgoing) =
computePending(currency: currency)
@@ -99,11 +102,13 @@ struct BalancesSectionView: View {
NavigationLink {
//let _ = print("button: Pending Transactions: \(currency)")
LazyView {
- TransactionsListView(navTitle: String(localized:
"Pending"), currency: currency,
- transactions: pendingTransactions,
- showUpDown: false,
- reloadAllAction: reloadPending,
- reloadOneAction: reloadOneAction)
+ TransactionsListView(stack: stack.push(),
+ navTitle: String(localized:
"Pending"),
+ currency: currency,
+ transactions: pendingTransactions,
+ showUpDown: false,
+ reloadAllAction: reloadPending,
+ reloadOneAction: reloadOneAction)
}
} label: {
VStack(spacing: 6) {
@@ -118,6 +123,7 @@ struct BalancesSectionView: View {
}
if rows == 0 {
Text("Some pending transactions")
+ .accessibilityFont(.body)
}
}
}
@@ -127,12 +133,13 @@ struct BalancesSectionView: View {
NavigationLink {
//let _ = print("button: Uncompleted Transactions: \(currency)")
LazyView {
- TransactionsListView(navTitle: String(localized:
"Uncompleted"),
- currency: currency,
- transactions: uncompletedTransactions,
- showUpDown: false,
- reloadAllAction: reloadUncompleted,
- reloadOneAction: reloadOneAction)
+ TransactionsListView(stack: stack.push(),
+ navTitle: String(localized:
"Uncompleted"),
+ currency: currency,
+ transactions: uncompletedTransactions,
+ showUpDown: false,
+ reloadAllAction: reloadUncompleted,
+ reloadOneAction: reloadOneAction)
}
} label: {
UncompletedRowView(uncompletedTransactions:
$uncompletedTransactions)
@@ -140,13 +147,16 @@ struct BalancesSectionView: View {
}
} header: {
- Text(currency)
- .font(.title)
+ HStack (alignment: .bottom, spacing: 10) {
+ BarGraph(transactions: $completedTransactions, barHeight: 10)
+ Text(currency)
+ .accessibilityFont(.title2)
+ }
}.id(sectionID)
.task {
// if shownSectionID != sectionID {
- symLog.log("task for BalancesSectionView \(sectionID) - reload
Transactions")
- let response = await model.transactionsT(currency: currency)
+ symLog.log("task for \(sectionID) - reload Transactions")
+ let response = await model.transactionsT(stack.push("task for
\(sectionID) - reload Transactions"), currency: currency)
transactions = response
pendingTransactions = WalletModel.pendingTransactions(response)
uncompletedTransactions =
WalletModel.uncompletedTransactions(response)
@@ -172,18 +182,20 @@ struct BalancesSectionView: View {
let slice = sortedTransactions.prefix(3)
let threeTransactions = Array(slice)
TransactionsRowsView(symLog: symLog,
- currency: currency,
- transactions: threeTransactions,
- reloadOneAction: reloadOneAction)
+ stack: stack.push(),
+ currency: currency,
+ transactions: threeTransactions,
+ reloadOneAction: reloadOneAction)
} header: {
Text("Recent transactions")
- .font(.callout)
+ .accessibilityFont(.callout)
}
}
} // body
}
fileprivate struct NavigationLinksView : View {
+ let stack: CallStack
let balance: Balance
// let sectionCount: Int
@Binding var centsToTransfer: UInt64
@@ -198,25 +210,29 @@ fileprivate struct NavigationLinksView : View {
let currency = balance.available.currencyStr
HStack(spacing: 0) {
NavigationLink(destination: LazyView {
- SendAmount(amountAvailable: balance.available,
- centsToTransfer: $centsToTransfer,
- summary: $summary)
+ SendAmount(stack: stack.push(),
+ amountAvailable: balance.available,
+ centsToTransfer: $centsToTransfer,
+ summary: $summary)
}, tag: 1, selection: $buttonSelected
) { EmptyView() }.frame(width: 0).opacity(0).hidden() //
SendAmount
NavigationLink(destination: LazyView {
- RequestPayment(scopeInfo: balance.scopeInfo,
- centsToTransfer: $centsToTransfer,
- summary: $summary)
+ RequestPayment(stack: stack.push(),
+ scopeInfo: balance.scopeInfo,
+ centsToTransfer: $centsToTransfer,
+ summary: $summary)
}, tag: 2, selection: $buttonSelected
) { EmptyView() }.frame(width: 0).opacity(0).hidden() //
RequestPayment
NavigationLink(destination: LazyView {
- TransactionsListView(navTitle: String(localized:
"Transactions"), currency: currency,
- transactions: completedTransactions,
- showUpDown: true,
- reloadAllAction: reloadAllAction,
- reloadOneAction: reloadOneAction)
+ TransactionsListView(stack: stack.push(),
+ navTitle: String(localized: "Transactions"),
+ currency: currency,
+ transactions: completedTransactions,
+ showUpDown: true,
+ reloadAllAction: reloadAllAction,
+ reloadOneAction: reloadOneAction)
}, tag: 3, selection: $buttonSelected
) { EmptyView() }.frame(width: 0).opacity(0).hidden() //
TransactionsListView
@@ -231,7 +247,7 @@ fileprivate struct NavigationLinksView : View {
}
}
// MARK: -
-#if DEBUG
+#if false // model crashes
fileprivate struct BindingViewContainer : View {
@State var centsToTransfer: UInt64 = 333
@State private var summary: String = "bla-bla"
diff --git a/TalerWallet1/Views/Balances/PendingRowView.swift
b/TalerWallet1/Views/Balances/PendingRowView.swift
index 657b1a6..c883f57 100644
--- a/TalerWallet1/Views/Balances/PendingRowView.swift
+++ b/TalerWallet1/Views/Balances/PendingRowView.swift
@@ -12,26 +12,24 @@ struct PendingRowView: View {
var body: some View {
HStack {
+ let pendingColor = WalletColors().pendingColor(incoming)
Image(systemName: incoming ? "text.badge.plus"
: "text.badge.minus")
- .font(.largeTitle)
-// .foregroundColor(WalletColors().pendingColor) // pending
is always gray
- .foregroundColor(WalletColors().pendingColor(incoming))
+ .foregroundColor(pendingColor)
+ .accessibilityFont(.largeTitle)
.accessibility(hidden: true)
Spacer()
Text("pending\n" + (incoming ? "incoming" : "outgoing"))
+ .accessibilityFont(.body)
Spacer()
VStack(alignment: .trailing) {
let sign = incoming ? "+" : "-"
let valueStr = sign + amount.valueStr
Text(valueStr)
- .font(.title)
- .foregroundColor(WalletColors().pendingColor(incoming))
+ .foregroundColor(pendingColor)
+ .accessibilityFont(.title)
.monospacedDigit()
-// Text("PENDING")
-// .font(.callout)
-// .foregroundColor(WalletColors().pendingColor(incoming))
}
}
.accessibilityElement(children: .combine)
diff --git a/TalerWallet1/Views/Balances/UncompletedRowView.swift
b/TalerWallet1/Views/Balances/UncompletedRowView.swift
index b4637a2..a9436b6 100644
--- a/TalerWallet1/Views/Balances/UncompletedRowView.swift
+++ b/TalerWallet1/Views/Balances/UncompletedRowView.swift
@@ -14,7 +14,7 @@ struct UncompletedRowView: View {
HStack {
Spacer()
Text("\(count) uncompleted transactions")
- .font(.title2)
+ .accessibilityFont(.title2)
.foregroundColor(WalletColors().uncompletedColor)
Spacer()
}
diff --git a/TalerWallet1/Views/Exchange/ExchangeListView.swift
b/TalerWallet1/Views/Exchange/ExchangeListView.swift
index f36d510..3d1a1f2 100644
--- a/TalerWallet1/Views/Exchange/ExchangeListView.swift
+++ b/TalerWallet1/Views/Exchange/ExchangeListView.swift
@@ -9,6 +9,7 @@ import SymLog
/// This view shows the list of exchanges
struct ExchangeListView: View {
private let symLog = SymLogV(0)
+ let stack: CallStack
let navTitle: String
var hamburgerAction: () -> Void
@@ -24,7 +25,7 @@ struct ExchangeListView: View {
}
func addExchange(_ exUrl: String) -> Void {
- Task {
+ Task { // runs on MainActor
symLog.log("adding: \(exUrl)")
do {
try await model.addExchange(url: exUrl)
@@ -50,9 +51,10 @@ struct ExchangeListView: View {
//Text("Exchanges...")
Content(symLog: symLog,
- exchanges: $exchanges,
- centsToTransfer: $centsToTransfer,
- reloadAction: reloadAction)
+ stack: stack.push(),
+ exchanges: $exchanges,
+ centsToTransfer: $centsToTransfer,
+ reloadAction: reloadAction)
.navigationTitle(navTitle)
.navigationBarItems(leading: HamburgerButton(action: hamburgerAction),
trailing: PlusButton(action: plusAction)
@@ -60,6 +62,7 @@ struct ExchangeListView: View {
.overlay {
if exchanges.isEmpty {
Text("No Exchanges yet...")
+ .accessibilityFont(.body)
}
}
.task {
@@ -83,6 +86,7 @@ struct ExchangeListView: View {
extension ExchangeListView {
struct Content: View {
let symLog: SymLogV?
+ let stack: CallStack
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
@Binding var exchanges: [Exchange]
@Binding var centsToTransfer: UInt64
@@ -109,7 +113,9 @@ extension ExchangeListView {
let sortedDict = dict.sorted{ $0.key < $1.key}
Group { // necessary for .backslide transition (bug in SwiftUI)
List(sortedDict, id: \.key) { key, value in
- ExchangeSectionView(currency: key, exchanges: value,
centsToTransfer: $centsToTransfer)
+ ExchangeSectionView(stack: stack.push(),
+ currency: key, exchanges: value,
+ centsToTransfer: $centsToTransfer)
}
.refreshable {
symLog?.log("refreshing")
@@ -123,7 +129,7 @@ extension ExchangeListView {
.onNotification(.ExchangeAdded) { notification in
// doesn't need to be received on main thread because we just
reload in the background anyway
symLog?.log(".onNotification(.ExchangeAdded) ==> reloading
exchanges")
- Task { await reloadAction() }
+ Task { await reloadAction() } // runs on MainActor
}
} // body
}
diff --git a/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
b/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
index ffe53dc..a87ea0b 100644
--- a/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
+++ b/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
@@ -6,6 +6,7 @@ import SwiftUI
import taler_swift
struct ExchangeRowView: View {
+ let stack: CallStack
let exchange: Exchange
let currency: String
@Binding var centsToTransfer: UInt64
@@ -16,14 +17,16 @@ struct ExchangeRowView: View {
HStack(spacing: 0) { // can't use the built in Label because it
adds the accessory arrow
Text(baseURL.trimURL())
+ .accessibilityFont(.body)
NavigationLink(destination: LazyView {
EmptyView() // TODO: Deposit
}, tag: 1, selection: $buttonSelected
) { EmptyView() }.frame(width: 0).opacity(0)
NavigationLink(destination: LazyView {
- ManualWithdraw(exchange: exchange,
- centsToTransfer: $centsToTransfer)
+ ManualWithdraw(stack: stack.push(),
+ exchange: exchange,
+ centsToTransfer: $centsToTransfer)
}, tag: 2, selection: $buttonSelected
) { EmptyView() }.frame(width: 0).opacity(0)
}.listRowSeparator(.hidden)
@@ -46,6 +49,7 @@ struct ExchangeRowView: View {
/// currency
/// [Deposit Coins] [Withdraw Coins]
struct ExchangeSectionView: View {
+ let stack: CallStack
let currency: String
let exchanges: [Exchange]
@@ -58,12 +62,15 @@ struct ExchangeSectionView: View {
#endif
Section {
ForEach(exchanges) { exchange in
- ExchangeRowView(exchange: exchange, currency: currency,
centsToTransfer: $centsToTransfer)
+ ExchangeRowView(stack: stack.push(),
+ exchange: exchange,
+ currency: currency,
+ centsToTransfer: $centsToTransfer)
}
.accessibilityElement(children: .combine)
} header: {
Text(currency)
- .font(.title)
+ .accessibilityFont(.title)
}
}
}
@@ -76,18 +83,18 @@ struct ExchangeRow_Container : View {
let exchange1 = Exchange(exchangeBaseUrl: ARS_AGE_EXCHANGE,
currency: LONGCURRENCY,
paytoUris: [],
- tosStatus: "tosStatus",
- exchangeStatus: "exchangeStatus",
- ageRestrictionOptions: [12,16],
- permanent: true)
+ tosStatus: .pending,
+ exchangeEntryStatus: .preset,
+ exchangeUpdateStatus: .initial,
+ ageRestrictionOptions: [12,16])
let exchange2 = Exchange(exchangeBaseUrl: ARS_EXP_EXCHANGE,
currency: LONGCURRENCY,
paytoUris: [],
- tosStatus: "tosStatus",
- exchangeStatus: "exchangeStatus",
- ageRestrictionOptions: [],
- permanent: false)
- ExchangeSectionView(currency: LONGCURRENCY, exchanges: [exchange1,
exchange2],
+ tosStatus: .proposed,
+ exchangeEntryStatus: .ephemeral,
+ exchangeUpdateStatus: .ready,
+ ageRestrictionOptions: [])
+ ExchangeSectionView(stack: CallStack("Preview"), currency:
LONGCURRENCY, exchanges: [exchange1, exchange2],
centsToTransfer: $centsToTransfer)
}
}
diff --git a/TalerWallet1/Views/Exchange/ManualWithdraw.swift
b/TalerWallet1/Views/Exchange/ManualWithdraw.swift
index 5486073..5e45416 100644
--- a/TalerWallet1/Views/Exchange/ManualWithdraw.swift
+++ b/TalerWallet1/Views/Exchange/ManualWithdraw.swift
@@ -8,7 +8,8 @@ import SymLog
// Will be called by the user tapping "Withdraw Coins" in the exchange list
struct ManualWithdraw: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let exchange: Exchange
@Binding var centsToTransfer: UInt64
@@ -39,7 +40,7 @@ struct ManualWithdraw: View {
currency: currency, amountEffective:
withdrawalAmountDetails?.amountEffective)
Text(exchange.exchangeBaseUrl.trimURL())
.multilineTextAlignment(.center)
-// .font(.title3)
+ .accessibilityFont(.body)
let disabled = (centsToTransfer == 0) || someCoins.invalid ||
someCoins.tooMany
if !disabled {
@@ -51,23 +52,17 @@ struct ManualWithdraw: View {
// :
selectedAge
//let _ = print(selectedAge, restrictAge)
NavigationLink(destination: LazyView {
- ManualWithdrawDone(exchange: exchange,
- centsToTransfer: centsToTransfer)
-// restrictAge: restrictAge)
+ ManualWithdrawDone(stack: stack.push(),
+ exchange: exchange,
+ centsToTransfer: centsToTransfer)
+// restrictAge: restrictAge)
}) {
Text("Confirm Withdrawal") //
VIEW_WITHDRAW_ACCEPT
}.buttonStyle(TalerButtonStyle(type: .prominent))
} else {
- Text("You must accept the Terms of Service first
before you can withdraw electronic cash to your wallet.")
- .multilineTextAlignment(.leading)
- .padding()
- NavigationLink(destination: LazyView {
- WithdrawTOSView(exchangeBaseUrl:
exchange.exchangeBaseUrl,
- viewID:
VIEW_WITHDRAW_TOS,
- acceptAction: nil)
// pop back to here
- }) {
- Text("Read Terms of Service") //
VIEW_WITHDRAW_TOS
- }.buttonStyle(TalerButtonStyle(type: .prominent))
+ ToSButtonView(exchangeBaseUrl:
exchange.exchangeBaseUrl,
+ viewID: VIEW_WITHDRAW_TOS,
+ p2p: false)
}
}
} // disabled
@@ -108,13 +103,14 @@ struct ManualWithdraw_Container : View {
let exchange = Exchange(exchangeBaseUrl: DEMOEXCHANGE,
currency: LONGCURRENCY,
paytoUris: [],
- tosStatus: "tosStatus",
- exchangeStatus: "exchangeStatus",
- ageRestrictionOptions: [],
- permanent: false)
- ManualWithdraw(exchange: exchange,
- centsToTransfer: $centsToTransfer,
- withdrawalAmountDetails: details)
+ tosStatus: .pending,
+ exchangeEntryStatus: .preset,
+ exchangeUpdateStatus: .initial,
+ ageRestrictionOptions: [])
+ ManualWithdraw(stack: CallStack("Preview"),
+ exchange: exchange,
+ centsToTransfer: $centsToTransfer,
+ withdrawalAmountDetails: details)
}
}
diff --git a/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
b/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
index e5d3b20..df5b3b1 100644
--- a/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
+++ b/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
@@ -7,7 +7,8 @@ import taler_swift
import SymLog
struct ManualWithdrawDone: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let navTitle = String(localized: "Wire Transfer")
let exchange: Exchange
@@ -30,15 +31,16 @@ struct ManualWithdrawDone: View {
#endif
Group {
if let transactionId {
- TransactionDetailView(transactionId: transactionId,
- reloadAction: reloadOneAction,
- navTitle: navTitle,
- doneAction:
ViewState.shared.popToRootView,
- abortAction: nil,
- deleteAction: nil,
- failAction: nil,
- suspendAction: nil,
- resumeAction: nil)
+ TransactionDetailView(stack: stack.push(),
+ transactionId: transactionId,
+ reloadAction: reloadOneAction,
+ navTitle: navTitle,
+ doneAction: ViewState.shared.popToRootView,
+ abortAction: nil,
+ deleteAction: nil,
+ failAction: nil,
+ suspendAction: nil,
+ resumeAction: nil)
.navigationBarBackButtonHidden(true)
.interactiveDismissDisabled() // can only use "Done"
button to dismiss
// .navigationTitle(navTitle)
@@ -71,12 +73,13 @@ struct ManualWithdrawDone_Container : View {
let exchange = Exchange(exchangeBaseUrl: DEMOEXCHANGE,
currency: LONGCURRENCY,
paytoUris: [],
- tosStatus: "tosStatus",
- exchangeStatus: "exchangeStatus",
- ageRestrictionOptions: [],
- permanent: false)
- ManualWithdrawDone(exchange: exchange,
- centsToTransfer: centsToTransfer)
+ tosStatus: .pending,
+ exchangeEntryStatus: .preset,
+ exchangeUpdateStatus: .initial,
+ ageRestrictionOptions: [])
+ ManualWithdrawDone(stack: CallStack("Preview"),
+ exchange: exchange,
+ centsToTransfer: centsToTransfer)
}
}
diff --git a/TalerWallet1/Views/Exchange/QuiteSomeCoins.swift
b/TalerWallet1/Views/Exchange/QuiteSomeCoins.swift
index aa00586..961d9d6 100644
--- a/TalerWallet1/Views/Exchange/QuiteSomeCoins.swift
+++ b/TalerWallet1/Views/Exchange/QuiteSomeCoins.swift
@@ -47,7 +47,7 @@ extension SomeCoins {
}
// MARK: -
struct QuiteSomeCoins: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
let someCoins: SomeCoins
let shouldShowFee: Bool
let currency: String
@@ -57,11 +57,12 @@ struct QuiteSomeCoins: View {
if !someCoins.invalid {
if !someCoins.tooMany {
if someCoins.manyCoins {
- Text(someCoins.quiteSome ? "Warning: It will take quite
some time to encrypt this amount!"
- : "Warning: It will take some
time to encrypt this amount.")
+ Text(someCoins.quiteSome ? "Warning: It will take quite
some time to withdraw this amount!"
+ : "Warning: It will take some
time to withdraw this amount.")
+ .foregroundColor(someCoins.quiteSome ? .red : .primary)
+ .accessibilityFont(.body)
.multilineTextAlignment(.leading)
.padding(.vertical, 6)
- .foregroundColor(someCoins.quiteSome ? .red : .primary)
} // warnings
}
}
@@ -71,6 +72,7 @@ struct QuiteSomeCoins: View {
: someCoins.hasFee ? "- \(someCoins.fee) fee"
: "No withdrawal fee")
.foregroundColor((someCoins.invalid || someCoins.tooMany ||
someCoins.hasFee) ? .red : .primary)
+ .accessibilityFont(.body)
// .padding(4)
}
}
diff --git a/TalerWallet1/Helper/AgePicker.swift
b/TalerWallet1/Views/HelperViews/AgePicker.swift
similarity index 94%
rename from TalerWallet1/Helper/AgePicker.swift
rename to TalerWallet1/Views/HelperViews/AgePicker.swift
index 2622f39..d833037 100644
--- a/TalerWallet1/Helper/AgePicker.swift
+++ b/TalerWallet1/Views/HelperViews/AgePicker.swift
@@ -32,7 +32,7 @@ struct AgePicker: View {
HStack {
Text("If this wallet belongs to a child or teenager, the
generated electronic cash should be age-restricted:")
.multilineTextAlignment(.leading)
- .font(.footnote)
+ .accessibilityFont(.footnote)
Spacer()
}.padding(.top)
Picker("Select age", selection: $selectedAge) {
@@ -42,6 +42,7 @@ struct AgePicker: View {
: "\(index) years").tag(index)
}
}
+ .accessibilityFont(.body)
}
}
}
diff --git a/TalerWallet1/Views/HelperViews/AmountView.swift
b/TalerWallet1/Views/HelperViews/AmountView.swift
index 8702ac5..f635dc0 100644
--- a/TalerWallet1/Views/HelperViews/AmountView.swift
+++ b/TalerWallet1/Views/HelperViews/AmountView.swift
@@ -15,15 +15,15 @@ struct AmountView: View {
Text(title)
.fixedSize(horizontal: false, vertical: true) // wrap in
scrollview
.multilineTextAlignment(.leading)
- .font(.body)
+ .accessibilityFont(.body)
HStack {
Spacer()
Text(value)
.fixedSize(horizontal: false, vertical: true) // wrap in
scrollview
.multilineTextAlignment(.center)
- .font(large ? .title : .title2)
-// .fontWeight(large ? .medium : .regular) //
@available(iOS 16.0, *)
.foregroundColor(color)
+ .accessibilityFont(large ? .title : .title2)
+// .fontWeight(large ? .medium : .regular) //
@available(iOS 16.0, *)
.monospacedDigit()
Spacer()
}
diff --git a/TalerWallet1/Views/HelperViews/BarGraph.swift
b/TalerWallet1/Views/HelperViews/BarGraph.swift
new file mode 100644
index 0000000..a842c01
--- /dev/null
+++ b/TalerWallet1/Views/HelperViews/BarGraph.swift
@@ -0,0 +1,87 @@
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
+import SwiftUI
+
+struct BarGraph: View {
+ @Binding var transactions: [Transaction]
+ let barHeight : Double
+
+ func maxValue(_ someTransactions: [Transaction]) -> Double {
+ var maxValue = 0.0
+ for transaction in someTransactions {
+ let value = transaction.common.amountEffective.value
+ if value > maxValue {
+ maxValue = value
+ }
+ }
+ return maxValue
+ }
+
+ var body: some View {
+ let slice = transactions.prefix(8)
+ let eightTransactions: [Transaction] = Array(slice)
+ let count = eightTransactions.count
+ let maxValue = maxValue(eightTransactions)
+
+ HStack(alignment: .firstTextBaseline, spacing: 1) {
+#if DEBUG
+// Text("first")
+#endif
+ if count > 0 {
+ ForEach(Array(eightTransactions.enumerated()), id: \.element)
{ index, transaction in
+ let common = transaction.common
+ let incoming = common.incoming()
+ let netto = common.amountEffective.value
+ let valueColored = barHeight * netto / maxValue
+ let valueTransparent = barHeight - valueColored
+// let _ = print("max: \(maxValue), ", incoming ? "+" :
"-", netto)
+ VStack(spacing: 0) {
+ Rectangle()
+ .opacity(0.001)
+ .frame (width: 3, height: incoming ?
valueTransparent : barHeight )
+ Rectangle()
+ .foregroundColor(incoming ? .green : .red)
+ .frame (width: 3, height: valueColored )
+ Rectangle()
+ .opacity(0.001)
+ .frame (width: 3, height: incoming ? barHeight :
valueTransparent)
+ }
+ }
+ }
+// if count < 8 {
+// ForEach(count...8, id: \.self) {_ in
+// Rectangle()
+// .opacity(0.001)
+// .frame (width: 3, height: barHeight * 2 )
+// }
+// }
+#if DEBUG
+// Text("last")
+#endif
+ }
+ .accessibilityHidden(true) // cannot speak out this bar chart info
+// .flippedDirection() // draw first array item on trailing edge
+ }
+}
+
+
+
+#if false
+#Preview {
+ var sampleBars: [BarData] {
+ var tempBars = [BarData]()
+
+ for _ in 1...8 {
+ let rand = Double.random(in: -100.0...100.0)
+
+ let bar = BarData(value: rand)
+ tempBars.append(bar)
+ }
+ return tempBars
+ }
+
+ return BarGraph(bars: sampleBars)
+}
+#endif
diff --git a/TalerWallet1/Views/HelperViews/Buttons.swift
b/TalerWallet1/Views/HelperViews/Buttons.swift
index a1e80b4..650ad36 100644
--- a/TalerWallet1/Views/HelperViews/Buttons.swift
+++ b/TalerWallet1/Views/HelperViews/Buttons.swift
@@ -18,77 +18,76 @@ extension ShapeStyle where Self == Color {
}
struct HamburgerButton : View {
- var font: Font?
let action: () -> Void
var body: some View {
Button(action: action) {
Image(systemName: "line.3.horizontal")
}
- .font(font ?? .title)
+ .accessibilityFont(.title)
+ .accessibilityLabel("Main Menu")
}
}
struct QRButton : View {
- var font: Font?
let action: () -> Void
var body: some View {
Button(action: action) {
Image(systemName: "qrcode.viewfinder")
}
- .font(font ?? .title)
+ .accessibilityFont(.title)
.accessibilityLabel("Scan QR codes")
}
}
struct PlusButton : View {
- var font: Font?
let action: () -> Void
var body: some View {
Button(action: action) {
Image(systemName: "plus")
}
- .font(font ?? .title)
+ .accessibilityFont(.title)
+ .accessibilityLabel("Add...")
}
}
struct ArrowUpButton : View {
- var font: Font?
let action: () -> Void
var body: some View {
Button(action: action) {
Image(systemName: "arrow.up.to.line")
}
- .font(font ?? .title3)
+ .accessibilityFont(.title2)
+ .accessibilityLabel("Scroll up")
}
}
struct ArrowDownButton : View {
- var font: Font?
let action: () -> Void
var body: some View {
Button(action: action) {
Image(systemName: "arrow.down.to.line")
}
- .font(font ?? .title3)
+ .accessibilityFont(.title2)
+ .accessibilityLabel("Scroll down")
}
}
struct ReloadButton : View {
let disabled: Bool
- var font: Font?
let action: () -> Void
var body: some View {
Button(action: action) {
Image(systemName: "arrow.clockwise")
}
- .font(font ?? .title)
- .disabled(disabled)
+ .accessibilityFont(.title)
+ .accessibilityLabel("Reload")
+ .disabled(disabled)
}
}
@@ -172,8 +171,8 @@ struct TalerButtonStyle: ButtonStyle {
: Alignment.trailing
configuration.label
.multilineTextAlignment(aligned)
- .font(.title3)
-// .font(narrow ? .title3 : .title2)
+ .accessibilityFont(.title3)
+// narrow ? .title3 : .title2
.frame(minWidth: 0, maxWidth: narrow ? nil : .infinity,
alignment: aligned2)
.padding(.vertical, 10)
.padding(.horizontal, 6)
diff --git a/TalerWallet1/Views/HelperViews/CopyShare.swift
b/TalerWallet1/Views/HelperViews/CopyShare.swift
index b3de098..c3825fd 100644
--- a/TalerWallet1/Views/HelperViews/CopyShare.swift
+++ b/TalerWallet1/Views/HelperViews/CopyShare.swift
@@ -32,10 +32,12 @@ struct CopyButton: View {
}
}
}
+ .accessibilityFont(.body)
.disabled(!isEnabled)
}
}
// MARK: -
+@MainActor
struct ShareButton: View {
private let symLog = SymLogV(0)
@Environment(\.isEnabled) private var isEnabled: Bool
@@ -54,6 +56,7 @@ struct ShareButton: View {
Text("Share")
}
}
+ .accessibilityFont(.body)
.disabled(!isEnabled)
}
}
diff --git a/TalerWallet1/Views/HelperViews/CurrencyField.swift
b/TalerWallet1/Views/HelperViews/CurrencyField.swift
index 91cb70a..2072b02 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyField.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyField.swift
@@ -22,6 +22,7 @@
import SwiftUI
import UIKit
+@MainActor
public struct CurrencyField: View {
@Binding var value: UInt64
var currency: String
@@ -88,6 +89,7 @@ class NoCaretTextField: UITextField {
}
}
+@MainActor
struct CurrencyInputField: UIViewRepresentable {
@Binding var value: UInt64
var formatter: NumberFormatter
@@ -97,11 +99,11 @@ struct CurrencyInputField: UIViewRepresentable {
Coordinator(self)
}
- public func becomeFirstResponder() -> Void {
+ @MainActor public func becomeFirstResponder() -> Void {
textField.becomeFirstResponder()
}
- public func resignFirstResponder() -> Void {
+ @MainActor public func resignFirstResponder() -> Void {
textField.resignFirstResponder()
}
diff --git a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
index e4405b3..f89a702 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
@@ -16,12 +16,12 @@ struct CurrencyInputView: View {
VStack (alignment: .leading) {
Text(title)
// .padding(.top)
- .font(.title3)
+ .accessibilityFont(.title3)
currencyField
.frame(maxWidth: .infinity, alignment: .trailing)
.foregroundColor(WalletColors().fieldForeground) // text
color
.background(WalletColors().fieldBackground)
- .font(.title2)
+ .accessibilityFont(.title2)
.textFieldStyle(.roundedBorder)
}.onAppear { // make CurrencyField show the keyboard after 0.4
seconds
if hasBeenShown {
diff --git a/TalerWallet1/Views/HelperViews/LaunchAnimationView.swift
b/TalerWallet1/Views/HelperViews/LaunchAnimationView.swift
index 761bf73..de4712d 100644
--- a/TalerWallet1/Views/HelperViews/LaunchAnimationView.swift
+++ b/TalerWallet1/Views/HelperViews/LaunchAnimationView.swift
@@ -9,7 +9,7 @@ struct LaunchAnimationView: View {
var body: some View {
ZStack {
Color(.systemGray6).ignoresSafeArea()
- RotatingTaler(size: (350 < UIScreen.main.bounds.width) ? 200 :
250, rotationEnabled: $rotationEnabled)
+ RotatingTaler(size: (350 < UIScreen.screenWidth) ? 200 : 250,
rotationEnabled: $rotationEnabled)
}
}
}
diff --git a/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift
b/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift
index 091a1ab..435e4ea 100644
--- a/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift
+++ b/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift
@@ -17,10 +17,11 @@ struct QRCodeDetailView: View {
VStack (alignment: .leading) {
Text("Either copy and send this link:")
.multilineTextAlignment(.leading)
- .font(.title3)
+ .accessibilityFont(.title3)
.padding(.vertical)
Text(talerURI)
+ .accessibilityFont(.title3)
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true) //
wrap in scrollview
.padding(.bottom)
@@ -40,7 +41,7 @@ struct QRCodeDetailView: View {
Text(amountStr)
.fixedSize(horizontal: false, vertical: true) //
wrap in scrollview
// .padding(.top, 30)
- .font(.title3)
+ .accessibilityFont(.title3)
HStack {
Spacer()
QRGeneratorView(text: talerURI)
diff --git a/TalerWallet1/Views/HelperViews/TextFieldAlert.swift
b/TalerWallet1/Views/HelperViews/TextFieldAlert.swift
index e307ff1..f665620 100644
--- a/TalerWallet1/Views/HelperViews/TextFieldAlert.swift
+++ b/TalerWallet1/Views/HelperViews/TextFieldAlert.swift
@@ -17,7 +17,9 @@ struct TextFieldAlert: ViewModifier {
.disabled(isPresented)
if isPresented {
VStack {
- Text(title).font(.headline).padding()
+ Text(title)
+ .accessibilityFont(.headline)
+ .padding()
TextField(placeholder, text:
$text).textFieldStyle(.roundedBorder).padding()
Divider()
HStack {
@@ -34,6 +36,7 @@ struct TextFieldAlert: ViewModifier {
action(text)
withAnimation { isPresented.toggle() }
}
+// .accessibilityFont(.talerBody) TODO: check
Spacer()
}
}
diff --git a/TalerWallet1/Views/HelperViews/ToSButtonView.swift
b/TalerWallet1/Views/HelperViews/ToSButtonView.swift
new file mode 100644
index 0000000..9799c5d
--- /dev/null
+++ b/TalerWallet1/Views/HelperViews/ToSButtonView.swift
@@ -0,0 +1,35 @@
+//
+// ToSButtonView.swift
+// TalerWallet
+//
+// Created by Marc Stibane on 2023-09-05.
+// Copyright © 2023 Taler. All rights reserved.
+//
+
+import SwiftUI
+
+struct ToSButtonView: View {
+ let exchangeBaseUrl: String?
+ let viewID: Int // either VIEW_WITHDRAW_TOS or SHEET_WITHDRAW_TOS
+ let p2p: Bool
+
+ var body: some View {
+ let hint = p2p ? String(localized: "You must accept the Exchange's
Terms of Service first before you can receive electronic cash in your wallet.",
comment: "P2P Receive")
+ : String(localized: "You must accept the Exchange's
Terms of Service first before you can use it to withdraw electronic cash to
your wallet.")
+ Text(hint)
+ .accessibilityFont(.body)
+ .multilineTextAlignment(.leading)
+ .padding()
+ NavigationLink(destination: LazyView {
+ WithdrawTOSView(exchangeBaseUrl: exchangeBaseUrl,
+ viewID: viewID,
+ acceptAction: nil) // pop back to here
+ }) {
+ Text("Terms of Service") // VIEW_WITHDRAW_TOS
+ }.buttonStyle(TalerButtonStyle(type: .prominent))
+ }
+}
+
+#Preview {
+ ToSButtonView(exchangeBaseUrl: nil, viewID: 0, p2p: false)
+}
diff --git a/TalerWallet1/Views/HelperViews/TransactionButton.swift
b/TalerWallet1/Views/HelperViews/TransactionButton.swift
index 4c17030..0ac465e 100644
--- a/TalerWallet1/Views/HelperViews/TransactionButton.swift
+++ b/TalerWallet1/Views/HelperViews/TransactionButton.swift
@@ -19,7 +19,7 @@ struct TransactionButton: View {
: isDestructive ? .destructive
: nil
Button(role: role, action: {
- Task {
+ Task { // runs on MainActor
disabled = true // don't try this more than once
do {
try await action(transactionId)
@@ -69,7 +69,7 @@ struct TransactionButton: View {
}
}
}
- .font(.title)
+ .accessibilityFont(.title2)
.frame(maxWidth: .infinity)
})
.buttonStyle(.bordered)
diff --git a/TalerWallet1/Views/Main/MainView.swift
b/TalerWallet1/Views/Main/MainView.swift
index 70c8a23..7743cf9 100644
--- a/TalerWallet1/Views/Main/MainView.swift
+++ b/TalerWallet1/Views/Main/MainView.swift
@@ -15,11 +15,13 @@ struct LazyView<Content: View>: View {
struct MainView: View {
private let symLog = SymLogV(0)
+ let stack: CallStack
@EnvironmentObject private var viewState: ViewState //
popToRootView()
@EnvironmentObject private var controller: Controller
@State private var sheetPresented = false
@State private var urlToOpen: URL? = nil
@Binding var soundPlayed: Bool
+ @AppStorage("talerFont") var talerFont: Int = 0 // extension
mustn't define this, so it must be here
func sheetDismissed() -> Void {
symLog.log("sheet dismiss")
@@ -31,7 +33,7 @@ struct MainView: View {
#endif
Group {
if controller.backendState == .ready {
- Content(symLog: symLog)
+ Content(symLog: symLog, stack: stack.push(), talerFont:
$talerFont)
// any change to rootViewId triggers popToRootView behaviour
.id(viewState.rootViewId)
.onAppear() {
@@ -58,7 +60,7 @@ struct MainView: View {
urlToOpen = url // raise sheet
}
.sheet(item: $urlToOpen, onDismiss: sheetDismissed) { url in
- let sheet = AnyView(URLSheet(urlToOpen: url))
+ let sheet = AnyView(URLSheet(stack: stack.push(), urlToOpen: url))
Sheet(sheetView: sheet)
}
} // body
@@ -67,10 +69,13 @@ struct MainView: View {
extension MainView {
struct Content: View {
let symLog: SymLogV?
-
+ let stack: CallStack
+ @Binding var talerFont: Int
@State var sidebarVisible: Bool = false
func hamburgerAction() {
- sidebarVisible = !sidebarVisible
+ withAnimation(.easeInOut(duration: 0.25)) {
+ sidebarVisible = !sidebarVisible
+ }
}
let balances = String(localized: "Balances")
@@ -79,13 +84,15 @@ extension MainView {
var views: [SidebarItem] {[
SidebarItem(name: balances,
sysImage: "creditcard.fill", // TODO: Wallet Icon
- view: AnyView(BalancesListView(navTitle: balances,
- hamburgerAction:
hamburgerAction)
+ view: AnyView(BalancesListView(stack:
stack.push(balances),
+ navTitle: balances,
+ hamburgerAction: hamburgerAction)
)),
SidebarItem(name: exchanges,
sysImage: "building.columns",
- view: AnyView(ExchangeListView(navTitle: exchanges,
- hamburgerAction:
hamburgerAction)
+ view: AnyView(ExchangeListView(stack:
stack.push(exchanges),
+ navTitle: exchanges,
+ hamburgerAction: hamburgerAction)
)),
SidebarItem(name: settings, // TODO: "About"?
sysImage: "gearshape.fill",
@@ -107,13 +114,25 @@ extension MainView {
.id(views[currentView].name)
.frame(maxWidth: .infinity, maxHeight: .infinity,
alignment: .center)
.transition(.backslide)
- }
+ } .id(talerFont)
.navigationBarTitleDisplayMode(.automatic)
- }.navigationViewStyle(.stack)
- // The side view is on top of the current view
- SideBarView(views: views,
+ .background(NavigationBarBuilder {
navigationController in
+ //
navigationController.navigationBar.barTintColor = .red
+
navigationController.navigationBar.titleTextAttributes =
+ [.font: TalerFont.talerFont(talerFont, size:
24, relativeTo: .title2)]
+
navigationController.navigationBar.largeTitleTextAttributes =
+ [.font: TalerFont.talerFont(talerFont, size:
38, relativeTo: .largeTitle)]
+ })
+ }
+ .navigationViewStyle(.stack)
+ .talerNavBar(talerFont: talerFont)
+
+ // The side view is above (Z-Axis) the current view
+ SideBarView(stack: stack.push(),
+ views: views,
currentView: $currentView,
- sidebarVisible: $sidebarVisible)
+ sidebarVisible: sidebarVisible,
+ hamburgerAction: hamburgerAction)
}
}
} // Content
diff --git a/TalerWallet1/Views/Main/SideBarView.swift
b/TalerWallet1/Views/Main/SideBarView.swift
index 31a00d9..9072542 100644
--- a/TalerWallet1/Views/Main/SideBarView.swift
+++ b/TalerWallet1/Views/Main/SideBarView.swift
@@ -15,9 +15,11 @@ struct SidebarItem {
struct SideBarView: View {
private let symLog = SymLogV(0)
+ let stack: CallStack
let views: [SidebarItem]
@Binding var currentView: Int
- @Binding var sidebarVisible: Bool
+ let sidebarVisible: Bool
+ let hamburgerAction: () -> Void
@State private var rotationEnabled = false
var body: some View {
@@ -26,7 +28,7 @@ struct SideBarView: View {
VStack(spacing: 10) {
let gnuTaler = String("GNU Taler") // this should NOT be
translated
Link(gnuTaler, destination:
URL(string:"https://taler.net")!)
- .font(.largeTitle) //.bold() iOS 16
+ .accessibilityFont(.largeTitle)
.padding(.top, 30)
RotatingTaler(size: 100, rotationEnabled: $rotationEnabled)
.onTapGesture {
@@ -35,8 +37,8 @@ struct SideBarView: View {
ForEach(0..<views.count, id: \.self) { i in
Button {
symLog.log("sidebar item \"\(views[i].name)\"
selected")
- sidebarVisible = false // slide sidebar to
the left
- withAnimation(.easeInOut) {currentView = i}
// switch to the view the user selected
+ hamburgerAction() // slide sidebar to the left
+ withAnimation(.easeInOut(duration: 0.3))
{currentView = i} // animate to the view the user selected
} label: {
if let sysImage = views[i].sysImage {
Label(views[i].name, systemImage: sysImage)
@@ -48,7 +50,7 @@ struct SideBarView: View {
}
.padding()
.buttonStyle(.borderless)
- .font(.title2)
+ .accessibilityFont(.title2)
.disabled(i == currentView)
.accessibilityHidden(i == currentView) // don't
suggest the current item
}
@@ -68,11 +70,9 @@ struct SideBarView: View {
.offset(x: sidebarVisible ? sidebarWidth : 0)
.contentShape(Rectangle())
.onTapGesture {
- sidebarVisible = false
+ hamburgerAction() // slide sidebar to the left
}
}
- .animation(.linear //(duration: SLIDEDURATION)
- , value: sidebarVisible)
}
}
// MARK: -
@@ -86,7 +86,9 @@ fileprivate struct BindingViewContainer : View {
ZStack(alignment: .leading) {
views[currentView].view
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment:
.center)
- SideBarView(views: views, currentView: $currentView,
sidebarVisible: $sidebarVisible)
+ SideBarView(stack: CallStack("Preview"), views: views,
currentView: $currentView,
+ sidebarVisible: sidebarVisible,
+ hamburgerAction: { sidebarVisible = !sidebarVisible })
}
}
}
diff --git a/TalerWallet1/Views/Main/WalletEmptyView.swift
b/TalerWallet1/Views/Main/WalletEmptyView.swift
index 300248b..e8b824e 100644
--- a/TalerWallet1/Views/Main/WalletEmptyView.swift
+++ b/TalerWallet1/Views/Main/WalletEmptyView.swift
@@ -28,7 +28,7 @@ struct WalletEmptyView: View {
}
}
.listStyle(myListStyle.style).anyView
- .font(.title2)
+ .accessibilityFont(.title2)
.background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
.onAppear() {
DebugViewC.shared.setViewID(VIEW_EMPTY) // 10
diff --git a/TalerWallet1/Views/Payment/PayTemplateView.swift
b/TalerWallet1/Views/Payment/PayTemplateView.swift
index 526d0b0..ea6c73a 100644
--- a/TalerWallet1/Views/Payment/PayTemplateView.swift
+++ b/TalerWallet1/Views/Payment/PayTemplateView.swift
@@ -10,6 +10,7 @@ import SymLog
// both from the shop's website. We show the payment details
struct PayTemplateView: View {
private let symLog = SymLogV()
+ let stack: CallStack
let navTitle = String(localized: "Confirm Payment", comment:"pay merchant")
@EnvironmentObject private var controller: Controller
@@ -21,7 +22,7 @@ struct PayTemplateView: View {
@EnvironmentObject private var model: WalletModel
func acceptAction(preparePayResult: PreparePayResult) {
- Task {
+ Task { // runs on MainActor
do {
let confirmPayResult = try await
model.confirmPayM(preparePayResult.transactionId)
// symLog.log(confirmPayResult as Any)
@@ -67,6 +68,7 @@ struct PayTemplateView: View {
// TODO: payment: popup with all possible exchanges, check
fees
} else if let balanceDetails = preparePayResult.balanceDetails
{ // Insufficient
Text("You don't have enough \(currency)")
+ .accessibilityFont(.body)
ThreeAmountsView(topTitle: topTitle,
topAmount: raw, fee: nil,
bottomTitle: String(localized:
"\(currency) available:"),
@@ -76,6 +78,7 @@ struct PayTemplateView: View {
} else {
// TODO: Error - neither effective nor balanceDetails
Text("Error")
+ .accessibilityFont(.body)
}
}
.listStyle(myListStyle.style).anyView
diff --git a/TalerWallet1/Views/Payment/PaymentView.swift
b/TalerWallet1/Views/Payment/PaymentView.swift
index 6b6bc0f..b50d970 100644
--- a/TalerWallet1/Views/Payment/PaymentView.swift
+++ b/TalerWallet1/Views/Payment/PaymentView.swift
@@ -9,7 +9,8 @@ import SymLog
// Will be called either by the user scanning a QR code or tapping the
provided link,
// both from the shop's website. We show the payment details
struct PaymentView: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let navTitle = String(localized: "Confirm Payment", comment:"pay merchant")
@EnvironmentObject private var controller: Controller
@@ -21,7 +22,7 @@ struct PaymentView: View {
@EnvironmentObject private var model: WalletModel
func acceptAction(preparePayResult: PreparePayResult) {
- Task {
+ Task { // runs on MainActor
do {
let confirmPayResult = try await
model.confirmPayM(preparePayResult.transactionId)
// symLog.log(confirmPayResult as Any)
@@ -60,6 +61,7 @@ struct PaymentView: View {
// TODO: payment: popup with all possible exchanges, check
fees
} else if let balanceDetails = preparePayResult.balanceDetails
{ // Insufficient
Text("You don't have enough \(currency)")
+ .accessibilityFont(.body)
ThreeAmountsView(topTitle: topTitle,
topAmount: raw, fee: nil,
bottomTitle: String(localized:
"\(currency) available:"),
@@ -69,6 +71,7 @@ struct PaymentView: View {
} else {
// TODO: Error - neither effective nor balanceDetails
Text("Error")
+ .accessibilityFont(.body)
}
}
.listStyle(myListStyle.style).anyView
@@ -152,6 +155,6 @@ struct PaymentURIView_Previews: PreviewProvider {
)
let url = URL(string: "taler://pay/some_amount")!
- PaymentView(url: url, preparePayResult: details)
+ PaymentView(stack: CallStack("Preview"), url: url, preparePayResult:
details)
}
}
diff --git a/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift
b/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift
index 766aa6d..577ecc5 100644
--- a/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift
+++ b/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift
@@ -7,7 +7,8 @@ import taler_swift
import SymLog
struct PaymentPurpose: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let scopeInfo: ScopeInfo
let centsToTransfer: UInt64
@@ -19,7 +20,7 @@ struct PaymentPurpose: View {
@FocusState private var isFocused: Bool
let formatter = CurrencyFormatter.shared // TODO: based on currency
- let buttonFont: Font = .title2
+// let buttonFont: Font = .talerTitle2
private var label: String {
let mag = pow(10, formatter.maximumFractionDigits)
@@ -36,10 +37,10 @@ struct PaymentPurpose: View {
VStack(alignment: .leading, spacing: 6) {
Text("Purpose:")
.padding(.top)
- .font(.title3)
+ .accessibilityFont(.title3)
TextField("Purpose", text: $summary)
- .font(.title)
+ .accessibilityFont(.title)
.foregroundColor(WalletColors().fieldForeground) //
text color
.background(WalletColors().fieldBackground)
.textFieldStyle(.roundedBorder)
@@ -56,7 +57,7 @@ struct PaymentPurpose: View {
} // maximum 100 characters
Text("Expires in:")
- .font(.title3)
+ .accessibilityFont(.title3)
SelectDays(selected: $expireDays, maxExpiration: THIRTYDAYS)
.disabled(false)
@@ -65,12 +66,14 @@ struct PaymentPurpose: View {
let disabled = (expireDays == 0) || (summary.count < 1)
NavigationLink(destination: LazyView {
- SendDone(amountToSend: nil,
- amountToReceive: amount,
- summary: summary, expireDays: expireDays)
+ SendDone(stack: stack.push(),
+ amountToSend: nil,
+ amountToReceive: amount,
+ summary: summary,
+ expireDays: expireDays)
}) {
Text("Request \(label) \(scopeInfo.currency)")
- .font(buttonFont)
+// .accessibilityFont(buttonFont)
}
.buttonStyle(TalerButtonStyle(type: .prominent))
.disabled(disabled)
diff --git a/TalerWallet1/Views/Peer2peer/RequestPayment.swift
b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
index 76da5d9..3460039 100644
--- a/TalerWallet1/Views/Peer2peer/RequestPayment.swift
+++ b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
@@ -8,7 +8,8 @@ import SymLog
// Called when tapping "Request Payment" in the balances list
struct RequestPayment: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
var scopeInfo: ScopeInfo
@Binding var centsToTransfer: UInt64
@@ -40,11 +41,12 @@ struct RequestPayment: View {
let disabled = (centsToTransfer == 0) || someCoins.invalid ||
someCoins.tooMany
NavigationLink(destination: LazyView {
- PaymentPurpose(scopeInfo: scopeInfo,
- centsToTransfer: centsToTransfer,
- fee: someCoins.fee,
- summary: $summary,
- expireDays: $expireDays)
+ PaymentPurpose(stack: stack.push(),
+ scopeInfo: scopeInfo,
+ centsToTransfer: centsToTransfer,
+ fee: someCoins.fee,
+ summary: $summary,
+ expireDays: $expireDays)
// { deactivateAction() }
}) {
let amount = Amount.amountFromCents(currency,
centsToTransfer)
diff --git a/TalerWallet1/Views/Peer2peer/SendAmount.swift
b/TalerWallet1/Views/Peer2peer/SendAmount.swift
index 52c1367..a7475cd 100644
--- a/TalerWallet1/Views/Peer2peer/SendAmount.swift
+++ b/TalerWallet1/Views/Peer2peer/SendAmount.swift
@@ -8,7 +8,8 @@ import SymLog
// Called when tapping "Send Coins" in the balances list
struct SendAmount: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let amountAvailable: Amount // TODO: GetMaxPeerPushAmount
@Binding var centsToTransfer: UInt64
@@ -44,22 +45,24 @@ struct SendAmount: View {
VStack(alignment: .trailing) {
let available = amountAvailable.readableDescription
Text("Available: \(available)")
- .font(.title3)
+ .accessibilityFont(.title3)
.padding(.bottom, 2)
CurrencyInputView(currencyField: currencyField,
title: String(localized: "Amount to send:"))
Text("+ \(fee) payment fee")
+ .accessibilityFont(.body)
.foregroundColor(.red)
.padding(4)
let disabled = centsToTransfer == 0 // TODO: check
amountAvailable
NavigationLink(destination: LazyView {
- SendPurpose(amountAvailable: amountAvailable,
- centsToTransfer: centsToTransfer,
- fee: fee,
- summary: $summary,
- expireDays: $expireDays)
+ SendPurpose(stack: stack.push(),
+ amountAvailable: amountAvailable,
+ centsToTransfer: centsToTransfer,
+ fee: fee,
+ summary: $summary,
+ expireDays: $expireDays)
}) {
Text("Next")
} .buttonStyle(TalerButtonStyle(type: .prominent))
@@ -102,9 +105,10 @@ struct SendAmount_Container : View {
var body: some View {
let amount = Amount(currency: LONGCURRENCY, integer: 10, fraction: 0)
- SendAmount(amountAvailable: amount,
- centsToTransfer: $centsToTransfer,
- summary: $summary)
+ SendAmount(stack: CallStack("Preview"),
+ amountAvailable: amount,
+ centsToTransfer: $centsToTransfer,
+ summary: $summary)
}
}
diff --git a/TalerWallet1/Views/Peer2peer/SendDone.swift
b/TalerWallet1/Views/Peer2peer/SendDone.swift
index 0830374..5cfe04d 100644
--- a/TalerWallet1/Views/Peer2peer/SendDone.swift
+++ b/TalerWallet1/Views/Peer2peer/SendDone.swift
@@ -8,7 +8,8 @@ import SymLog
// Called when initiating a P2P transaction: Send coins or Send
Request(Invoice)
struct SendDone: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let navTitle = String(localized: "P2P Ready")
#if DEBUG
@AppStorage("developerMode") var developerMode: Bool = true
@@ -37,15 +38,16 @@ struct SendDone: View {
#endif
VStack {
if let transactionId {
- TransactionDetailView(transactionId: transactionId,
- reloadAction: reloadOneAction,
- navTitle: navTitle,
- doneAction:
ViewState.shared.popToRootView,
- abortAction: nil,
- deleteAction: nil,
- failAction: nil,
- suspendAction: nil,
- resumeAction: nil)
+ TransactionDetailView(stack: stack.push(),
+ transactionId: transactionId,
+ reloadAction: reloadOneAction,
+ navTitle: navTitle,
+ doneAction: ViewState.shared.popToRootView,
+ abortAction: nil,
+ deleteAction: nil,
+ failAction: nil,
+ suspendAction: nil,
+ resumeAction: nil)
.navigationBarBackButtonHidden(true)
.interactiveDismissDisabled() // can only use "Done"
button to dismiss
.navigationTitle(navTitle)
@@ -86,10 +88,11 @@ struct SendDone: View {
struct SendNow_Previews: PreviewProvider {
static var previews: some View {
Group {
- SendDone(amountToSend: try! Amount(fromString: LONGCURRENCY +
":4.8"),
- amountToReceive: nil,
- summary: "some purpose",
- expireDays: 0)
+ SendDone(stack: CallStack("Preview"),
+ amountToSend: try! Amount(fromString: LONGCURRENCY + ":4.8"),
+ amountToReceive: nil,
+ summary: "some purpose",
+ expireDays: 0)
}
}
}
diff --git a/TalerWallet1/Views/Peer2peer/SendPurpose.swift
b/TalerWallet1/Views/Peer2peer/SendPurpose.swift
index 49086df..3117450 100644
--- a/TalerWallet1/Views/Peer2peer/SendPurpose.swift
+++ b/TalerWallet1/Views/Peer2peer/SendPurpose.swift
@@ -7,7 +7,8 @@ import taler_swift
import SymLog
struct SendPurpose: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
@FocusState private var isFocused: Bool
let amountAvailable: Amount
@@ -19,8 +20,7 @@ struct SendPurpose: View {
let formatter = CurrencyFormatter.shared // TODO: based on currency
- let buttonFont: Font = .title2
- private var label: String {
+ private var value: String {
let mag = pow(10, formatter.maximumFractionDigits)
return formatter.string(for: Decimal(centsToTransfer) / mag) ?? ""
}
@@ -31,15 +31,16 @@ struct SendPurpose: View {
VStack (spacing: 6) {
Text(amount.readableDescription)
Text("+ \(fee) payment fee")
+ .accessibilityFont(.body)
.foregroundColor(.red)
VStack(alignment: .leading, spacing: 6) {
Text("Purpose:")
+ .accessibilityFont(.title2)
.padding(.top)
- .font(.title2)
if #available(iOS 16.0, *) {
TextField("Purpose", text: $summary, axis: .vertical)
- .font(.title2)
+ .accessibilityFont(.title2)
.lineLimit(2...)
.foregroundColor(WalletColors().fieldForeground)
// text color
.background(WalletColors().fieldBackground)
@@ -52,7 +53,7 @@ struct SendPurpose: View {
}
} else {
TextField("Purpose", text: $summary)
- .font(.title)
+ .accessibilityFont(.title)
// .lineLimit(2...5) // lineLimit' is only available
in iOS 16.0 or newer
.foregroundColor(WalletColors().fieldForeground)
// text color
.background(WalletColors().fieldBackground)
@@ -68,10 +69,11 @@ struct SendPurpose: View {
HStack{
Spacer()
Text(verbatim: "\(summary.count)/100")
+ .accessibilityFont(.body)
} // maximum 100 characters
Text("Expires in:")
- .font(.title3)
+ .accessibilityFont(.title3)
// TODO: compute max Expiration day from peerPushCheck to
disable 30 (and even 7)
SelectDays(selected: $expireDays, maxExpiration: THIRTYDAYS)
@@ -81,12 +83,13 @@ struct SendPurpose: View {
let disabled = (expireDays == 0) || (summary.count < 1) //
TODO: check amountAvailable
NavigationLink(destination: LazyView {
- SendDone(amountToSend: amount,
- amountToReceive: nil,
- summary: summary, expireDays: expireDays)
+ SendDone(stack: stack.push(),
+ amountToSend: amount,
+ amountToReceive: nil,
+ summary: summary,
+ expireDays: expireDays)
}) {
- Text("Send \(label) \(amountAvailable.currencyStr) now",
comment: "first is value, second currencyString")
- .font(buttonFont)
+ Text("Send \(value) \(amountAvailable.currencyStr) now",
comment: "first is value, second currencyString") // TODO: currency formatter
}
.buttonStyle(TalerButtonStyle(type: .prominent))
.disabled(disabled)
diff --git a/TalerWallet1/Views/Settings/Pending/PendingOpView.swift
b/TalerWallet1/Views/Settings/Pending/PendingOpView.swift
index cd68214..c3ceb29 100644
--- a/TalerWallet1/Views/Settings/Pending/PendingOpView.swift
+++ b/TalerWallet1/Views/Settings/Pending/PendingOpView.swift
@@ -13,25 +13,28 @@ struct PendingOpView: View {
var body: some View {
Section {
- if let baseURL = pendingOp.exchangeBaseUrl {
- Text(baseURL)
+ 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 = TalerDater.dateString(from:
pendingOp.timestampDue)
+ Text(dateString)
}
- Text(pendingOp.id)
- .font(.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 = TalerDater.dateString(from:
pendingOp.timestampDue)
- Text(dateString)
+ .accessibilityFont(.body)
} header: {
Text(pendingOp.type)
- .font(.title2)
+ .accessibilityFont(.title2)
}
// .textCase(nil) // don't capitalize
.onAppear {
diff --git a/TalerWallet1/Views/Settings/SettingsItem.swift
b/TalerWallet1/Views/Settings/SettingsItem.swift
index 919ea48..e49194c 100644
--- a/TalerWallet1/Views/Settings/SettingsItem.swift
+++ b/TalerWallet1/Views/Settings/SettingsItem.swift
@@ -20,15 +20,16 @@ struct SettingsItem<Content: View>: View {
VStack {
Text(name)
.frame(maxWidth: .infinity, alignment: .leading)
- .font(.talerTitle2)
+ .accessibilityFont(.title2)
.padding([.bottom], 0.01)
if let desc = description {
Text(desc)
.frame(maxWidth: .infinity, alignment: .leading)
- .font(.talerCaption)
+ .accessibilityFont(.caption)
}
}
content()
+ .accessibilityFont(.body)
}.padding([.bottom], 4)
}
}
@@ -42,7 +43,7 @@ struct SettingsToggle: View {
var body: some View {
VStack {
Toggle(name, isOn: $value.animation())
- .font(.talerTitle2)
+ .accessibilityFont(.title2)
.onChange(of: value) { value in
action()
}
@@ -50,7 +51,7 @@ struct SettingsToggle: View {
if let desc = description {
Text(desc)
.frame(maxWidth: .infinity, alignment: .leading)
- .font(.talerCaption)
+ .accessibilityFont(.caption)
}
}.padding([.bottom], 4)
}
@@ -62,7 +63,7 @@ struct SettingsFont: View {
let action: (Int) -> Void
@State private var selected = 0
- let fonts = [String(localized: "Standard iOS Font"),
"Atkinson-Hyperlegible", "Nunito"]
+ let fonts = [String(localized: "Standard iOS Font"),
"Atkinson-Hyperlegible", "Nunito", "Nunito Italic"]
var body: some View {
Picker(title, selection: $selected, content: {
@@ -70,7 +71,7 @@ struct SettingsFont: View {
Text(fonts[index]).tag(index)
})
})
- .font(.talerTitle2)
+ .accessibilityFont(.title2)
.pickerStyle(.menu)
.onAppear() {
withAnimation { selected = value }
@@ -88,12 +89,12 @@ struct SettingsStyle: View {
var body: some View {
HStack {
Text(title)
- .font(.talerTitle2)
+ .accessibilityFont(.title2)
Spacer()
Picker(selection: $myListStyle) {
ForEach(MyListStyle.allCases, id: \.self) {
Text($0.displayName.capitalized).tag($0)
- .font(.talerTitle2)
+ .accessibilityFont(.title2)
}
} label: {}
.pickerStyle(.menu)
@@ -119,12 +120,12 @@ struct SettingsSpeaker: View {
let image = imageName(value)
HStack {
Text(name)
- .font(.talerTitle2)
+ .accessibilityFont(.title2)
Text(" ")
- .font(.talerLargeTitle)
+ .accessibilityFont(.largeTitle)
Spacer()
Image(systemName: image.0)
- .font(.talerLargeTitle)
+ .accessibilityFont(.largeTitle)
.accessibilityLabel(image.1)
.onTapGesture {
if value > 0 {
@@ -143,7 +144,7 @@ struct SettingsSpeaker: View {
if let desc = description {
Text(desc)
.frame(maxWidth: .infinity, alignment: .leading)
- .font(.talerCaption)
+ .accessibilityFont(.caption)
}
}.padding([.bottom], 4)
}
diff --git a/TalerWallet1/Views/Settings/SettingsView.swift
b/TalerWallet1/Views/Settings/SettingsView.swift
index 6394d07..cf2734e 100644
--- a/TalerWallet1/Views/Settings/SettingsView.swift
+++ b/TalerWallet1/Views/Settings/SettingsView.swift
@@ -21,11 +21,13 @@ struct SettingsView: View {
private let symLog = SymLogV(0)
let navTitle: String
+ @EnvironmentObject private var controller: Controller
#if DEBUG
@AppStorage("developerMode") var developerMode: Bool = true
#else
@AppStorage("developerMode") var developerMode: Bool = false
#endif
+ @AppStorage("useHaptics") var useHaptics: Bool = false
@AppStorage("playSounds") var playSounds: Int = 0
@AppStorage("talerFont") var talerFont: Int = 0
@AppStorage("developDelay") var developDelay: Bool = false
@@ -55,7 +57,7 @@ struct SettingsView: View {
Button("Reset", role: .destructive) {
didReset = true
showResetAlert = false
- Task {
+ Task { // runs on MainActor
symLog.log("❗️Reset wallet-core❗️")
do {
try await model.resetWalletCoreT()
@@ -81,8 +83,12 @@ struct SettingsView: View {
let walletCore = WalletCore.shared
Group {
List {
+ if controller.hapticCapability.supportsHaptics {
+ SettingsToggle(name: String(localized: "Haptics"), value:
$useHaptics,
+ description: String(localized: "Vibration
Feedback"))
+ }
SettingsSpeaker(name: String(localized: "Play Payment
Sounds"), value: $playSounds,
- description: String(localized: "After a
transaction finished"))
+ description: String(localized: "When a
transaction finished"))
SettingsFont(title: String(localized: "Font:"), value:
talerFont, action: redraw)
SettingsStyle(title: String(localized: "Liststyle:"),
myListStyle: $myListStyle)
if diagnosticModeEnabled {
@@ -98,17 +104,15 @@ struct SettingsView: View {
SettingsItem(name: String(localized: "Pending
Operations"),
description: String(localized: "Exchange not
yet ready...")) {}
}
- SettingsToggle(name: String(localized: "Set 2 seconds
delay"), value: $developDelay,
+ SettingsToggle(name: String(localized: "Set 2 seconds
delay"),
+ value: $developDelay.onChange({ delay in
+ walletCore.developDelay = delay}),
description: String(localized: "After each
wallet-core action"))
- .onChange(of: developDelay, perform: { developDelay in
- walletCore.developDelay = developDelay
- })
-
SettingsItem(name: String(localized: "Withdraw
\(DEMOCURRENCY)"),
description: String(localized: "Get money for
testing")) {
Button("Withdraw") {
withDrawDisabled = true // don't run twice
- Task {
+ Task { // runs on MainActor
symLog.log("Withdraw KUDOS")
do {
try await model.loadTestKudosM(test:
false)
@@ -124,7 +128,7 @@ struct SettingsView: View {
description: String(localized: "Get money for
testing")) {
Button("Withdraw") {
withDrawDisabled = true // don't run twice
- Task {
+ Task { // runs on MainActor
symLog.log("Withdraw TESTKUDOS")
do {
try await model.loadTestKudosM(test:
true)
@@ -140,7 +144,7 @@ struct SettingsView: View {
description: String(localized: "Perform basic
test transactions")) {
Button("Demo 1") {
checkDisabled = true // don't run twice
- Task {
+ Task { // runs on MainActor
symLog.log("running integration test on
demo")
do {
try await
model.runIntegrationTestM(newVersion: false, test: false)
@@ -156,7 +160,7 @@ struct SettingsView: View {
description: String(localized: "Perform basic
test transactions")) {
Button("Test 1") {
checkDisabled = true // don't run twice
- Task {
+ Task { // runs on MainActor
symLog.log("running integration test on
test")
do {
try await
model.runIntegrationTestM(newVersion: false, test: true)
@@ -172,7 +176,7 @@ struct SettingsView: View {
description: String(localized: "Perform more
test transactions")) {
Button("Demo 2") {
checkDisabled = true // don't run twice
- Task {
+ Task { // runs on MainActor
symLog.log("running integration test V2 on
demo")
do {
try await
model.runIntegrationTestM(newVersion: true, test: false)
@@ -188,7 +192,7 @@ struct SettingsView: View {
description: String(localized: "Perform more
test transactions")) {
Button("Test 2") {
checkDisabled = true // don't run twice
- Task {
+ Task { // runs on MainActor
symLog.log("running integration test V2 on
test")
do {
try await
model.runIntegrationTestM(newVersion: true, test: true)
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
index b3941ea..b26a6d0 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
@@ -8,7 +8,8 @@ import SymLog
// Called when accepting a scanned P2P transaction: Receive coins or Pay
Request(Invoice)
struct P2pAcceptDone: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let transactionId: String
let incoming: Bool
@@ -29,15 +30,16 @@ struct P2pAcceptDone: View {
#endif
let navTitle = incoming ? String(localized: "Received P2P", comment:
"Title, short")
: String(localized: "Paid P2P", comment:
"Title, short")
- TransactionDetailView(transactionId: transactionId,
- reloadAction: reloadOneAction,
- navTitle: navTitle,
- doneAction: { dismissTop() },
- abortAction: nil,
- deleteAction: nil,
- failAction: nil,
- suspendAction: nil,
- resumeAction: nil)
+ TransactionDetailView(stack: stack.push(),
+ transactionId: transactionId,
+ reloadAction: reloadOneAction,
+ navTitle: navTitle,
+ doneAction: { dismissTop() },
+ abortAction: nil,
+ deleteAction: nil,
+ failAction: nil,
+ suspendAction: nil,
+ resumeAction: nil)
.navigationBarBackButtonHidden(true)
.interactiveDismissDisabled() // can only use "Done"
button to dismiss
.navigationTitle(navTitle)
@@ -64,6 +66,8 @@ struct P2pAcceptDone: View {
// MARK: -
struct P2pAcceptDone_Previews: PreviewProvider {
static var previews: some View {
- P2pAcceptDone(transactionId: "some ID", incoming: true)
+ P2pAcceptDone(stack: CallStack("Preview"),
+ transactionId: "some ID",
+ incoming: true)
}
}
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
index cdcb99a..01529fe 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
@@ -9,7 +9,8 @@ import SymLog
// Called either when scanning a QR code or tapping the provided link
// from another user's Send Request(Invoice). We show the P2P details.
struct P2pPayURIView: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let navTitle = String(localized: "Pay P2P")
// the scanned URL
@@ -39,8 +40,9 @@ struct P2pPayURIView: View {
.navigationTitle(navTitle)
NavigationLink(destination: LazyView {
- P2pAcceptDone(transactionId:
peerPullDebitResponse.transactionId,
- incoming: false)
+ P2pAcceptDone(stack: stack.push(),
+ transactionId: peerPullDebitResponse.transactionId,
+ incoming: false)
}) {
Text("Confirm Payment", comment:"pay P2P
request/invoice") // SHEET_PAY_P2P
}.buttonStyle(TalerButtonStyle(type: .prominent))
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
index d38d73b..9760a2b 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
@@ -9,7 +9,8 @@ import SymLog
// Will be called either by the user scanning a QR code or tapping the
provided link,
// from another user's SendCoins. We show the P2P details - but first the ToS
must be accepted.
struct P2pReceiveURIView: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let navTitle = String(localized: "P2P Receive")
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
@@ -40,24 +41,17 @@ struct P2pReceiveURIView: View {
let tosAccepted = true // TODO:
https://bugs.gnunet.org/view.php?id=7869
if tosAccepted {
NavigationLink(destination: LazyView {
- P2pAcceptDone(transactionId:
peerPushCreditResponse.transactionId,
- incoming: true)
+ P2pAcceptDone(stack: stack.push(),
+ transactionId:
peerPushCreditResponse.transactionId,
+ incoming: true)
}) {
Text("Accept P2P Receive") // SHEET_RCV_P2P_ACCEPT
}.buttonStyle(TalerButtonStyle(type: .prominent))
.padding()
} else {
- Text("You must accept the Terms of Service first before
you can receive electronic cash to your wallet.")
- .multilineTextAlignment(.leading)
- .padding()
- NavigationLink(destination: LazyView {
- WithdrawTOSView(exchangeBaseUrl: nil,
- viewID: SHEET_RCV_P2P_TOS,
- acceptAction: nil) // pop
back to here
- }) {
- Text("Read Terms of Service")
- }.buttonStyle(TalerButtonStyle(type: .prominent))
- .padding()
+ ToSButtonView(exchangeBaseUrl: nil,
+ viewID: SHEET_RCV_P2P_TOS,
+ p2p: true)
}
} else {
// Yikes no details or no baseURL
diff --git a/TalerWallet1/Views/Sheets/QRSheet.swift
b/TalerWallet1/Views/Sheets/QRSheet.swift
index 6385ec4..315c60b 100644
--- a/TalerWallet1/Views/Sheets/QRSheet.swift
+++ b/TalerWallet1/Views/Sheets/QRSheet.swift
@@ -8,7 +8,8 @@ import SymLog
import AVFoundation
struct QRSheet: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
@State private var scannedCode: String?
var body: some View {
@@ -19,7 +20,7 @@ struct QRSheet: View {
if let scannedURL = URL(string: scannedCode!) {
let scheme = scannedURL.scheme
if scheme == "taler" {
- URLSheet(urlToOpen: scannedURL)
+ URLSheet(stack: stack.push(), urlToOpen: scannedURL)
} else {
let _ = print(scannedURL) // TODO: logging
ErrorView(errortext: scannedURL.absoluteString)
diff --git a/TalerWallet1/Views/Sheets/ShareSheet.swift
b/TalerWallet1/Views/Sheets/ShareSheet.swift
index e56148f..72a6faa 100644
--- a/TalerWallet1/Views/Sheets/ShareSheet.swift
+++ b/TalerWallet1/Views/Sheets/ShareSheet.swift
@@ -21,7 +21,7 @@ import SymLog
public class ShareSheet: ObservableObject {
private let symLog = SymLogC()
- static func shareSheet(url: String) {
+ @MainActor static func shareSheet(url: String) {
let url = URL(string: url)
let activityView = UIActivityViewController(activityItems: [url!],
applicationActivities: nil)
diff --git a/TalerWallet1/Views/Sheets/Sheet.swift
b/TalerWallet1/Views/Sheets/Sheet.swift
index abc564b..1daa1e6 100644
--- a/TalerWallet1/Views/Sheets/Sheet.swift
+++ b/TalerWallet1/Views/Sheets/Sheet.swift
@@ -7,9 +7,10 @@ import SymLog
import os.log
struct Sheet: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
@Environment(\.dismiss) var dismiss // call dismiss() to get rid of
the sheet
@EnvironmentObject private var debugViewC: DebugViewC
+ @AppStorage("talerFont") var talerFont: Int = 0
var sheetView: AnyView
@@ -30,12 +31,14 @@ struct Sheet: View {
.navigationBarItems(leading: cancelButton)
.navigationBarTitleDisplayMode(.automatic)
.background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
- }.navigationViewStyle(.stack)
+ }
+ .navigationViewStyle(.stack)
+ .talerNavBar(talerFont: talerFont)
.overlay(alignment: .top) {
// Show the viewID on top of the sheet's NavigationView
Text(idString)
- .font(.caption2)
.foregroundColor(.purple)
+ .font(.system(size: 11)) // no accessibilityFont
.monospacedDigit()
.edgesIgnoringSafeArea(.top)
.id("sheetID")
diff --git a/TalerWallet1/Views/Sheets/URLSheet.swift
b/TalerWallet1/Views/Sheets/URLSheet.swift
index 1db6169..b9f5751 100644
--- a/TalerWallet1/Views/Sheets/URLSheet.swift
+++ b/TalerWallet1/Views/Sheets/URLSheet.swift
@@ -6,7 +6,8 @@ import SwiftUI
import SymLog
struct URLSheet: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let navTitle = String(localized: "Checking Link")
var urlToOpen: URL
@EnvironmentObject private var controller: Controller
@@ -17,21 +18,21 @@ struct URLSheet: View {
Group {
switch urlCommand {
case .withdraw:
- WithdrawURIView(url: urlToOpen)
+ WithdrawURIView(stack: stack.push(), url: urlToOpen)
case .pay:
- PaymentView(url: urlToOpen)
+ PaymentView(stack: stack.push(), url: urlToOpen)
case .payPull:
- P2pPayURIView(url: urlToOpen)
+ P2pPayURIView(stack: stack.push(), url: urlToOpen)
case .payPush:
- P2pReceiveURIView(url: urlToOpen)
+ P2pReceiveURIView(stack: stack.push(), url: urlToOpen)
case .payTemplate:
- PayTemplateView(url: urlToOpen)
+ PayTemplateView(stack: stack.push(), url: urlToOpen)
// case .reward:
// RewardURIView(url: urlToOpen)
default: // Error view
VStack {
Text("unknown command")
- .font(.title)
+ .accessibilityFont(.title)
Text(controller.messageForSheet ??
urlToOpen.absoluteString)
}
.navigationTitle(navTitle)
diff --git a/TalerWallet1/Views/Transactions/ManualDetails.swift
b/TalerWallet1/Views/Transactions/ManualDetails.swift
index 8172254..adc9e15 100644
--- a/TalerWallet1/Views/Transactions/ManualDetails.swift
+++ b/TalerWallet1/Views/Transactions/ManualDetails.swift
@@ -14,50 +14,53 @@ struct ManualDetails: View {
let payURL = URL(string: payto)
let iban = payURL?.iban ?? "unknown IBAN"
let amount = common.amountRaw.readableDescription
- Text("You need to transfer \(amount) from your regular bank
account to the Exchange.")
- Text("Step 1: Copy this code and paste it into the subject/purpose
field in your banking app or bank website.\nThis is mandatory, otherwise your
money will not arrive in this wallet.")
- .multilineTextAlignment(.leading)
- .listRowSeparator(.hidden)
- HStack {
- Text(details.reservePub)
- .monospacedDigit()
- .accessibilityLabel("Cryptocode")
- Spacer()
- CopyButton(textToCopy: details.reservePub, vertical: true)
- .accessibilityLabel("Copy the cryptocode")
- .disabled(false)
- } .padding(.leading)
- .listRowSeparator(.hidden)
- Text("Step 2: If you don't already have it in your banking
favourites list, then copy and paste this IBAN into the receiver IBAN field in
your banking app or website:")
- .multilineTextAlignment(.leading)
- .listRowSeparator(.hidden)
- HStack {
- Text(iban)
- .monospacedDigit()
- Spacer()
- CopyButton(textToCopy: iban, vertical: true)
- .accessibilityLabel("Copy the IBAN")
- .disabled(false)
- } .padding(.leading)
- .padding(.top, -8)
- .listRowSeparator(.hidden)
- Text("Step 3: Finish the wire transfer of \(amount) in your
banking app or website, then this withdrawal will proceed automatically.")
- .multilineTextAlignment(.leading)
- .listRowSeparator(.visible)
- Text("Alternative: If your bank already supports PayTo, you can
use this PayTo-Link instead:")
- .multilineTextAlignment(.leading)
- .padding(.top, 2)
- .listRowSeparator(.hidden)
- HStack {
- Text(verbatim: "|") // only reason for this
leading-aligned text is to get a nice full length listRowSeparator
- .accessibilityHidden(true)
- .foregroundColor(Color.clear)
- Spacer()
- ShareButton(textToShare: payto)
- .accessibilityLabel("Share the PayTo URL")
- .disabled(false)
- Spacer()
- } .listRowSeparator(.automatic)
+ Group {
+ Text("You need to transfer \(amount) from your regular bank
account to the Exchange.")
+ Text("Step 1: Copy this code and paste it into the
subject/purpose field in your banking app or bank website.\nThis is mandatory,
otherwise your money will not arrive in this wallet.")
+ .multilineTextAlignment(.leading)
+ .listRowSeparator(.hidden)
+ HStack {
+ Text(details.reservePub)
+ .monospacedDigit()
+ .accessibilityLabel("Cryptocode")
+ Spacer()
+ CopyButton(textToCopy: details.reservePub, vertical: true)
+ .accessibilityLabel("Copy the cryptocode")
+ .disabled(false)
+ } .padding(.leading)
+ .listRowSeparator(.hidden)
+ Text("Step 2: If you don't already have it in your banking
favourites list, then copy and paste this IBAN into the receiver IBAN field in
your banking app or website:")
+ .multilineTextAlignment(.leading)
+ .listRowSeparator(.hidden)
+ HStack {
+ Text(iban)
+ .monospacedDigit()
+ Spacer()
+ CopyButton(textToCopy: iban, vertical: true)
+ .accessibilityLabel("Copy the IBAN")
+ .disabled(false)
+ } .padding(.leading)
+ .padding(.top, -8)
+ .listRowSeparator(.hidden)
+ Text("Step 3: Finish the wire transfer of \(amount) in your
banking app or website, then this withdrawal will proceed automatically.")
+ .multilineTextAlignment(.leading)
+ .listRowSeparator(.visible)
+ Text("Alternative: If your bank already supports PayTo, you
can use this PayTo-Link instead:")
+ .multilineTextAlignment(.leading)
+ .padding(.top, 2)
+ .listRowSeparator(.hidden)
+ HStack {
+ Text(verbatim: "|") // only reason for this
leading-aligned text is to get a nice full length listRowSeparator
+ .accessibilityHidden(true)
+ .foregroundColor(Color.clear)
+ Spacer()
+ ShareButton(textToShare: payto)
+ .accessibilityLabel("Share the PayTo URL")
+ .disabled(false)
+ Spacer()
+ } .listRowSeparator(.automatic)
+ }
+ .accessibilityFont(.body)
}
}
}
diff --git a/TalerWallet1/Views/Transactions/ThreeAmounts.swift
b/TalerWallet1/Views/Transactions/ThreeAmounts.swift
index 7b1397b..ff01a1a 100644
--- a/TalerWallet1/Views/Transactions/ThreeAmounts.swift
+++ b/TalerWallet1/Views/Transactions/ThreeAmounts.swift
@@ -68,12 +68,12 @@ struct ThreeAmountsView: View {
VStack(alignment: .leading) {
Text("Using Exchange:")
.multilineTextAlignment(.leading)
- .font(.body)
+ .accessibilityFont(.body)
HStack {
Spacer()
Text(baseURL.trimURL())
.multilineTextAlignment(.center)
- .font(large ? .title2 : .title3)
+ .accessibilityFont(large ? .title2 : .title3)
// .fontWeight(large ? .medium : .regular) //
@available(iOS 16.0, *)
.foregroundColor(labelColor)
Spacer()
diff --git a/TalerWallet1/Views/Transactions/TransactionDetailView.swift
b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
index 3178519..148d755 100644
--- a/TalerWallet1/Views/Transactions/TransactionDetailView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
@@ -22,7 +22,8 @@ extension Transaction { // for Dummys
}
// MARK: -
struct TransactionDetailView: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
#if DEBUG
@AppStorage("developerMode") var developerMode: Bool = true
@@ -66,17 +67,16 @@ struct TransactionDetailView: View {
} }
} // Suspend + Resume buttons
Text(dateString)
- .font(.title2)
+ .accessibilityFont(.title2)
.listRowSeparator(.hidden)
HStack {
Text(verbatim: "|") // only reason for this
leading-aligned text is to get a nice full length listRowSeparator
- .font(.title2)
.accessibilityHidden(true)
.foregroundColor(Color.clear)
Spacer()
Text("Status: \(common.txState.major.localizedState)")
- .font(.title2)
} .listRowSeparator(.automatic)
+ .accessibilityFont(.title2)
SwitchCase(transaction: $transaction, hasDone: doneAction !=
nil)
if transaction.isAbortable { if let abortAction {
@@ -128,7 +128,7 @@ struct TransactionDetailView: View {
} else {
symLog.log("ignoring newTxState.major:
\(newMajor)")
}
- } else { Task {
+ } else { Task { // runs on MainActor
do {
symLog.log("newState: \(newMajor), reloading
transaction")
withAnimation() { transaction =
Transaction(dummyCurrency: DEMOCURRENCY); viewId = UUID() }
@@ -175,8 +175,9 @@ struct TransactionDetailView: View {
let pending = transaction.isPending
Group {
switch transaction {
- case .dummy(let dummyTransaction):
+ case .dummy(_):
Text("")
+ .accessibilityFont(.body)
case .withdrawal(let withdrawalTransaction):
let details = withdrawalTransaction.details
if pending {
@@ -209,7 +210,7 @@ struct TransactionDetailView: View {
case .payment(let paymentTransaction):
let details = paymentTransaction.details
Text(details.info.summary)
- .font(.title2)
+ .accessibilityFont(.title2)
.lineLimit(4)
.padding(.bottom)
ThreeAmountsSheet(common: common, topTitle:
String(localized: "Sum to be paid:"),
@@ -229,16 +230,16 @@ struct TransactionDetailView: View {
case .peer2peer(let p2pTransaction):
let details = p2pTransaction.details
// TODO: details
Text(details.info.summary)
- .font(.title2)
+ .accessibilityFont(.title2)
.lineLimit(4)
.padding(.bottom)
- // TODO: isSendCoins should show QR only while not
expired
+ // 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.")
.multilineTextAlignment(.leading)
- .font(.subheadline)
+ .accessibilityFont(.subheadline)
.padding(.top)
}
}
@@ -265,7 +266,7 @@ struct TransactionDetailView: View {
} else if keys.contains(EXCHANGEBASEURL) {
if let baseURL = details[EXCHANGEBASEURL] {
Text("from \(baseURL.trimURL())")
- .font(.title2)
+ .accessibilityFont(.title2)
.padding(.bottom)
}
}
diff --git a/TalerWallet1/Views/Transactions/TransactionRowView.swift
b/TalerWallet1/Views/Transactions/TransactionRowView.swift
index cf57414..ee8ff88 100644
--- a/TalerWallet1/Views/Transactions/TransactionRowView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionRowView.swift
@@ -12,11 +12,11 @@ struct TransactionRowCenter: View {
var body: some View {
VStack(alignment: .leading) {
Text(centerTop)
- .font(.headline)
- .fontWeight(.medium)
+ .accessibilityFont(.headline)
+// .fontWeight(.medium) iOS 16
.padding(.bottom, -2.0)
Text(centerBottom)
- .font(.callout)
+ .accessibilityFont(.callout)
}
}
}
@@ -41,7 +41,7 @@ struct TransactionRowView: View {
HStack(spacing: 6) {
Image(systemName: incoming ? "text.badge.plus" :
"text.badge.minus")
.foregroundColor(foreColor)
- .font(.largeTitle)
+ .accessibilityFont(.largeTitle)
.accessibility(hidden: true)
TransactionRowCenter(centerTop: transaction.localizedType,
@@ -51,8 +51,8 @@ struct TransactionRowView: View {
let sign = incoming ? "+" : "-"
let valueStr = sign + amount.valueStr
Text(valueStr)
- .font(.title)
.foregroundColor(foreColor)
+ .accessibilityFont(.title)
.monospacedDigit()
}
}
diff --git a/TalerWallet1/Views/Transactions/TransactionsEmptyView.swift
b/TalerWallet1/Views/Transactions/TransactionsEmptyView.swift
index fa382ba..73dc463 100644
--- a/TalerWallet1/Views/Transactions/TransactionsEmptyView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionsEmptyView.swift
@@ -9,7 +9,7 @@ import SymLog
/// It is the very first thing the user sees after installing the app
struct TransactionsEmptyView: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
let currency: String
@@ -19,7 +19,7 @@ struct TransactionsEmptyView: View {
Section {
Text("There are no transactions for \(currency).")
}
- .font(.title2)
+ .accessibilityFont(.title2)
}
.listStyle(myListStyle.style).anyView
// .padding(.vertical)
diff --git a/TalerWallet1/Views/Transactions/TransactionsListView.swift
b/TalerWallet1/Views/Transactions/TransactionsListView.swift
index 16ce785..a0865a3 100644
--- a/TalerWallet1/Views/Transactions/TransactionsListView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionsListView.swift
@@ -7,6 +7,7 @@ import SymLog
struct TransactionsListView: View {
private let symLog = SymLogV(0)
+ let stack: CallStack
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
let navTitle: String
@@ -28,6 +29,7 @@ struct TransactionsListView: View {
ScrollViewReader { scrollView in
List {
TransactionsRowsView(symLog: symLog,
+ stack: stack.push(),
currency: currency,
transactions: transactions,
// reloadAllAction: reloadAllAction,
@@ -78,6 +80,7 @@ struct TransactionsListView: View {
// used by TransactionsListView, and by Balances to show the last 3
transactions
struct TransactionsRowsView: View {
let symLog: SymLogV?
+ let stack: CallStack
let currency: String
let transactions: [Transaction]
// let reloadAllAction: () async -> ()
@@ -97,15 +100,16 @@ struct TransactionsRowsView: View {
ForEach(Array(zip(transactions.indices, transactions)), id: \.1) {
index, transaction in
NavigationLink {
LazyView {
- TransactionDetailView(transactionId: transaction.id,
- reloadAction: reloadOneAction,
- navTitle: nil,
- doneAction: nil,
- abortAction: abortAction,
- deleteAction: deleteAction,
- failAction: failAction,
- suspendAction: suspendAction,
- resumeAction: resumeAction)
+ TransactionDetailView(stack: stack.push(),
+ transactionId: transaction.id,
+ reloadAction: reloadOneAction,
+ navTitle: nil,
+ doneAction: nil,
+ abortAction: abortAction,
+ deleteAction: deleteAction,
+ failAction: failAction,
+ suspendAction: suspendAction,
+ resumeAction: resumeAction)
}
} label: {
TransactionRowView(transaction: transaction)
diff --git a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptDone.swift
b/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptDone.swift
index ec64275..0799c6c 100644
--- a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptDone.swift
+++ b/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptDone.swift
@@ -7,7 +7,8 @@ import taler_swift
import SymLog
struct WithdrawAcceptDone: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let navTitle = String(localized: "Confirm with Bank")
let exchangeBaseUrl: String?
@@ -29,15 +30,16 @@ struct WithdrawAcceptDone: View {
#endif
Group {
if let transactionId {
- TransactionDetailView(transactionId: transactionId,
- reloadAction: reloadOneAction,
- navTitle: navTitle,
- doneAction: { dismissTop() },
- abortAction: nil,
- deleteAction: nil,
- failAction: nil,
- suspendAction: nil,
- resumeAction: nil)
+ TransactionDetailView(stack: stack.push(),
+ transactionId: transactionId,
+ reloadAction: reloadOneAction,
+ navTitle: navTitle,
+ doneAction: { dismissTop() },
+ abortAction: nil,
+ deleteAction: nil,
+ failAction: nil,
+ suspendAction: nil,
+ resumeAction: nil)
.navigationBarBackButtonHidden(true)
.interactiveDismissDisabled() // can only use "Done"
button to dismiss
.navigationTitle(navTitle)
@@ -66,7 +68,8 @@ struct WithdrawAcceptDone: View {
// MARK: -
struct WithdrawAcceptDone_Previews: PreviewProvider {
static var previews: some View {
- WithdrawAcceptDone(exchangeBaseUrl: DEMOEXCHANGE,
- url: URL(string: DEMOSHOP)!)
+ WithdrawAcceptDone(stack: CallStack("Preview"),
+ exchangeBaseUrl: DEMOEXCHANGE,
+ url: URL(string: DEMOSHOP)!)
}
}
diff --git a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptView.swift
b/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptView.swift
index ada134f..d336c82 100644
--- a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptView.swift
+++ b/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawAcceptView.swift
@@ -7,7 +7,7 @@ import taler_swift
import SymLog
struct WithdrawAcceptView: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
let navTitle = String(localized: "Accept Withdrawal")
let exchangeBaseUrl: String
diff --git
a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawProgressView.swift
b/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawProgressView.swift
index f623c3a..0c7e906 100644
--- a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawProgressView.swift
+++ b/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawProgressView.swift
@@ -15,7 +15,7 @@ struct WithdrawProgressView: View {
HStack {
Spacer()
Text(message)
- .font(.title)
+ .accessibilityFont(.title)
Spacer()
}
Spacer()
diff --git a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawTOSView.swift
b/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawTOSView.swift
index 41b7839..3df21c8 100644
--- a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawTOSView.swift
+++ b/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawTOSView.swift
@@ -6,12 +6,12 @@ import SwiftUI
import SymLog
struct WithdrawTOSView: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
let navTitle = String(localized: "Terms of Service")
- var exchangeBaseUrl: String?
+ let exchangeBaseUrl: String?
@EnvironmentObject private var model: WalletModel
@@ -24,7 +24,7 @@ struct WithdrawTOSView: View {
var body: some View {
VStack {
Content(symLog: symLog, exchangeTOS: exchangeTOS, myListStyle:
$myListStyle) {
- Task {
+ Task { // runs on MainActor
do {
if let exchangeBaseUrl {
_ = try await
model.setExchangeTOSAcceptedM(exchangeBaseUrl, etag: exchangeTOS!.currentEtag)
@@ -95,17 +95,18 @@ extension WithdrawTOSView {
if #available(iOS 16.0, *) {
Section {
Text(term5)
- .font(.footnote)
+ .accessibilityFont(.footnote)
.foregroundColor(Color(UIColor.label))
}
} else {
Text(term5)
- .font(.footnote)
+ .accessibilityFont(.footnote)
.foregroundColor(Color(UIColor.label))
}
- } else {
+ } else { // MarkDown
Section {
Text(term0)
+ .accessibilityFont(.body)
}
}
}.safeAreaInset(edge: .bottom) {
diff --git a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawURIView.swift
b/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawURIView.swift
index c41d20c..8e7580e 100644
--- a/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawURIView.swift
+++ b/TalerWallet1/Views/WithdrawBankIntegrated/WithdrawURIView.swift
@@ -10,7 +10,8 @@ import SymLog
// We show the user the withdrawal details - but first the ToS must be
accepted.
// After the user confirmed the withdrawal, we show a button to return to the
bank website to confirm there, too
struct WithdrawURIView: View {
- private let symLog = SymLogV()
+ private let symLog = SymLogV(0)
+ let stack: CallStack
let navTitle = String(localized: "Withdrawal")
// the URL from the bank website
@@ -50,23 +51,17 @@ struct WithdrawURIView: View {
let tosAccepted = withdrawalAmountDetails.tosAccepted
if tosAccepted {
NavigationLink(destination: LazyView {
- WithdrawAcceptDone(exchangeBaseUrl: exchangeBaseUrl,
url: url)
+ WithdrawAcceptDone(stack: stack.push(),
+ exchangeBaseUrl: exchangeBaseUrl,
+ url: url)
}) {
Text("Confirm Withdrawal") //
SHEET_WITHDRAW_ACCEPT
}.buttonStyle(TalerButtonStyle(type: .prominent))
.padding()
} else {
- Text("You must accept the Exchange's Terms of Service
first before you can use it to withdraw electronic cash to your wallet.")
- .multilineTextAlignment(.leading)
- .padding()
- NavigationLink(destination: LazyView {
- WithdrawTOSView(exchangeBaseUrl: exchangeBaseUrl,
- viewID: SHEET_WITHDRAW_TOS,
- acceptAction: nil) // pop
back to here
- }) {
- Text("Read Terms of Service") // VIEW_WITHDRAW_TOS
- }.buttonStyle(TalerButtonStyle(type: .prominent))
- .padding()
+ ToSButtonView(exchangeBaseUrl: exchangeBaseUrl,
+ viewID: SHEET_WITHDRAW_TOS,
+ p2p: false)
}
} else {
// Yikes no details or no baseURL
diff --git a/Taler_Wallet Info.plist b/Taler_Wallet Info.plist
index 958c7e9..811f070 100644
--- a/Taler_Wallet Info.plist
+++ b/Taler_Wallet Info.plist
@@ -32,6 +32,8 @@
<string>Atkinson-Hyperlegible-Italic-102.otf</string>
<string>Nunito-Regular.ttf</string>
<string>Nunito-Bold.ttf</string>
+ <string>Nunito-Black.ttf</string>
+ <string>Nunito-BlackItalic.ttf</string>
<string>Nunito-BoldItalic.ttf</string>
<string>Nunito-Italic.ttf</string>
</array>
diff --git a/taler-swift/Sources/taler-swift/Amount.swift
b/taler-swift/Sources/taler-swift/Amount.swift
index 647055b..2e91e6c 100644
--- a/taler-swift/Sources/taler-swift/Amount.swift
+++ b/taler-swift/Sources/taler-swift/Amount.swift
@@ -38,15 +38,15 @@ enum AmountError: Error {
case divideByZero
}
-public struct ScopedCurrencyInfo: Codable {
- var decimalSeparator: String
- var numFractionalDigits: Int // 0 Yen, 2 €,$, 3 arabic
- var numTinyDigits: Int // SuperScriptDigits
- var isCurrencyNameLeading: Bool
+public struct ScopedCurrencyInfo: Codable, Sendable {
+ let decimalSeparator: String
+ let numFractionalDigits: Int // 0 Yen, 2 €,$, 3 arabic
+ let numTinyDigits: Int // SuperScriptDigits
+ let isCurrencyNameLeading: Bool
}
/// A value of some currency.
-public class Amount: Codable, Hashable, CustomStringConvertible {
+public final class Amount: Codable, Hashable, @unchecked Sendable,
CustomStringConvertible { // TODO: @unchecked
/// Format that a currency must match.
private static let currencyRegex = #"^[-_*A-Za-z0-9]{1,12}$"#
diff --git a/taler-swift/Sources/taler-swift/Time.swift
b/taler-swift/Sources/taler-swift/Time.swift
index 43ada5d..98f0e27 100644
--- a/taler-swift/Sources/taler-swift/Time.swift
+++ b/taler-swift/Sources/taler-swift/Time.swift
@@ -17,7 +17,7 @@ enum TimestampError: Error {
}
/// A point in time, represented by milliseconds from January 1, 1970..
-public enum Timestamp: Codable, Hashable {
+public enum Timestamp: Codable, Hashable, Sendable {
case milliseconds(UInt64)
case never
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [taler-taler-ios] branch master updated (7d32de8 -> 8b2cfb1),
gnunet <=
- [taler-taler-ios] 02/28: move to HelperViews, gnunet, 2023/09/18
- [taler-taler-ios] 04/28: Font, gnunet, 2023/09/18
- [taler-taler-ios] 01/28: Haptics, gnunet, 2023/09/18
- [taler-taler-ios] 08/28: Binding+onChange, gnunet, 2023/09/18
- [taler-taler-ios] 10/28: ExchangeTosStatus, gnunet, 2023/09/18
- [taler-taler-ios] 03/28: talerFonts, gnunet, 2023/09/18
- [taler-taler-ios] 09/28: move AgePicker, gnunet, 2023/09/18
- [taler-taler-ios] 13/28: Comment, gnunet, 2023/09/18
- [taler-taler-ios] 06/28: Black, gnunet, 2023/09/18
- [taler-taler-ios] 14/28: ToSButton, gnunet, 2023/09/18