[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-taler-ios] branch master updated (a123c3a -> 4167b6d)
From: |
gnunet |
Subject: |
[taler-taler-ios] branch master updated (a123c3a -> 4167b6d) |
Date: |
Fri, 19 Jan 2024 09:01:33 +0100 |
This is an automated email from the git hooks/post-receive script.
marc-stibane pushed a change to branch master
in repository taler-ios.
from a123c3a Bump version to 0.9.3 (31)
new 6e44b57 WithdrawExchangeV
new 2387c4f LoadingView
new d555622 CurrencyInfo from Exchange
new 6ae5e98 AccountPicker, remove exchangePaytoUris
new 318f15a ToS language
new 477a0e4 withdraw-exchange
new fdc905d Haptics
new 1a49f45 fixes
new 223a4fb API 2:0:1
new d548c17 Exchanges->Banking
new f9af505 Landscape
new 447a870 PRINT_CHANGES
new d8d53cb definitions
new eb9f01f remove Apple Sounds
new 52494d6 hapticFeedback
new 66adf65 remove about protocols
new 0c0f3b1 Summary in ThreeAmountsV
new 7dea9e4 ToS for P2P
new 630e02a cleanup
new 9adda94 button color if disabled
new c5107ee TransactionDone popToRootView
new 2a34df6 Summary in ThreeAmountsV
new ca1e5b2 aborting Notification
new dc28b36 dbg+cleanup
new c6affde colors adapted for WCAG AA
new eaf3a05 Layout transactions
new 0d10ba1 use minor for Status
new 306b349 AccountRestriction
new 356f482 clearDb (instead of reset)
new 9729eec Layout Balances
new 10b9279 Withdraw only once
new b1b30fa Warnings for tx buttons
new c8adbe5 bars relative to fontSize
new d06f7ab tintcolor (blue) for links
new ab68cbf Markdown for ToS
new a436c14 b-i-withdrawal hint
new 67bd256 dbg+cleanup
new 6704c95 remove
new c4b0616 Bump version to 0.9.3 (34)
new c376385 Exchange -> Banking
new 864e217 AppIcon blue
new 439ae30 remove payto scheme
new 2d8ca75 currency == ISO 4217
new 5d28cd4 payment_received only for withdrawals
new bab4ef3 remove SideBarView
new 5ed30b8 AccountRestriction
new bd89805 single routine for rendering amounts
new 5e8eda4 fix fractions
new 4eb6003 accessibilityDate
new d243328 txStates
new f5d399e cleanup
new eaa421c return to Balances after tx
new 4b236b4 Summary no longer twice
new 71ec01c Keyboard needs more time
new 601bbf4 logging
new 85b019d remove "code", since "currency" IS ISO-4217 already for global
new 20225fb use cent initializer
new fa377b5 use scopeInfo
new c705832 bugfix
new 62578f6 P2P Expiration Timestamp
new ca2329e InfiniteTransactionLoop
new f6d7c04 cleanup, preview
new f4599bb Locale.preferredLanguageCode
new ae7a063 show minor in tx details
new 90c1ac1 Restrictions
new 58b2bab InterfaceOrientation
new 32813bf Bump version to 0.9.4 (0)
new 90aa5ad lowercased()
new 4167b6d Bump version to 0.9.4 (1)
The 69 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 | 1 -
TalerWallet.xcodeproj/project.pbxproj | 140 ++++++++++------
.../Contents.json | 19 +++
.../AppIcon3.appiconset/appstore1024.png | Bin 0 -> 916884 bytes
.../AppIcon3.appiconset/ipad152.png | Bin 0 -> 65123 bytes
.../Assets.xcassets/AppIcon3.appiconset/ipad76.png | Bin 0 -> 20561 bytes
.../AppIcon3.appiconset/ipadNotification20.png | Bin 0 -> 4737 bytes
.../AppIcon3.appiconset/ipadNotification40.png | Bin 0 -> 8588 bytes
.../AppIcon3.appiconset/ipadPro167.png | Bin 0 -> 76971 bytes
.../AppIcon3.appiconset/ipadSettings29.png | Bin 0 -> 6320 bytes
.../AppIcon3.appiconset/ipadSettings58.png | Bin 0 -> 13825 bytes
.../AppIcon3.appiconset/ipadSpotlight40.png | Bin 0 -> 8588 bytes
.../AppIcon3.appiconset/ipadSpotlight80.png | Bin 0 -> 21534 bytes
.../AppIcon3.appiconset/iphone120.png | Bin 0 -> 41974 bytes
.../AppIcon3.appiconset/iphone180.png | Bin 0 -> 85131 bytes
.../AppIcon3.appiconset/mac1024.png | Bin 0 -> 739039 bytes
.../Assets.xcassets/AppIcon3.appiconset/mac128.png | Bin 0 -> 46065 bytes
.../Assets.xcassets/AppIcon3.appiconset/mac16.png | Bin 0 -> 4224 bytes
.../Assets.xcassets/AppIcon3.appiconset/mac256.png | Bin 0 -> 150980 bytes
.../Assets.xcassets/AppIcon3.appiconset/mac32.png | Bin 0 -> 7369 bytes
.../Assets.xcassets/AppIcon3.appiconset/mac512.png | Bin 0 -> 432577 bytes
.../Assets.xcassets/AppIcon3.appiconset/mac64.png | Bin 0 -> 16145 bytes
.../AppIcon3.appiconset/notification40.png | Bin 0 -> 8588 bytes
.../AppIcon3.appiconset/notification60.png | Bin 0 -> 14846 bytes
.../AppIcon3.appiconset/settings58.png | Bin 0 -> 13825 bytes
.../AppIcon3.appiconset/settings87.png | Bin 0 -> 25602 bytes
.../AppIcon3.appiconset/spotlight120.png | Bin 0 -> 41974 bytes
.../AppIcon3.appiconset/spotlight80.png | Bin 0 -> 21534 bytes
TalerWallet1/Backend/WalletBackendRequest.swift | 4 +-
TalerWallet1/Backend/WalletCore.swift | 40 ++++-
TalerWallet1/Controllers/Controller.swift | 61 +++++--
TalerWallet1/Controllers/DebugViewC.swift | 6 +-
TalerWallet1/Controllers/PublicConstants.swift | 6 +-
TalerWallet1/Controllers/TalerWallet1App.swift | 2 +-
TalerWallet1/Helper/Controller+playSound.swift | 36 +++-
TalerWallet1/Helper/CurrencySpecification.swift | 34 +++-
TalerWallet1/Helper/TalerDater.swift | 9 +
TalerWallet1/Helper/URL+id+iban.swift | 4 +-
TalerWallet1/Helper/View+dismissTop.swift | 21 ++-
TalerWallet1/Helper/WalletColors.swift | 24 ++-
TalerWallet1/Model/Model+Exchange.swift | 5 +-
TalerWallet1/Model/Model+P2P.swift | 5 +-
TalerWallet1/Model/Model+Settings.swift | 31 +++-
TalerWallet1/Model/Model+Withdraw.swift | 52 +++++-
TalerWallet1/Model/Transaction.swift | 71 +++++++-
TalerWallet1/Model/WalletModel.swift | 2 +-
TalerWallet1/Quickjs/quickjs.swift | 11 +-
TalerWallet1/Views/Balances/BalanceRowView.swift | 83 +++++----
TalerWallet1/Views/Balances/BalancesListView.swift | 16 +-
.../Views/Balances/BalancesSectionView.swift | 44 +++--
TalerWallet1/Views/Balances/PendingRowView.swift | 21 +--
.../{Exchange => Banking}/ExchangeListView.swift | 18 +-
.../{Exchange => Banking}/ExchangeRowView.swift | 39 +++--
.../ExchangeSectionView.swift | 23 ++-
.../{Exchange => Banking}/ManualWithdraw.swift | 59 ++++---
.../{Exchange => Banking}/ManualWithdrawDone.swift | 27 +--
.../{Exchange => Banking}/QuiteSomeCoins.swift | 0
TalerWallet1/Views/HelperViews/AmountRowV.swift | 136 +++++++--------
TalerWallet1/Views/HelperViews/AmountV.swift | 38 +++++
TalerWallet1/Views/HelperViews/AmountView.swift | 44 -----
TalerWallet1/Views/HelperViews/BarGraph.swift | 12 +-
TalerWallet1/Views/HelperViews/Buttons.swift | 8 +-
TalerWallet1/Views/HelperViews/CopyShare.swift | 11 +-
TalerWallet1/Views/HelperViews/CurrencyField.swift | 2 +-
.../Views/HelperViews/CurrencyInputView.swift | 2 +-
.../Views/HelperViews/LaunchAnimationView.swift | 2 +-
TalerWallet1/Views/HelperViews/LoadingView.swift | 43 ++++-
.../Views/HelperViews/QRCodeDetailView.swift | 23 ++-
.../Views/HelperViews/TransactionButton.swift | 53 ++++--
TalerWallet1/Views/Main/MainView.swift | 111 +++---------
TalerWallet1/Views/Main/SideBarView.swift | 109 ------------
TalerWallet1/Views/Main/WalletEmptyView.swift | 2 +-
TalerWallet1/Views/Peer2peer/P2PReadyV.swift | 4 +-
TalerWallet1/Views/Peer2peer/P2PSubjectV.swift | 6 +-
TalerWallet1/Views/Peer2peer/RequestPayment.swift | 2 +-
TalerWallet1/Views/Peer2peer/SendAmount.swift | 4 +-
TalerWallet1/Views/Settings/AboutView.swift | 22 +--
TalerWallet1/Views/Settings/SettingsItem.swift | 4 +
TalerWallet1/Views/Settings/SettingsView.swift | 67 +++++---
.../Views/Sheets/P2P_Sheets/P2pAcceptDone.swift | 9 +-
.../Views/Sheets/P2P_Sheets/P2pPayURIView.swift | 34 ++--
.../Sheets/P2P_Sheets/P2pReceiveURIView.swift | 30 ++--
.../Views/Sheets/Payment/PayTemplateView.swift | 12 +-
.../Views/Sheets/Payment/PaymentView.swift | 17 +-
TalerWallet1/Views/Sheets/QRSheet.swift | 2 +-
.../Views/Sheets/Refund/RefundURIView.swift | 4 +-
TalerWallet1/Views/Sheets/URLSheet.swift | 6 +-
.../WithdrawAcceptDone.swift | 11 +-
.../WithdrawProgressView.swift | 32 ----
.../WithdrawBankIntegrated/WithdrawTOSView.swift | 162 +++++++++++-------
.../WithdrawBankIntegrated/WithdrawURIView.swift | 25 +--
TalerWallet1/Views/Sheets/WithdrawExchangeV.swift | 53 ++++++
.../Views/Transactions/ManualDetailsV.swift | 186 +++++++++++++++------
.../Views/Transactions/ThreeAmountsV.swift | 28 +++-
.../Views/Transactions/TransactionDetailView.swift | 130 +++++++-------
.../Views/Transactions/TransactionRowView.swift | 167 +++++++++---------
.../Views/Transactions/TransactionsListView.swift | 4 +-
Taler_Wallet Info.plist | 1 -
TestFlight/WhatToTest.en-US.txt | 34 ++++
taler-swift/Sources/taler-swift/Amount.swift | 3 +-
100 files changed, 1533 insertions(+), 1036 deletions(-)
copy TalerWallet1/Assets.xcassets/{AppIcon.appiconset =>
AppIcon3.appiconset}/Contents.json (80%)
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/appstore1024.png
create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad152.png
create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad76.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification20.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification40.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadPro167.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings29.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings58.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight40.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight80.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone120.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone180.png
create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac1024.png
create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac128.png
create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac16.png
create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac256.png
create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac32.png
create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac512.png
create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac64.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification40.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification60.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings58.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings87.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight120.png
create mode 100644
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight80.png
rename TalerWallet1/Views/{Exchange => Banking}/ExchangeListView.swift (92%)
rename TalerWallet1/Views/{Exchange => Banking}/ExchangeRowView.swift (84%)
rename TalerWallet1/Views/{Exchange => Banking}/ExchangeSectionView.swift (69%)
rename TalerWallet1/Views/{Exchange => Banking}/ManualWithdraw.swift (70%)
rename TalerWallet1/Views/{Exchange => Banking}/ManualWithdrawDone.swift (77%)
rename TalerWallet1/Views/{Exchange => Banking}/QuiteSomeCoins.swift (100%)
create mode 100644 TalerWallet1/Views/HelperViews/AmountV.swift
delete mode 100644 TalerWallet1/Views/HelperViews/AmountView.swift
delete mode 100644 TalerWallet1/Views/Main/SideBarView.swift
delete mode 100644
TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawProgressView.swift
create mode 100644 TalerWallet1/Views/Sheets/WithdrawExchangeV.swift
diff --git a/GNU_Taler Info.plist b/GNU_Taler Info.plist
index b6dac8f..e6aba3e 100644
--- a/GNU_Taler Info.plist
+++ b/GNU_Taler Info.plist
@@ -18,7 +18,6 @@
<string>taler</string>
<string>ext+taler</string>
<string>web+taler</string>
- <string>payto</string>
</array>
</dict>
</array>
diff --git a/TalerWallet.xcodeproj/project.pbxproj
b/TalerWallet.xcodeproj/project.pbxproj
index 058b9b6..1739f81 100644
--- a/TalerWallet.xcodeproj/project.pbxproj
+++ b/TalerWallet.xcodeproj/project.pbxproj
@@ -10,8 +10,9 @@
4E16E12329F3BB99008B9C86 /* CurrencySpecification.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4E16E12229F3BB99008B9C86 /*
CurrencySpecification.swift */; };
4E2254972A822B8100E41D29 /* payment_received.m4a in Resources
*/ = {isa = PBXBuildFile; fileRef = 4E2254952A822B8100E41D29 /*
payment_received.m4a */; };
4E2254982A822B8100E41D29 /* payment_sent.m4a in Resources */ =
{isa = PBXBuildFile; fileRef = 4E2254962A822B8100E41D29 /* payment_sent.m4a */;
};
- 4E2E5F7F2AEE30DA0027FA8A /* AmountRowV.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E2E5F7E2AEE30DA0027FA8A /* AmountRowV.swift */;
};
- 4E2E5F802AEE30DA0027FA8A /* AmountRowV.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E2E5F7E2AEE30DA0027FA8A /* AmountRowV.swift */;
};
+ 4E2D8DD32B3F513800234039 /* MarkdownUI in Frameworks */ = {isa
= PBXBuildFile; productRef = 4E2D8DD22B3F513800234039 /* MarkdownUI */; };
+ 4E2D8DD52B45822A00234039 /* AmountV.swift in Sources */ = {isa
= PBXBuildFile; fileRef = 4E2D8DD42B45822A00234039 /* AmountV.swift */; };
+ 4E2D8DD62B45822A00234039 /* AmountV.swift in Sources */ = {isa
= PBXBuildFile; fileRef = 4E2D8DD42B45822A00234039 /* AmountV.swift */; };
4E3327BA2AD1635100BF5AD6 /* AsyncSemaphore.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4E3327B92AD1635100BF5AD6 /*
AsyncSemaphore.swift */; };
4E3327BB2AD1635100BF5AD6 /* AsyncSemaphore.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4E3327B92AD1635100BF5AD6 /*
AsyncSemaphore.swift */; };
4E363CBC2A237E0900D7E98C /* URL+id+iban.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E363CBB2A237E0900D7E98C /* URL+id+iban.swift
*/; };
@@ -67,7 +68,7 @@
4E3EAE482A990778009F1BE8 /* PayTemplateView.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4EBA56402A7FF5200084948B /*
PayTemplateView.swift */; };
4E3EAE492A990778009F1BE8 /* ManualWithdrawDone.swift in Sources
*/ = {isa = PBXBuildFile; fileRef = 4EB431662A1E55C700C5690E /*
ManualWithdrawDone.swift */; };
4E3EAE4B2A990778009F1BE8 /* ShareSheet.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E753A072A0B6A5F002D9328 /* ShareSheet.swift */;
};
- 4E3EAE4C2A990778009F1BE8 /* AmountView.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB095492989CBFE0043A8A1 /* AmountView.swift */;
};
+ 4E3EAE4C2A990778009F1BE8 /* AmountRowV.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB095492989CBFE0043A8A1 /* AmountRowV.swift */;
};
4E3EAE4D2A990778009F1BE8 /* P2pAcceptDone.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E3B4BC22A42252300CC88B8 /* P2pAcceptDone.swift
*/; };
4E3EAE4E2A990778009F1BE8 /* AnyTransition+backslide.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4E363CBD2A23CB2100D7E98C /*
AnyTransition+backslide.swift */; };
4E3EAE4F2A990778009F1BE8 /* TwoRowButtons.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB065432A4CD1A80039B91D /* TwoRowButtons.swift
*/; };
@@ -85,14 +86,12 @@
4E3EAE5B2A990778009F1BE8 /* View+Notification.swift in Sources
*/ = {isa = PBXBuildFile; fileRef = 4E3B4BC62A429F2A00CC88B8 /*
View+Notification.swift */; };
4E3EAE5C2A990778009F1BE8 /* Model+Pending.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift
*/; };
4E3EAE5D2A990778009F1BE8 /* ExchangeListView.swift in Sources
*/ = {isa = PBXBuildFile; fileRef = 4EB095292989CBFE0043A8A1 /*
ExchangeListView.swift */; };
- 4E3EAE5E2A990778009F1BE8 /* WithdrawProgressView.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4EB0953F2989CBFE0043A8A1 /*
WithdrawProgressView.swift */; };
4E3EAE5F2A990778009F1BE8 /* QRSheet.swift in Sources */ = {isa
= PBXBuildFile; fileRef = 4EEC157929F9427F00D46A03 /* QRSheet.swift */; };
4E3EAE602A990778009F1BE8 /* P2pReceiveURIView.swift in Sources
*/ = {isa = PBXBuildFile; fileRef = 4E3B4BC02A41E6C200CC88B8 /*
P2pReceiveURIView.swift */; };
4E3EAE612A990778009F1BE8 /* ListStyle.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E6EDD862A363D8D0031D520 /* ListStyle.swift */;
};
4E3EAE622A990778009F1BE8 /* TransactionDetailView.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4EB095312989CBFE0043A8A1 /*
TransactionDetailView.swift */; };
4E3EAE632A990778009F1BE8 /* WalletCore.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB0951C2989CBCB0043A8A1 /* WalletCore.swift */;
};
4E3EAE642A990778009F1BE8 /* LaunchAnimationView.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4EB095432989CBFE0043A8A1 /*
LaunchAnimationView.swift */; };
- 4E3EAE652A990778009F1BE8 /* SideBarView.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB095422989CBFE0043A8A1 /* SideBarView.swift
*/; };
4E3EAE682A990778009F1BE8 /* WalletModel.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB095112989CBB00043A8A1 /* WalletModel.swift
*/; };
4E3EAE692A990778009F1BE8 /* URLSheet.swift in Sources */ = {isa
= PBXBuildFile; fileRef = 4EB095332989CBFE0043A8A1 /* URLSheet.swift */; };
4E3EAE6A2A990778009F1BE8 /* ThreeAmountsV.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4ED2F94A2A278F5100453B40 /* ThreeAmountsV.swift
*/; };
@@ -208,15 +207,13 @@
4EB095602989CBFE0043A8A1 /* BalancesSectionView.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4EB0953A2989CBFE0043A8A1 /*
BalancesSectionView.swift */; };
4EB095612989CBFE0043A8A1 /* WithdrawURIView.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4EB0953C2989CBFE0043A8A1 /*
WithdrawURIView.swift */; };
4EB095622989CBFE0043A8A1 /* Model+Withdraw.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4EB0953D2989CBFE0043A8A1 /*
Model+Withdraw.swift */; };
- 4EB095642989CBFE0043A8A1 /* WithdrawProgressView.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4EB0953F2989CBFE0043A8A1 /*
WithdrawProgressView.swift */; };
4EB095652989CBFE0043A8A1 /* WithdrawTOSView.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4EB095402989CBFE0043A8A1 /*
WithdrawTOSView.swift */; };
- 4EB095662989CBFE0043A8A1 /* SideBarView.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB095422989CBFE0043A8A1 /* SideBarView.swift
*/; };
4EB095672989CBFE0043A8A1 /* LaunchAnimationView.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4EB095432989CBFE0043A8A1 /*
LaunchAnimationView.swift */; };
4EB095682989CBFE0043A8A1 /* MainView.swift in Sources */ = {isa
= PBXBuildFile; fileRef = 4EB095442989CBFE0043A8A1 /* MainView.swift */; };
4EB095692989CBFE0043A8A1 /* ErrorView.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB095452989CBFE0043A8A1 /* ErrorView.swift */;
};
4EB0956A2989CBFE0043A8A1 /* Buttons.swift in Sources */ = {isa
= PBXBuildFile; fileRef = 4EB095472989CBFE0043A8A1 /* Buttons.swift */; };
4EB0956B2989CBFE0043A8A1 /* TextFieldAlert.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4EB095482989CBFE0043A8A1 /*
TextFieldAlert.swift */; };
- 4EB0956C2989CBFE0043A8A1 /* AmountView.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB095492989CBFE0043A8A1 /* AmountView.swift */;
};
+ 4EB0956C2989CBFE0043A8A1 /* AmountRowV.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB095492989CBFE0043A8A1 /* AmountRowV.swift */;
};
4EB0956D2989CBFE0043A8A1 /* LoadingView.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB0954A2989CBFE0043A8A1 /* LoadingView.swift
*/; };
4EB0956E2989CBFE0043A8A1 /* Model+Pending.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift
*/; };
4EB3136129FEE79B007D68BC /* P2PReadyV.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB3136029FEE79B007D68BC /* P2PReadyV.swift */;
};
@@ -237,10 +234,15 @@
4ED2F94B2A278F5100453B40 /* ThreeAmountsV.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4ED2F94A2A278F5100453B40 /* ThreeAmountsV.swift
*/; };
4EDBDCD92AB787CB00925C02 /* CallStack.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EDBDCD82AB787CB00925C02 /* CallStack.swift */;
};
4EDBDCDA2AB787CB00925C02 /* CallStack.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EDBDCD82AB787CB00925C02 /* CallStack.swift */;
};
+ 4EE171882B49635800BF9FF5 /* MarkdownUI in Frameworks */ = {isa
= PBXBuildFile; productRef = 4EE171872B49635800BF9FF5 /* MarkdownUI */; };
+ 4EE171902B49FE2B00BF9FF5 /* OrderedCollections in Frameworks */
= {isa = PBXBuildFile; productRef = 4EE1718F2B49FE2B00BF9FF5 /*
OrderedCollections */; };
+ 4EE171922B49FE4E00BF9FF5 /* OrderedCollections in Frameworks */
= {isa = PBXBuildFile; productRef = 4EE171912B49FE4E00BF9FF5 /*
OrderedCollections */; };
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 */; };
+ 4EEC3A712B2285A200D05F9D /* WithdrawExchangeV.swift in Sources
*/ = {isa = PBXBuildFile; fileRef = 4EEC3A702B2285A200D05F9D /*
WithdrawExchangeV.swift */; };
+ 4EEC3A722B2285A200D05F9D /* WithdrawExchangeV.swift in Sources
*/ = {isa = PBXBuildFile; fileRef = 4EEC3A702B2285A200D05F9D /*
WithdrawExchangeV.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
*/; };
@@ -294,7 +296,7 @@
4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */ =
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType =
sourcecode.swift; path = CurrencySpecification.swift; sourceTree = "<group>"; };
4E2254952A822B8100E41D29 /* payment_received.m4a */ = {isa =
PBXFileReference; lastKnownFileType = file; path = payment_received.m4a;
sourceTree = "<group>"; };
4E2254962A822B8100E41D29 /* payment_sent.m4a */ = {isa =
PBXFileReference; lastKnownFileType = file; path = payment_sent.m4a; sourceTree
= "<group>"; };
- 4E2E5F7E2AEE30DA0027FA8A /* AmountRowV.swift */ = {isa =
PBXFileReference; lastKnownFileType = sourcecode.swift; path =
AmountRowV.swift; sourceTree = "<group>"; };
+ 4E2D8DD42B45822A00234039 /* AmountV.swift */ = {isa =
PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmountV.swift;
sourceTree = "<group>"; };
4E3327B92AD1635100BF5AD6 /* AsyncSemaphore.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= AsyncSemaphore.swift; sourceTree = "<group>"; };
4E363CBB2A237E0900D7E98C /* URL+id+iban.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= "URL+id+iban.swift"; sourceTree = "<group>"; };
4E363CBD2A23CB2100D7E98C /* AnyTransition+backslide.swift */ =
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType =
sourcecode.swift; path = "AnyTransition+backslide.swift"; sourceTree =
"<group>"; };
@@ -383,15 +385,13 @@
4EB0953A2989CBFE0043A8A1 /* BalancesSectionView.swift */ = {isa
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift;
path = BalancesSectionView.swift; sourceTree = "<group>"; };
4EB0953C2989CBFE0043A8A1 /* WithdrawURIView.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= WithdrawURIView.swift; sourceTree = "<group>"; };
4EB0953D2989CBFE0043A8A1 /* Model+Withdraw.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= "Model+Withdraw.swift"; sourceTree = "<group>"; };
- 4EB0953F2989CBFE0043A8A1 /* WithdrawProgressView.swift */ =
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType =
sourcecode.swift; path = WithdrawProgressView.swift; sourceTree = "<group>"; };
4EB095402989CBFE0043A8A1 /* WithdrawTOSView.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= WithdrawTOSView.swift; sourceTree = "<group>"; };
- 4EB095422989CBFE0043A8A1 /* SideBarView.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= SideBarView.swift; sourceTree = "<group>"; };
4EB095432989CBFE0043A8A1 /* LaunchAnimationView.swift */ = {isa
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift;
path = LaunchAnimationView.swift; sourceTree = "<group>"; };
4EB095442989CBFE0043A8A1 /* MainView.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= MainView.swift; sourceTree = "<group>"; };
4EB095452989CBFE0043A8A1 /* ErrorView.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= ErrorView.swift; sourceTree = "<group>"; };
4EB095472989CBFE0043A8A1 /* Buttons.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= Buttons.swift; sourceTree = "<group>"; };
4EB095482989CBFE0043A8A1 /* TextFieldAlert.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= TextFieldAlert.swift; sourceTree = "<group>"; };
- 4EB095492989CBFE0043A8A1 /* AmountView.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= AmountView.swift; sourceTree = "<group>"; };
+ 4EB095492989CBFE0043A8A1 /* AmountRowV.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= AmountRowV.swift; sourceTree = "<group>"; };
4EB0954A2989CBFE0043A8A1 /* LoadingView.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= LoadingView.swift; sourceTree = "<group>"; };
4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= "Model+Pending.swift"; sourceTree = "<group>"; };
4EB3136029FEE79B007D68BC /* P2PReadyV.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= P2PReadyV.swift; sourceTree = "<group>"; };
@@ -411,6 +411,7 @@
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>"; };
+ 4EEC3A702B2285A200D05F9D /* WithdrawExchangeV.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= WithdrawExchangeV.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; };
@@ -429,7 +430,9 @@
4E3EAE772A990778009F1BE8 /* AnyCodable in
Frameworks */,
4E3EAE782A990778009F1BE8 /* SymLog in
Frameworks */,
4E3EAE792A990778009F1BE8 /*
FTalerWalletcore.framework in Frameworks */,
+ 4EE171882B49635800BF9FF5 /* MarkdownUI in
Frameworks */,
4E3EAE7A2A990778009F1BE8 /* taler-swift in
Frameworks */,
+ 4EE171922B49FE4E00BF9FF5 /* OrderedCollections
in Frameworks */,
4E3EAE7B2A990778009F1BE8 /* CodeScanner in
Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -441,7 +444,9 @@
ABE97B1D286D82BF00580772 /* AnyCodable in
Frameworks */,
4EB094FD29897D280043A8A1 /* SymLog in
Frameworks */,
4EB094F829897CA20043A8A1 /*
FTalerWalletcore.framework in Frameworks */,
+ 4E2D8DD32B3F513800234039 /* MarkdownUI in
Frameworks */,
ABC13AA32859962800D23185 /* taler-swift in
Frameworks */,
+ 4EE171902B49FE2B00BF9FF5 /* OrderedCollections
in Frameworks */,
4EEC157629F8ECBF00D46A03 /* CodeScanner in
Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -613,7 +618,7 @@
4ECB627F2A0BA6DF004ABBB7 /* Model+P2P.swift */,
4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift
*/,
4EB0952C2989CBFE0043A8A1 /* Model+Payment.swift
*/,
- E37AA6292AF197E5003850CF /* Model+Refund.swift */,
+ E37AA6292AF197E5003850CF /* Model+Refund.swift
*/,
4EB095102989CBB00043A8A1 /*
Model+Settings.swift */,
4EB095322989CBFE0043A8A1 /*
Model+Transactions.swift */,
4EB0953D2989CBFE0043A8A1 /*
Model+Withdraw.swift */,
@@ -638,7 +643,7 @@
4EB095412989CBFE0043A8A1 /* Main */,
4EB095342989CBFE0043A8A1 /* Balances */,
4EB0952E2989CBFE0043A8A1 /* Transactions */,
- 4EB095272989CBFE0043A8A1 /* Exchange */,
+ 4EB095272989CBFE0043A8A1 /* Banking */,
4EB095242989CBFE0043A8A1 /* Settings */,
4ECB627E2A0BA4DA004ABBB7 /* Peer2peer */,
4EEC157129F7188B00D46A03 /* Sheets */,
@@ -657,7 +662,7 @@
path = Settings;
sourceTree = "<group>";
};
- 4EB095272989CBFE0043A8A1 /* Exchange */ = {
+ 4EB095272989CBFE0043A8A1 /* Banking */ = {
isa = PBXGroup;
children = (
4EB095292989CBFE0043A8A1 /*
ExchangeListView.swift */,
@@ -667,7 +672,7 @@
4EBA82AC2A3F580500E5F39A /*
QuiteSomeCoins.swift */,
4EB431662A1E55C700C5690E /*
ManualWithdrawDone.swift */,
);
- path = Exchange;
+ path = Banking;
sourceTree = "<group>";
};
4EB0952A2989CBFE0043A8A1 /* Payment */ = {
@@ -709,7 +714,6 @@
children = (
4EB0953C2989CBFE0043A8A1 /*
WithdrawURIView.swift */,
4E5A88F62A3B9E5B00072618 /*
WithdrawAcceptDone.swift */,
- 4EB0953F2989CBFE0043A8A1 /*
WithdrawProgressView.swift */,
4EB095402989CBFE0043A8A1 /*
WithdrawTOSView.swift */,
);
path = WithdrawBankIntegrated;
@@ -719,7 +723,6 @@
isa = PBXGroup;
children = (
4EB095442989CBFE0043A8A1 /* MainView.swift */,
- 4EB095422989CBFE0043A8A1 /* SideBarView.swift
*/,
4EB095452989CBFE0043A8A1 /* ErrorView.swift */,
4EB095392989CBFE0043A8A1 /*
WalletEmptyView.swift */,
);
@@ -730,12 +733,12 @@
isa = PBXGroup;
children = (
4E97968F2A3765ED006F73BC /* AgePicker.swift */,
- 4E2E5F7E2AEE30DA0027FA8A /* AmountRowV.swift */,
4E605DB92AB05FB6002FB9A7 /* BarGraph.swift */,
4EB095472989CBFE0043A8A1 /* Buttons.swift */,
4EF840A62A0B85F400EE0D47 /* CopyShare.swift */,
4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */,
4EA551242A2C923600FEC9A8 /*
CurrencyInputView.swift */,
+ 4E2D8DD42B45822A00234039 /* AmountV.swift */,
4E53A33629F50B7B00830EC2 /* CurrencyField.swift
*/,
4EEC157229F8242800D46A03 /*
QRGeneratorView.swift */,
4E5A88F42A38A4FD00072618 /*
QRCodeDetailView.swift */,
@@ -744,7 +747,7 @@
4E983C2B2ADC416800FA9CC5 /*
View+fitsSideBySide.swift */,
4EB095482989CBFE0043A8A1 /*
TextFieldAlert.swift */,
4EBA82AA2A3EB2CA00E5F39A /*
TransactionButton.swift */,
- 4EB095492989CBFE0043A8A1 /* AmountView.swift */,
+ 4EB095492989CBFE0043A8A1 /* AmountRowV.swift */,
4EB0954A2989CBFE0043A8A1 /* LoadingView.swift
*/,
4EB095432989CBFE0043A8A1 /*
LaunchAnimationView.swift */,
4EFA395F2AA7946B00742548 /* ToSButtonView.swift
*/,
@@ -770,9 +773,10 @@
4EEC157929F9427F00D46A03 /* QRSheet.swift */,
4E753A072A0B6A5F002D9328 /* ShareSheet.swift */,
4EB095332989CBFE0043A8A1 /* URLSheet.swift */,
+ 4EEC3A702B2285A200D05F9D /*
WithdrawExchangeV.swift */,
4EB0953B2989CBFE0043A8A1 /*
WithdrawBankIntegrated */,
4EB0952A2989CBFE0043A8A1 /* Payment */,
- E37AA62C2AF19BA6003850CF /* Refund */,
+ E37AA62C2AF19BA6003850CF /* Refund */,
4E3B4BBF2A41E64000CC88B8 /* P2P_Sheets */,
);
path = Sheets;
@@ -851,6 +855,8 @@
4E3EAE162A990778009F1BE8 /* AnyCodable */,
4E3EAE182A990778009F1BE8 /* SymLog */,
4E3EAE1A2A990778009F1BE8 /* CodeScanner */,
+ 4EE171872B49635800BF9FF5 /* MarkdownUI */,
+ 4EE171912B49FE4E00BF9FF5 /* OrderedCollections
*/,
);
productName = Taler;
productReference = 4E3EAE892A990778009F1BE8 /*
GNU_Taler.app */;
@@ -875,6 +881,8 @@
ABE97B1C286D82BF00580772 /* AnyCodable */,
4EB094FC29897D280043A8A1 /* SymLog */,
4EEC157529F8ECBF00D46A03 /* CodeScanner */,
+ 4E2D8DD22B3F513800234039 /* MarkdownUI */,
+ 4EE1718F2B49FE2B00BF9FF5 /* OrderedCollections
*/,
);
productName = Taler;
productReference = D14AFD1D24D232B300C51073 /*
Taler_Wallet.app */;
@@ -955,6 +963,8 @@
ABE97B1B286D82BF00580772 /*
XCRemoteSwiftPackageReference "AnyCodable" */,
4EB094FB29897D280043A8A1 /*
XCRemoteSwiftPackageReference "SymLog" */,
4EEC157429F8ECBF00D46A03 /*
XCRemoteSwiftPackageReference "CodeScanner" */,
+ 4E2D8DD12B3F513800234039 /*
XCRemoteSwiftPackageReference "swift-markdown-ui-standalone" */,
+ 4EE1718C2B49EB7200BF9FF5 /*
XCRemoteSwiftPackageReference "swift-collections" */,
);
productRefGroup = D14AFD1E24D232B300C51073 /* Products
*/;
projectDirPath = "";
@@ -1079,11 +1089,12 @@
4E3EAE452A990778009F1BE8 /* P2PReadyV.swift in
Sources */,
4E3EAE462A990778009F1BE8 /*
TextFieldAlert.swift in Sources */,
4E3EAE472A990778009F1BE8 /*
QuiteSomeCoins.swift in Sources */,
+ 4E2D8DD52B45822A00234039 /* AmountV.swift in
Sources */,
4E3EAE482A990778009F1BE8 /*
PayTemplateView.swift in Sources */,
4E3EAE492A990778009F1BE8 /*
ManualWithdrawDone.swift in Sources */,
4E3EAE4B2A990778009F1BE8 /* ShareSheet.swift in
Sources */,
4EC4008F2AE8019700DF72C7 /*
ExchangeRowView.swift in Sources */,
- 4E3EAE4C2A990778009F1BE8 /* AmountView.swift in
Sources */,
+ 4E3EAE4C2A990778009F1BE8 /* AmountRowV.swift in
Sources */,
4E605DBA2AB05FB6002FB9A7 /* BarGraph.swift in
Sources */,
4E3EAE4D2A990778009F1BE8 /* P2pAcceptDone.swift
in Sources */,
4E3EAE4E2A990778009F1BE8 /*
AnyTransition+backslide.swift in Sources */,
@@ -1102,11 +1113,9 @@
4E3EAE592A990778009F1BE8 /*
Model+Settings.swift in Sources */,
4EC4008C2AE5664100DF72C7 /*
CharacterSet+contains.swift in Sources */,
4E3EAE5A2A990778009F1BE8 /* ErrorView.swift in
Sources */,
- 4E2E5F7F2AEE30DA0027FA8A /* AmountRowV.swift in
Sources */,
4E3EAE5B2A990778009F1BE8 /*
View+Notification.swift in Sources */,
4E3EAE5C2A990778009F1BE8 /* Model+Pending.swift
in Sources */,
4E3EAE5D2A990778009F1BE8 /*
ExchangeListView.swift in Sources */,
- 4E3EAE5E2A990778009F1BE8 /*
WithdrawProgressView.swift in Sources */,
E37AA62E2AF19BE0003850CF /* RefundURIView.swift
in Sources */,
4E3EAE5F2A990778009F1BE8 /* QRSheet.swift in
Sources */,
4E3EAE602A990778009F1BE8 /*
P2pReceiveURIView.swift in Sources */,
@@ -1114,7 +1123,6 @@
4E3EAE622A990778009F1BE8 /*
TransactionDetailView.swift in Sources */,
4E3EAE632A990778009F1BE8 /* WalletCore.swift in
Sources */,
4E3EAE642A990778009F1BE8 /*
LaunchAnimationView.swift in Sources */,
- 4E3EAE652A990778009F1BE8 /* SideBarView.swift
in Sources */,
E37AA62A2AF197E5003850CF /* Model+Refund.swift
in Sources */,
4E3EAE682A990778009F1BE8 /* WalletModel.swift
in Sources */,
4E3EAE692A990778009F1BE8 /* URLSheet.swift in
Sources */,
@@ -1126,6 +1134,7 @@
4E3EAE6F2A990778009F1BE8 /* TalerStrings.swift
in Sources */,
4E3EAE702A990778009F1BE8 /*
CurrencyInputView.swift in Sources */,
4E3EAE712A990778009F1BE8 /* URL+id+iban.swift
in Sources */,
+ 4EEC3A712B2285A200D05F9D /*
WithdrawExchangeV.swift in Sources */,
4EDBDCD92AB787CB00925C02 /* CallStack.swift in
Sources */,
4E3EAE722A990778009F1BE8 /*
RequestPayment.swift in Sources */,
4E3EAE732A990778009F1BE8 /* SettingsItem.swift
in Sources */,
@@ -1186,11 +1195,12 @@
4EB3136129FEE79B007D68BC /* P2PReadyV.swift in
Sources */,
4EB0956B2989CBFE0043A8A1 /*
TextFieldAlert.swift in Sources */,
4EBA82AD2A3F580500E5F39A /*
QuiteSomeCoins.swift in Sources */,
+ 4E2D8DD62B45822A00234039 /* AmountV.swift in
Sources */,
4EBA56412A7FF5200084948B /*
PayTemplateView.swift in Sources */,
4EB431672A1E55C700C5690E /*
ManualWithdrawDone.swift in Sources */,
4E753A082A0B6A5F002D9328 /* ShareSheet.swift in
Sources */,
4EC400902AE8019700DF72C7 /*
ExchangeRowView.swift in Sources */,
- 4EB0956C2989CBFE0043A8A1 /* AmountView.swift in
Sources */,
+ 4EB0956C2989CBFE0043A8A1 /* AmountRowV.swift in
Sources */,
4E605DBB2AB05FB6002FB9A7 /* BarGraph.swift in
Sources */,
4E3B4BC32A42252300CC88B8 /* P2pAcceptDone.swift
in Sources */,
4E363CBE2A23CB2100D7E98C /*
AnyTransition+backslide.swift in Sources */,
@@ -1209,11 +1219,9 @@
4EB095152989CBB00043A8A1 /*
Model+Settings.swift in Sources */,
4EC4008D2AE5664100DF72C7 /*
CharacterSet+contains.swift in Sources */,
4EB095692989CBFE0043A8A1 /* ErrorView.swift in
Sources */,
- 4E2E5F802AEE30DA0027FA8A /* AmountRowV.swift in
Sources */,
4E3B4BC72A429F2A00CC88B8 /*
View+Notification.swift in Sources */,
4EB0956E2989CBFE0043A8A1 /* Model+Pending.swift
in Sources */,
4EB095522989CBFE0043A8A1 /*
ExchangeListView.swift in Sources */,
- 4EB095642989CBFE0043A8A1 /*
WithdrawProgressView.swift in Sources */,
E37AA62F2AF19BE0003850CF /* RefundURIView.swift
in Sources */,
4EEC157A29F9427F00D46A03 /* QRSheet.swift in
Sources */,
4E3B4BC12A41E6C200CC88B8 /*
P2pReceiveURIView.swift in Sources */,
@@ -1221,7 +1229,6 @@
4EB095582989CBFE0043A8A1 /*
TransactionDetailView.swift in Sources */,
4EB095202989CBCB0043A8A1 /* WalletCore.swift in
Sources */,
4EB095672989CBFE0043A8A1 /*
LaunchAnimationView.swift in Sources */,
- 4EB095662989CBFE0043A8A1 /* SideBarView.swift
in Sources */,
E37AA62B2AF197E5003850CF /* Model+Refund.swift
in Sources */,
4EB095162989CBB00043A8A1 /* WalletModel.swift
in Sources */,
4EB0955A2989CBFE0043A8A1 /* URLSheet.swift in
Sources */,
@@ -1233,6 +1240,7 @@
4EB0950A2989CB7C0043A8A1 /* TalerStrings.swift
in Sources */,
4EA551252A2C923600FEC9A8 /*
CurrencyInputView.swift in Sources */,
4E363CBC2A237E0900D7E98C /* URL+id+iban.swift
in Sources */,
+ 4EEC3A722B2285A200D05F9D /*
WithdrawExchangeV.swift in Sources */,
4EDBDCDA2AB787CB00925C02 /* CallStack.swift in
Sources */,
4E9320452A1645B600A87B0E /*
RequestPayment.swift in Sources */,
4EB095502989CBFE0043A8A1 /* SettingsItem.swift
in Sources */,
@@ -1280,12 +1288,12 @@
4E3EAE872A990778009F1BE8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon2;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS =
"$(TARGET_NAME).entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 23;
+ CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = GUDDQ9428Y;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -1296,14 +1304,14 @@
INFOPLIST_KEY_NSHumanReadableCopyright = "©
Taler-Systems.com";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
- INFOPLIST_KEY_UISupportedInterfaceOrientations
= UIInterfaceOrientationPortrait;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations
= "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
UIInterfaceOrientationPortrait";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad =
"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 0.9.3;
+ MARKETING_VERSION = 0.9.4;
PRODUCT_BUNDLE_IDENTIFIER =
"com.taler-systems.talerwallet-1";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1311,6 +1319,7 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG
GNU_TALER PRINT_CHANGES";
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = 1;
VALIDATE_WORKSPACE = YES;
@@ -1320,13 +1329,13 @@
4E3EAE882A990778009F1BE8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon2;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME
= AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS =
"$(TARGET_NAME).entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 23;
+ CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = GUDDQ9428Y;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -1337,14 +1346,14 @@
INFOPLIST_KEY_NSHumanReadableCopyright = "©
Taler-Systems.com";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
- INFOPLIST_KEY_UISupportedInterfaceOrientations
= UIInterfaceOrientationPortrait;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations
= "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
UIInterfaceOrientationPortrait";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad =
"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 0.9.3;
+ MARKETING_VERSION = 0.9.4;
PRODUCT_BUNDLE_IDENTIFIER =
"com.taler-systems.talerwallet-1";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1352,6 +1361,7 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS =
"GNU_TALER PRINT_CHANGES";
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = 1;
VALIDATE_WORKSPACE = YES;
@@ -1479,12 +1489,12 @@
D14AFD4824D232B500C51073 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon2;
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon3;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS =
"$(TARGET_NAME).entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 31;
+ CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = GUDDQ9428Y;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -1495,14 +1505,14 @@
INFOPLIST_KEY_NSHumanReadableCopyright = "©
Taler-Systems.com";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
- INFOPLIST_KEY_UISupportedInterfaceOrientations
= UIInterfaceOrientationPortrait;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations
= "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
UIInterfaceOrientationPortrait";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad =
"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 0.9.3;
+ MARKETING_VERSION = 0.9.4;
PRODUCT_BUNDLE_IDENTIFIER =
"com.taler-systems.talerwallet-2";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1510,7 +1520,7 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
- SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG
TABBAR";
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG
TALER_WALLET";
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = 1;
VALIDATE_WORKSPACE = YES;
@@ -1520,13 +1530,13 @@
D14AFD4924D232B500C51073 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon2;
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon3;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME
= AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS =
"$(TARGET_NAME).entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 31;
+ CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = GUDDQ9428Y;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -1537,14 +1547,14 @@
INFOPLIST_KEY_NSHumanReadableCopyright = "©
Taler-Systems.com";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
- INFOPLIST_KEY_UISupportedInterfaceOrientations
= UIInterfaceOrientationPortrait;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations
= "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
UIInterfaceOrientationPortrait";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad =
"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 0.9.3;
+ MARKETING_VERSION = 0.9.4;
PRODUCT_BUNDLE_IDENTIFIER =
"com.taler-systems.talerwallet-2";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1552,7 +1562,7 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
- SWIFT_ACTIVE_COMPILATION_CONDITIONS = TABBAR;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS =
TALER_WALLET;
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = 1;
VALIDATE_WORKSPACE = YES;
@@ -1703,6 +1713,14 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
+ 4E2D8DD12B3F513800234039 /* XCRemoteSwiftPackageReference
"swift-markdown-ui-standalone" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL =
"https://github.com/Fesh-com/swift-markdown-ui-standalone";
+ requirement = {
+ branch = main;
+ kind = branch;
+ };
+ };
4E3EAE172A990778009F1BE8 /* XCRemoteSwiftPackageReference
"AnyCodable" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL =
"https://github.com/Flight-School/AnyCodable";
@@ -1735,6 +1753,14 @@
minimumVersion = 0.1.0;
};
};
+ 4EE1718C2B49EB7200BF9FF5 /* XCRemoteSwiftPackageReference
"swift-collections" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL =
"https://github.com/Fesh-com/swift-collections";
+ requirement = {
+ branch = main;
+ kind = branch;
+ };
+ };
4EEC157429F8ECBF00D46A03 /* XCRemoteSwiftPackageReference
"CodeScanner" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL =
"https://github.com/twostraws/CodeScanner";
@@ -1754,6 +1780,11 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
+ 4E2D8DD22B3F513800234039 /* MarkdownUI */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 4E2D8DD12B3F513800234039 /*
XCRemoteSwiftPackageReference "swift-markdown-ui-standalone" */;
+ productName = MarkdownUI;
+ };
4E3EAE152A990778009F1BE8 /* taler-swift */ = {
isa = XCSwiftPackageProductDependency;
productName = "taler-swift";
@@ -1778,6 +1809,21 @@
package = 4EB094FB29897D280043A8A1 /*
XCRemoteSwiftPackageReference "SymLog" */;
productName = SymLog;
};
+ 4EE171872B49635800BF9FF5 /* MarkdownUI */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 4E2D8DD12B3F513800234039 /*
XCRemoteSwiftPackageReference "swift-markdown-ui-standalone" */;
+ productName = MarkdownUI;
+ };
+ 4EE1718F2B49FE2B00BF9FF5 /* OrderedCollections */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 4EE1718C2B49EB7200BF9FF5 /*
XCRemoteSwiftPackageReference "swift-collections" */;
+ productName = OrderedCollections;
+ };
+ 4EE171912B49FE4E00BF9FF5 /* OrderedCollections */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 4EE1718C2B49EB7200BF9FF5 /*
XCRemoteSwiftPackageReference "swift-collections" */;
+ productName = OrderedCollections;
+ };
4EEC157529F8ECBF00D46A03 /* CodeScanner */ = {
isa = XCSwiftPackageProductDependency;
package = 4EEC157429F8ECBF00D46A03 /*
XCRemoteSwiftPackageReference "CodeScanner" */;
diff --git a/TalerWallet1/Assets.xcassets/AppIcon.appiconset/Contents.json
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/Contents.json
similarity index 80%
copy from TalerWallet1/Assets.xcassets/AppIcon.appiconset/Contents.json
copy to TalerWallet1/Assets.xcassets/AppIcon3.appiconset/Contents.json
index 2005c8a..dfcf466 100644
--- a/TalerWallet1/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/Contents.json
@@ -49,46 +49,55 @@
"size" : "60x60"
},
{
+ "filename" : "ipadNotification20.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
+ "filename" : "ipadNotification40.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
+ "filename" : "ipadSettings29.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
+ "filename" : "ipadSettings58.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
+ "filename" : "ipadSpotlight40.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
+ "filename" : "ipadSpotlight80.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
+ "filename" : "ipad76.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
+ "filename" : "ipad152.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
+ "filename" : "ipadPro167.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
@@ -100,51 +109,61 @@
"size" : "1024x1024"
},
{
+ "filename" : "mac16.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
+ "filename" : "mac32.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
+ "filename" : "mac32.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
+ "filename" : "mac64.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
+ "filename" : "mac128.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
+ "filename" : "mac256.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
+ "filename" : "mac256.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
+ "filename" : "mac512.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
+ "filename" : "mac512.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
+ "filename" : "mac1024.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/appstore1024.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/appstore1024.png
new file mode 100644
index 0000000..91c5976
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/appstore1024.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad152.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad152.png
new file mode 100644
index 0000000..a8422d2
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad152.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad76.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad76.png
new file mode 100644
index 0000000..0ecaea8
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad76.png differ
diff --git
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification20.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification20.png
new file mode 100644
index 0000000..199c374
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification20.png differ
diff --git
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification40.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification40.png
new file mode 100644
index 0000000..b4d12b4
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification40.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadPro167.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadPro167.png
new file mode 100644
index 0000000..c0d8790
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadPro167.png differ
diff --git
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings29.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings29.png
new file mode 100644
index 0000000..ce30c8f
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings29.png differ
diff --git
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings58.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings58.png
new file mode 100644
index 0000000..663db28
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings58.png differ
diff --git
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight40.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight40.png
new file mode 100644
index 0000000..b4d12b4
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight40.png differ
diff --git
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight80.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight80.png
new file mode 100644
index 0000000..e4121dd
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight80.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone120.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone120.png
new file mode 100644
index 0000000..907d180
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone120.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone180.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone180.png
new file mode 100644
index 0000000..cc557db
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone180.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac1024.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac1024.png
new file mode 100644
index 0000000..f6efbc0
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac1024.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac128.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac128.png
new file mode 100644
index 0000000..f89fd8d
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac128.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac16.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac16.png
new file mode 100644
index 0000000..eb73f4e
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac16.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac256.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac256.png
new file mode 100644
index 0000000..2d75a91
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac256.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac32.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac32.png
new file mode 100644
index 0000000..d736095
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac32.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac512.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac512.png
new file mode 100644
index 0000000..69fe35d
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac512.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac64.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac64.png
new file mode 100644
index 0000000..83d6cb8
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac64.png differ
diff --git
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification40.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification40.png
new file mode 100644
index 0000000..b4d12b4
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification40.png differ
diff --git
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification60.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification60.png
new file mode 100644
index 0000000..9ff0069
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification60.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings58.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings58.png
new file mode 100644
index 0000000..663db28
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings58.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings87.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings87.png
new file mode 100644
index 0000000..27a66ff
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings87.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight120.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight120.png
new file mode 100644
index 0000000..907d180
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight120.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight80.png
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight80.png
new file mode 100644
index 0000000..e4121dd
Binary files /dev/null and
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight80.png differ
diff --git a/TalerWallet1/Backend/WalletBackendRequest.swift
b/TalerWallet1/Backend/WalletBackendRequest.swift
index 293269c..9a95db2 100644
--- a/TalerWallet1/Backend/WalletBackendRequest.swift
+++ b/TalerWallet1/Backend/WalletBackendRequest.swift
@@ -31,8 +31,8 @@ struct ScopeInfo: Codable, Hashable {
case auditor
}
var type: ScopeInfoType
- var url: String? // only for "exchange"
- var currency: String
+ var url: String? // only for "exchange"
+ var currency: String // 3-char ISO 4217 code for global currency.
Regional MUST be >= 4 letters
public static func == (lhs: ScopeInfo, rhs: ScopeInfo) -> Bool {
if let lhsBaseURL = lhs.url {
diff --git a/TalerWallet1/Backend/WalletCore.swift
b/TalerWallet1/Backend/WalletCore.swift
index 76ba1ab..f081aac 100644
--- a/TalerWallet1/Backend/WalletCore.swift
+++ b/TalerWallet1/Backend/WalletCore.swift
@@ -153,7 +153,7 @@ extension WalletCore {
userInfo: [AnyHashable: Any]? = nil) {
Task { // runs on MainActor
await postNotificationM(aName, object: anObject, userInfo:
userInfo)
- logger.info("Notification sent: \(aName.rawValue)")
+// logger.info("Notification sent: \(aName.rawValue, privacy:
.public)")
}
}
@@ -193,26 +193,43 @@ extension WalletCore {
if components.count >= 3 { // txn:$txtype:$uid
if let type = TransactionType(rawValue: components[1]) {
guard type != .refresh else { return }
- switch decoded.newTxState.major {
+ let newMajor = decoded.newTxState.major
+ let newMinor = decoded.newTxState.minor
+ switch newMajor {
case .done:
logger.info("Done: \(decoded.transactionId,
privacy: .private(mask: .hash))")
- Controller.shared.playSound(type.isIncoming ? 2 :
1)
+ if type.isWithdrawal {
+ Controller.shared.playSound(2) //
payment_received only for withdrawals
+ } else if !type.isIncoming {
+ Controller.shared.playSound(1) //
payment_sent for all outgoing tx
+ }
postNotification(.TransactionDone, userInfo:
[TRANSACTIONTRANSITION: decoded])
return
+ case .aborting:
+ if let newMinor {
+ if newMinor == .refreshExpired {
+ logger.warning("RefreshExpired:
\(decoded.transactionId, privacy: .private(mask: .hash))")
+ Controller.shared.playSound(0)
+ postNotification(.TransactionExpired,
userInfo: [TRANSACTIONTRANSITION: decoded])
+ return
+ }
+ }
+ logger.warning("Unknown aborting:
\(decoded.transactionId, privacy: .private(mask: .hash))")
+ postNotification(.TransactionStateTransition,
userInfo: [TRANSACTIONTRANSITION: decoded])
case .expired:
logger.warning("Expired: \(decoded.transactionId,
privacy: .private(mask: .hash))")
Controller.shared.playSound(0)
postNotification(.TransactionExpired, userInfo:
[TRANSACTIONTRANSITION: decoded])
return
case .pending:
- if let newMinor = decoded.newTxState.minor {
+ if let newMinor {
if newMinor == .ready {
logger.log("PendingReady:
\(decoded.transactionId, privacy: .private(mask: .hash))")
postNotification(.PendingReady, userInfo:
[TRANSACTIONTRANSITION: decoded])
return
} else if newMinor == .exchangeWaitReserve
// user did confirm on bank website
|| newMinor == .withdrawCoins {
// coin-withdrawal has started
- logger.log("DismissSheet:
\(decoded.transactionId, privacy: .private(mask: .hash))")
+// logger.log("DismissSheet:
\(decoded.transactionId, privacy: .private(mask: .hash))")
postNotification(.DismissSheet, userInfo:
[TRANSACTIONTRANSITION: decoded])
return
} else if newMinor == .kyc { // user did
confirm on bank website, but KYC is needed
@@ -220,11 +237,17 @@ extension WalletCore {
postNotification(.KYCrequired, userInfo:
[TRANSACTIONTRANSITION: decoded])
return
}
+ logger.log("Pending:\(newMinor.rawValue,
privacy: .public) \(decoded.transactionId, privacy: .private(mask: .hash))")
+ } else {
+ logger.log("Pending: \(decoded.transactionId,
privacy: .private(mask: .hash))")
}
- logger.log("Pending: \(decoded.transactionId,
privacy: .private(mask: .hash))")
postNotification(.TransactionStateTransition,
userInfo: [TRANSACTIONTRANSITION: decoded])
default:
- logger.warning("Unknow transition:
\(decoded.transactionId, privacy: .private(mask: .hash))")
+ if let newMinor {
+ logger.warning("\(newMajor.rawValue, privacy:
.public):\(newMinor.rawValue, privacy: .public) \(decoded.transactionId,
privacy: .private(mask: .hash))")
+ } else {
+ logger.warning("\(newMajor.rawValue, privacy:
.public): \(decoded.transactionId, privacy: .private(mask: .hash))")
+ }
postNotification(.TransactionStateTransition,
userInfo: [TRANSACTIONTRANSITION: decoded])
} // switch
} // type
@@ -345,6 +368,7 @@ print("\n❗️ WalletCore.swift:251 Notification: ",
anyPayload, "\n") /
self.requestsMade += 1
self.semaphore.signal() // free requestsMade
self.symLog.log(jsonString)
+ self.logger.log("sendRequest \(requestId, privacy: .public):
\(request.operation, privacy: .public)")
self.quickjs.sendMessage(message: jsonString)
} catch { // call completion
self.semaphore.signal()
@@ -364,7 +388,7 @@ extension WalletCore {
sendRequest(request: reqData) { requestId, timeSent, result, error
in
let timeUsed = Date.now - timeSent
let millisecs = timeUsed.milliseconds
- self.logger.info("Request \(requestId) took \(millisecs) ms")
+ self.logger.info("Request \(requestId, privacy: .public) took
\(millisecs, privacy: .public) ms")
var err: Error? = nil
if let json = result {
do {
diff --git a/TalerWallet1/Controllers/Controller.swift
b/TalerWallet1/Controllers/Controller.swift
index 05f5d77..78a3686 100644
--- a/TalerWallet1/Controllers/Controller.swift
+++ b/TalerWallet1/Controllers/Controller.swift
@@ -35,9 +35,10 @@ class Controller: ObservableObject {
private let symLog = SymLogC()
@Published var backendState: BackendState = .none // only used for
launch animation
- @Published var currencyTicker: Int = 0
+ @Published var currencyTicker: Int = 0 // updates
whenever a new currency is added
@AppStorage("useHaptics") var useHaptics: Bool = true // extension
mustn't define this, so it must be here
- @AppStorage("playSounds") var playSounds: Int = 1 // extension
mustn't define this, so it must be here
+ @AppStorage("playSoundsI") var playSoundsI: Int = 1 // extension
mustn't define this, so it must be here
+ @AppStorage("playSoundsB") var playSoundsB: Bool = true
@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")
@@ -59,25 +60,63 @@ class Controller: ObservableObject {
currencyInfos = []
}
// MARK: -
- func hasInfo(for currency: String) -> Bool {
+ func hasInfo(for currency: String) -> CurrencyInfo? {
for info in currencyInfos {
if info.scope.currency == currency {
- return true
+ return info
}
}
- return false
+ return nil
}
+ func info(for currency: String) -> CurrencyInfo? {
+ for info in currencyInfos {
+ if info.scope.currency == currency {
+ return info
+ }
+ }
+ return nil
+ }
func info(for currency: String, _ ticker: Int) -> CurrencyInfo {
if ticker != currencyTicker {
print("❗️Yikes")
}
- for info in currencyInfos {
- if info.scope.currency == currency {
- return info
+ return info(for: currency) ?? CurrencyInfo.zero(currency)
+ }
+
+ @MainActor
+ func getInfo(from exchangeBaseUrl: String, model: WalletModel) async ->
CurrencyInfo? {
+ if let exchange = await model.getExchangeByUrl(url: exchangeBaseUrl) {
+// let scopeInfo = exchange.scopeInfo
+// if let info = hasInfo(for: scopeInfo.currency) {
+// return info
+// }
+// do {
+// let info = try await model.getCurrencyInfoM(scope:
scopeInfo, delay: 0)
+// await setInfo(info)
+// return info
+// } catch {
+// return nil
+// }
+
+ if let scopeInfo = exchange.scopeInfo {
+ if let info = hasInfo(for: scopeInfo.currency) {
+ return info
+ }
+ do {
+ let info = try await model.getCurrencyInfoM(scope:
scopeInfo, delay: 0)
+ await setInfo(info)
+ return info
+ } catch {
+ return nil
+ }
+ } else {
+ // TODO: Error "Can't get scopeInfo"
}
+ } else {
+ // TODO: Error "Can't get Exchange Info"
}
- return CurrencyInfo.zero(currency)
+ return nil
}
@MainActor
@@ -127,7 +166,7 @@ extension Controller {
symLog.log(url)
guard let scheme = url.scheme else {return UrlCommand.unknown}
var uncrypted = false
- switch scheme {
+ switch scheme.lowercased() {
case "taler+http":
uncrypted = true
fallthrough
@@ -157,7 +196,7 @@ extension Controller {
symLog.log("uncrypted: taler://\(command)")
// TODO: uncrypted
}
- switch command {
+ switch command.lowercased() {
case "withdraw":
return .withdraw
case "withdraw-exchange":
diff --git a/TalerWallet1/Controllers/DebugViewC.swift
b/TalerWallet1/Controllers/DebugViewC.swift
index fd6551b..33a15d5 100644
--- a/TalerWallet1/Controllers/DebugViewC.swift
+++ b/TalerWallet1/Controllers/DebugViewC.swift
@@ -110,7 +110,7 @@ struct DebugViewV: View {
@EnvironmentObject private var debugViewC: DebugViewC
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
// let _ = Self._printChanges()
// let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -161,10 +161,10 @@ class DebugViewC: ObservableObject {
if viewID == 0 {
logger.log("switching ON, \(newID, privacy: .public)")
viewID = newID // publish ON
- announce(this: "Current view is: \(newID).")
+// announce(this: "Current view is: \(newID).")
} else if viewID != newID {
logger.log("switching from \(self.viewID, privacy: .public) to
\(newID, privacy: .public)")
- announce(this: "View switched from \(self.viewID) to
\(newID).")
+// announce(this: "View switched from \(self.viewID) to
\(newID).")
viewID = newID // publish new
viewID
} else {
logger.log("\(newID, privacy: .public) stays")
diff --git a/TalerWallet1/Controllers/PublicConstants.swift
b/TalerWallet1/Controllers/PublicConstants.swift
index da8bb9a..248ce6c 100644
--- a/TalerWallet1/Controllers/PublicConstants.swift
+++ b/TalerWallet1/Controllers/PublicConstants.swift
@@ -24,8 +24,10 @@ public let DONE_OUTGOING = "minus.circle.fill"
public let HTTPS = "https://"
//public let DEMOBANK = HTTPS + "bAnK.dEmO.tAlEr.nEt" // should be
weird to read, but still work
//public let DEMOEXCHANGE = HTTPS + "eXcHaNgE.dEmO.tAlEr.nEt"
-public let DEMO = ".demo.taler.net"
-public let TEST = ".test.taler.net"
+public let TALER = "taler.net"
+public let TALER_NET = HTTPS + TALER
+public let DEMO = ".demo." + TALER
+public let TEST = ".test." + TALER
public let DEMOBANK = HTTPS + "bank" + DEMO
public let DEMOSHOP = HTTPS + "shop" + DEMO
diff --git a/TalerWallet1/Controllers/TalerWallet1App.swift
b/TalerWallet1/Controllers/TalerWallet1App.swift
index 043d646..694e05c 100644
--- a/TalerWallet1/Controllers/TalerWallet1App.swift
+++ b/TalerWallet1/Controllers/TalerWallet1App.swift
@@ -104,7 +104,7 @@ final class ViewState : ObservableObject {
@Published var rootViewId = UUID()
let logger = Logger(subsystem: "net.taler.gnu", category: "ViewState")
- public func popToRootView() -> Void {
+ public func popToRootView(_ stack: CallStack) -> Void {
logger.info("popToRootView")
rootViewId = UUID() // setting a new ID will cause tableView
popToRootView behaviour
}
diff --git a/TalerWallet1/Helper/Controller+playSound.swift
b/TalerWallet1/Helper/Controller+playSound.swift
index 7e76e36..c86858b 100644
--- a/TalerWallet1/Helper/Controller+playSound.swift
+++ b/TalerWallet1/Helper/Controller+playSound.swift
@@ -8,6 +8,32 @@ import UIKit
extension Controller {
+ @MainActor
+ func hapticNotification(_ feedbackType:
UINotificationFeedbackGenerator.FeedbackType) {
+ if useHaptics {
+ /// call when a notification is displayed, passing the
corresponding type
+
UINotificationFeedbackGenerator().notificationOccurred(feedbackType)
+ }
+ }
+
+ @MainActor
+ func hapticFeedback(_ feedbackStyle:
UIImpactFeedbackGenerator.FeedbackStyle) {
+ /// call when your UI element impacts something else
+ UIImpactFeedbackGenerator(style: feedbackStyle).impactOccurred()
+ }
+
+ @MainActor
+ func hapticFeedback(intensity: CGFloat) {
+ /// call when your UI element impacts something else with a specific
intensity [0.0, 1.0]
+ UIImpactFeedbackGenerator().impactOccurred(intensity: intensity)
+ }
+
+ @MainActor
+ func hapticFeedback() {
+ /// call when the selection changes (not on initial selection)
+ UISelectionFeedbackGenerator().selectionChanged()
+ }
+
/// 0 = failure, 1 = received, 2 = sent
@MainActor func playSound(_ number: Int) {
let sysVolume = AVAudioSession.sharedInstance().outputVolume
@@ -31,7 +57,6 @@ extension Controller {
// logger.log("❗️sys:\(sysVolume) vol:\(volume)")
var soundID: SystemSoundID = 0
- let notificationGenerator = useHaptics ?
UINotificationFeedbackGenerator() : nil
if number > 9 {
soundID = UInt32(number)
} else {
@@ -43,9 +68,9 @@ extension Controller {
AudioServicesCreateSystemSoundID(fileURL as CFURL, &soundID)
logger.log("\(sound, privacy: .public) \(soundID)")
}
- if number == 0 || number > 9 || playSounds < 0 {
+ if number == 0 || number > 9 || playSoundsI < 0 {
AudioServicesPlaySystemSound(soundID);
- } else if playSounds > 0 {
+ } else if playSoundsI > 0 && playSoundsB {
if let url = Bundle.main.url(forResource: (number == 1) ?
"payment_sent"
:
"payment_received",
withExtension: "m4a") {
@@ -58,9 +83,6 @@ extension Controller {
AudioServicesPlaySystemSound(soundID);
}
}
- if let notificationGenerator {
- notificationGenerator.notificationOccurred(number == 0 ? .error :
.success)
- }
-
+ hapticNotification(number == 0 ? .error : .success)
}
}
diff --git a/TalerWallet1/Helper/CurrencySpecification.swift
b/TalerWallet1/Helper/CurrencySpecification.swift
index 5b15d9a..5422183 100644
--- a/TalerWallet1/Helper/CurrencySpecification.swift
+++ b/TalerWallet1/Helper/CurrencySpecification.swift
@@ -5,6 +5,20 @@
import Foundation
import taler_swift
+extension Locale {
+ static var preferredLanguageCode: String {
+ guard let preferredLanguage = preferredLanguages.first,
+ let code = Locale(identifier: preferredLanguage).languageCode
else {
+ return "en"
+ }
+ return code
+ }
+
+ static var preferredLanguageCodes: [String] {
+ return Locale.preferredLanguages.compactMap({Locale(identifier:
$0).languageCode})
+ }
+}
+
extension Locale {
func leadingCurrencySymbol() -> Bool {
let currencyFormatter = NumberFormatter()
@@ -56,7 +70,6 @@ public struct CurrencyInfo {
public static func zero(_ currency: String) -> CurrencyInfo {
let scope = ScopeInfo(type: .global, currency: currency)
let specs = CurrencySpecification(name: currency,
- code: currency,
fractionalInputDigits: 0,
fractionalNormalDigits: 0,
fractionalTrailingZeroDigits: 0,
@@ -155,7 +168,6 @@ public struct CurrencySpecification2: Codable, Sendable {
public struct CurrencySpecification: Codable, Sendable {
enum CodingKeys: String, CodingKey {
case name = "name"
- case code = "code"
case fractionalInputDigits = "num_fractional_input_digits"
case fractionalNormalDigits = "num_fractional_normal_digits"
case fractionalTrailingZeroDigits =
"num_fractional_trailing_zero_digits"
@@ -163,8 +175,6 @@ public struct CurrencySpecification: Codable, Sendable {
}
/// some name for this CurrencySpecification
let name: String
- /// 3-char ISO 4217 code for global currency. Regional MUST be >= 4 letters
- let code: String?
/// how much digits the user may enter after the decimal separator
let fractionalInputDigits: Int
/// €,$,£: 2; some arabic currencies: 3, ¥: 0
@@ -199,7 +209,7 @@ public class CurrencyFormatter: NumberFormatter {
self.hasAltUnitName0 = false
self.leadingCurrencySymbol = false
super.init()
- self.locale = Locale.current
+ self.locale = Locale.autoupdatingCurrent
self.usesGroupingSeparator = true
self.numberStyle = .currencyISOCode // .currency
self.maximumFractionDigits = 8 // ensure that formatter
will not round
@@ -243,3 +253,17 @@ public class CurrencyFormatter: NumberFormatter {
fatalError("init(coder:) has not been implemented")
}
}
+// MARK: -
+#if DEBUG
+func PreviewCurrencyInfo(_ currency: String, digits: Int) -> CurrencyInfo {
+ let unitName = digits == 0 ? "テ" : "ク" // do not use real currency
symbols like "¥" : "€"
+ let scope = ScopeInfo(type: .global, currency: currency)
+ let specs = CurrencySpecification(name: currency,
+ fractionalInputDigits: digits,
+ fractionalNormalDigits: digits,
+ fractionalTrailingZeroDigits: digits,
+ altUnitNames: [0 : unitName])
+ let previewFormatter = CurrencyFormatter.formatter(scope: scope, specs:
specs)
+ return CurrencyInfo(scope: scope, specs: specs, formatter:
previewFormatter)
+}
+#endif
diff --git a/TalerWallet1/Helper/TalerDater.swift
b/TalerWallet1/Helper/TalerDater.swift
index 427e0f9..b3f5e19 100644
--- a/TalerWallet1/Helper/TalerDater.swift
+++ b/TalerWallet1/Helper/TalerDater.swift
@@ -80,6 +80,15 @@ public class TalerDater: DateFormatter {
}
}
+ public static func accessibilityDate(_ date: Date?) -> String? {
+ if let date {
+ let formatted = date.formatted(date: .long, time: .shortened)
+// print(formatted)
+ return formatted
+ }
+ return nil
+ }
+
public static func dateString() -> String {
return shared.string(from: Date())
}
diff --git a/TalerWallet1/Helper/URL+id+iban.swift
b/TalerWallet1/Helper/URL+id+iban.swift
index ca4689c..643c493 100644
--- a/TalerWallet1/Helper/URL+id+iban.swift
+++ b/TalerWallet1/Helper/URL+id+iban.swift
@@ -23,11 +23,11 @@ extension URL {
}
/// SwifterSwift: Dictionary of the URL's query parameters.
- var queryParameters: [String: String]? {
+ var queryParameters: [String:String]? {
guard let components = URLComponents(url: self,
resolvingAgainstBaseURL: false),
let queryItems = components.queryItems else { return nil }
- var items: [String: String] = [:]
+ var items: [String:String] = [:]
for queryItem in queryItems {
items[queryItem.name] = queryItem.value
diff --git a/TalerWallet1/Helper/View+dismissTop.swift
b/TalerWallet1/Helper/View+dismissTop.swift
index 3d11bd0..2e2a92a 100644
--- a/TalerWallet1/Helper/View+dismissTop.swift
+++ b/TalerWallet1/Helper/View+dismissTop.swift
@@ -35,12 +35,31 @@ extension View {
if gotPresented {
topController.dismiss(animated: animated)
} else {
- print("Yikes❗️ Trying to dismiss the rootViewController!")
+ Self.findNavigationController(viewController:
topController)?.popToRootViewController(animated: animated)
}
} else {
print("Yikes❗️ There is no window/rootViewController!")
}
}
+ @MainActor static func findNavigationController(viewController:
UIViewController?) -> UINavigationController? {
+ guard let viewController = viewController else {
+ return nil
+ }
+
+ if let navigationController = viewController as? UITabBarController {
+ return findNavigationController(viewController:
navigationController.selectedViewController)
+ }
+
+ if let navigationController = viewController as?
UINavigationController {
+ return navigationController
+ }
+
+ for childViewController in viewController.children {
+ return findNavigationController(viewController:
childViewController)
+ }
+
+ return nil
+ }
}
// MARK: -
extension View {
diff --git a/TalerWallet1/Helper/WalletColors.swift
b/TalerWallet1/Helper/WalletColors.swift
index a358caf..11e192e 100644
--- a/TalerWallet1/Helper/WalletColors.swift
+++ b/TalerWallet1/Helper/WalletColors.swift
@@ -4,15 +4,21 @@
*/
import SwiftUI
+fileprivate let grouped = true
+
public struct WalletColors {
let tint = Color(.tintColor)
- let gray1 = Color(.systemGray)
- let gray2 = Color(.systemGray2)
+ let gray1 = Color(.systemGray) // uncompleted
+ let gray2 = Color(.systemGray2) // disabled Fore
let gray3 = Color(.systemGray3)
let gray4 = Color(.systemGray4)
let gray5 = Color(.systemGray5)
let gray6 = Color(.systemGray6)
+ let gray7 = grouped ? Color(.tertiarySystemGroupedBackground)
+ : Color(.tertiarySystemBackground) // enabled
Back
+ let gray8 = grouped ? Color(.secondarySystemGroupedBackground)
+ : Color(.secondarySystemBackground) // disabled
Back
func buttonForeColor(pressed: Bool, disabled: Bool,
prominent: Bool = false, balance: Bool = false) ->
Color {
@@ -25,27 +31,29 @@ public struct WalletColors {
func buttonBackColor(pressed: Bool, disabled: Bool,
prominent: Bool = false, balance: Bool = false) ->
Color {
balance ? (pressed ? gray5 : gray6)
- : disabled ? gray5
+ : disabled ? gray7
: prominent ? tint
- : pressed ? gray5 : gray4
+ : pressed ? gray4 : gray5
}
var backgroundColor: Color {
- gray6
+ grouped ? Color(.systemGroupedBackground)
+ : Color(.systemBackground)
}
var sideBackground: Color {
- gray5
+ gray6
}
var fieldForeground: Color { // text color
Color.primary
}
var fieldBackground: Color {
- Color(.systemBackground)
+ gray8
}
- var incompleteColor: Color {
+ var uncompletedColor: Color {
+ // used in TransactionRowView
gray1
}
func pendingColor(_ incoming: Bool) -> Color {
diff --git a/TalerWallet1/Model/Model+Exchange.swift
b/TalerWallet1/Model/Model+Exchange.swift
index 4764116..5b362f0 100644
--- a/TalerWallet1/Model/Model+Exchange.swift
+++ b/TalerWallet1/Model/Model+Exchange.swift
@@ -175,7 +175,7 @@ extension WalletModel {
async -> Exchange? {
do {
let request = GetExchangeByUrl(exchangeBaseUrl: url)
- logger.info("query for exchange: \(url, privacy: .public)")
+// logger.info("query for exchange: \(url, privacy: .public)")
let response = try await sendRequest(request)
return response
} catch {
@@ -199,7 +199,8 @@ extension WalletModel {
_ = try await sendRequest(request)
}
- func getCurrencyInfo(scope: ScopeInfo, delay: UInt = 0)
+ @MainActor
+ func getCurrencyInfoM(scope: ScopeInfo, delay: UInt = 0)
async throws -> CurrencyInfo {
let request = GetCurrencySpecification(scope: scope)
let response = try await sendRequest(request, ASYNCDELAY + delay)
diff --git a/TalerWallet1/Model/Model+P2P.swift
b/TalerWallet1/Model/Model+P2P.swift
index bff8aeb..28ad60d 100644
--- a/TalerWallet1/Model/Model+P2P.swift
+++ b/TalerWallet1/Model/Model+P2P.swift
@@ -41,10 +41,10 @@ extension WalletModel {
} // getMaxPeerPushAmountM
// - - - - - -
struct CheckPeerPushDebitResponse: Codable {
- let exchangeBaseUrl: String?
+ let exchangeBaseUrl: String? // API "2:0:1"
let amountRaw: Amount
let amountEffective: Amount
- let maxExpirationDate: Timestamp? // TODO: limit expiration (30
days or 7 days)
+ let maxExpirationDate: Timestamp? // API "2:0:1" TODO: limit
expiration (30 days or 7 days)
}
fileprivate struct CheckPeerPushDebit: WalletBackendFormattedRequest {
typealias Response = CheckPeerPushDebitResponse
@@ -166,7 +166,6 @@ struct PreparePeerPushCreditResponse: Codable {
let amountRaw: Amount
let amountEffective: Amount
let exchangeBaseUrl: String
-// let peerPushCreditId: String
// the dialog transaction is already created in the local DB - must either
accept or delete
let transactionId: String
}
diff --git a/TalerWallet1/Model/Model+Settings.swift
b/TalerWallet1/Model/Model+Settings.swift
index 288e22b..eb3b019 100644
--- a/TalerWallet1/Model/Model+Settings.swift
+++ b/TalerWallet1/Model/Model+Settings.swift
@@ -35,7 +35,7 @@ fileprivate struct WithdrawTestBalanceRequest:
WalletBackendFormattedRequest {
extension WalletModel {
@MainActor func loadTestKudosM(test: Bool)
async throws { // M for MainActor
- let amount = Amount(currency: test ? TESTCURRENCY : DEMOCURRENCY,
integer: 11, fraction: 0)
+ let amount = Amount(currency: test ? TESTCURRENCY : DEMOCURRENCY,
cent: 1100)
let request = WithdrawTestBalanceRequest(amount: amount,
// bankBaseUrl: test ?
TESTBANK : DEMOBANK,
corebankApiBaseUrl: test ?
TESTBANK : DEMOBANK,
@@ -80,8 +80,8 @@ fileprivate struct RunIntegrationTest:
WalletBackendFormattedRequest {
extension WalletModel {
@MainActor func runIntegrationTestM(newVersion: Bool, test: Bool)
async throws { // M for MainActor
- let amountW = Amount(currency: test ? TESTCURRENCY : DEMOCURRENCY,
integer: 3, fraction: 0)
- let amountS = Amount(currency: test ? TESTCURRENCY : DEMOCURRENCY,
integer: 1, fraction: 0)
+ let amountW = Amount(currency: test ? TESTCURRENCY : DEMOCURRENCY,
cent: 300)
+ let amountS = Amount(currency: test ? TESTCURRENCY : DEMOCURRENCY,
cent: 100)
let request = RunIntegrationTest(newVersion: newVersion,
exchangeBaseUrl: test ? TESTEXCHANGE
: DEMOEXCHANGE,
// bankBaseUrl: (test ? TESTBANK :
DEMOBANK),
@@ -92,4 +92,29 @@ extension WalletModel {
amountToSpend: amountS)
let _ = try await sendRequest(request, ASYNCDELAY)
}
+} // runIntegrationTestM()
+ // MARK: -
+/// A request to add a test balance to the wallet.
+fileprivate struct InfiniteTransactionLoop: WalletBackendFormattedRequest {
+ struct Response: Decodable {} // no result - getting no error back means
success
+ func operation() -> String { "testingInfiniteTransactionLoop" }
+ func args() -> Args { Args(delayMs: delayMs,
+ shouldFetch: shouldFetch)
+ }
+
+ let delayMs: Int32
+ let shouldFetch: Bool
+
+
+ struct Args: Encodable {
+ let delayMs: Int32
+ let shouldFetch: Bool
+ }
+}
+extension WalletModel {
+ func testingInfiniteTransaction(delayMs: Int32, shouldFetch: Bool)
+ async throws {
+ let request = InfiniteTransactionLoop(delayMs: delayMs, shouldFetch:
shouldFetch)
+ let _ = try await sendRequest(request, ASYNCDELAY)
+ }
} // runIntegrationTestM()
diff --git a/TalerWallet1/Model/Model+Withdraw.swift
b/TalerWallet1/Model/Model+Withdraw.swift
index 52f8355..a2d3ca9 100644
--- a/TalerWallet1/Model/Model+Withdraw.swift
+++ b/TalerWallet1/Model/Model+Withdraw.swift
@@ -7,17 +7,25 @@ import taler_swift
import SymLog
fileprivate let ASYNCDELAY: UInt = 0 //set e.g to 6 or 9 seconds for
debugging
-enum AccountRestrictionType: String, Decodable {
+enum AccountRestrictionType: String, Codable {
case deny
case regex
}
-struct AccountRestriction: Decodable {
+
+typealias HintDict = [String:String]
+
+struct AccountRestriction: Codable, Hashable {
var type: AccountRestrictionType
var payto_regex: String?
var human_hint: String?
- var human_hint_i18n: String?
+ var human_hint_i18n: HintDict?
}
+extension AccountRestriction: Identifiable {
+ var id: AccountRestriction {self}
+}
+
struct WithdrawalExchangeAccountDetails: Decodable {
+ var status: String
var bankName: String?
var paytoUri: String
var transferAmount: Amount
@@ -43,6 +51,23 @@ fileprivate struct GetWithdrawalDetailsForURI:
WalletBackendFormattedRequest {
}
}
// MARK: -
+/// The result from prepareWithdrawExchange
+struct WithdrawExchangeResponse: Decodable {
+ var exchangeBaseUrl: String
+ var amount: Amount?
+}
+/// A request to get an exchange's withdrawal details.
+fileprivate struct PrepareWithdrawExchange: WalletBackendFormattedRequest {
+ typealias Response = WithdrawExchangeResponse
+ func operation() -> String { "prepareWithdrawExchange" }
+ func args() -> Args { Args(talerUri: talerUri) }
+
+ var talerUri: String
+ struct Args: Encodable {
+ var talerUri: String
+ }
+}
+// MARK: -
/// The result from getWithdrawalDetailsForAmount
struct WithdrawalAmountDetails: Decodable {
var amountRaw: Amount // Amount that the user will transfer
to the exchange
@@ -75,20 +100,24 @@ struct ExchangeTermsOfService: Decodable {
var currentEtag: String
var acceptedEtag: String?
var tosStatus: ExchangeTosStatus
+ var tosAvailableLanguages: [String]
var contentType: String
+ var contentLanguage: String?
var content: String
}
/// A request to query an exchange's terms of service.
fileprivate struct GetExchangeTermsOfService: WalletBackendFormattedRequest {
typealias Response = ExchangeTermsOfService
func operation() -> String { "getExchangeTos" }
- func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl,
acceptedFormat: acceptedFormat) }
+ func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl,
acceptedFormat: acceptedFormat, acceptLanguage: acceptLanguage) }
var exchangeBaseUrl: String
var acceptedFormat: [String]?
+ var acceptLanguage: String?
struct Args: Encodable {
var exchangeBaseUrl: String
var acceptedFormat: [String]?
+ var acceptLanguage: String?
}
}
/// A request to mark an exchange's terms of service as accepted.
@@ -150,6 +179,14 @@ fileprivate struct AcceptManualWithdrawal:
WalletBackendFormattedRequest {
}
// MARK: -
extension WalletModel {
+ /// load withdraw-exchange details. Networking involved
+ @MainActor
+ func loadWithdrawalExchangeForUriM(_ talerUri: String) // M
for MainActor
+ async throws -> WithdrawExchangeResponse {
+ let request = PrepareWithdrawExchange(talerUri: talerUri)
+ let response = try await sendRequest(request, ASYNCDELAY)
+ return response
+ }
/// load withdrawal details. Networking involved
@MainActor
func loadWithdrawalDetailsForUriM(_ talerWithdrawUri: String)
// M for MainActor
@@ -167,9 +204,12 @@ extension WalletModel {
return response
}
@MainActor
- func loadExchangeTermsOfServiceM(_ exchangeBaseUrl: String,
acceptedFormat: [String]) // M for MainActor
+ func loadExchangeTermsOfServiceM(_ exchangeBaseUrl: String,
acceptedFormat: [String], acceptLanguage: String)
+ // M for MainActor
async throws -> ExchangeTermsOfService {
- let request = GetExchangeTermsOfService(exchangeBaseUrl:
exchangeBaseUrl, acceptedFormat: acceptedFormat)
+ let request = GetExchangeTermsOfService(exchangeBaseUrl:
exchangeBaseUrl,
+ acceptedFormat:
acceptedFormat,
+ acceptLanguage:
acceptLanguage)
let response = try await sendRequest(request, ASYNCDELAY)
return response
}
diff --git a/TalerWallet1/Model/Transaction.swift
b/TalerWallet1/Model/Transaction.swift
index 75c88a3..4fb492b 100644
--- a/TalerWallet1/Model/Transaction.swift
+++ b/TalerWallet1/Model/Transaction.swift
@@ -16,7 +16,7 @@ enum TransactionDecodingError: Error {
}
enum TransactionMinorState: String, Codable {
- // Placeholder until D37 is fully implemented
+ // Placeholder until D37 is fully implemented
case unknown
case deposit
case kyc // KycRequired
@@ -26,6 +26,7 @@ enum TransactionMinorState: String, Codable {
case submitPayment = "submit-payment"
case rebindSession = "rebind-session"
case refresh
+ case refreshExpired = "refresh-expired"
case pickup
case autoRefund = "auto-refund"
case user
@@ -50,6 +51,46 @@ enum TransactionMinorState: String, Codable {
case proposed
case refundAvailable = "refund-available"
case acceptRefund = "accept-refund"
+
+ var localizedState: String? {
+ switch self {
+ case .unknown: return self.rawValue
+ case .deposit: return self.rawValue
+ case .kyc: return String(localized:
"MinorState.kyc", defaultValue: "KYC required", comment: "TxMinorState heading")
+ case .aml: return String(localized:
"MinorState.aml", defaultValue: "AML required", comment: "TxMinorState heading")
+ case .mergeKycRequired: return String(localized:
"MinorState.mergekyc", defaultValue: "KYC merge", comment: "TxMinorState
heading")
+ case .track: return self.rawValue
+ case .submitPayment: return self.rawValue
+ case .rebindSession: return self.rawValue
+ case .refresh: return self.rawValue
+ case .refreshExpired: return self.rawValue
+ case .pickup: return self.rawValue
+ case .autoRefund: return self.rawValue
+ case .user: return self.rawValue
+ case .bank: return self.rawValue
+ case .exchange: return self.rawValue // in
aborted
+ case .claimProposal: return self.rawValue
+ case .checkRefund: return self.rawValue
+ case .createPurse: return self.rawValue
+ case .deletePurse: return self.rawValue
+ case .ready: return self.rawValue
+ case .merge: return self.rawValue
+ case .repurchase: return self.rawValue
+ case .bankRegisterReserve: return self.rawValue
+ case .bankConfirmTransfer: return String(localized:
"MinorState.bankConfirmTransfer", defaultValue: "Waiting for bank transfer",
comment: "TxMinorState heading")
+ case .withdrawCoins: return self.rawValue
+ case .exchangeWaitReserve: return self.rawValue
+ case .abortingBank: return self.rawValue
+ case .aborting: return self.rawValue
+ case .refused: return self.rawValue
+ case .withdraw: return self.rawValue
+ case .merchantOrderProposed: return self.rawValue
+ case .proposed: return self.rawValue
+ case .refundAvailable: return self.rawValue
+ case .acceptRefund: return self.rawValue
+// default: return nil
+ }
+ }
}
enum TransactionMajorState: String, Codable {
@@ -222,7 +263,7 @@ struct TransactionCommon: Decodable, Sendable {
do {
return try Amount.diff(amountEffective, amountRaw)
} catch {}
- return Amount(currency: amountRaw.currencyStr, integer: 0, fraction: 0)
+ return Amount.zero(currency: amountRaw.currencyStr)
}
func incoming() -> Bool {
@@ -245,8 +286,7 @@ struct WithdrawalDetails: Decodable {
var reserveIsReady: Bool
/// Details for manual withdrawals:
- /// The payto URIs that the exchange supports.
- var exchangePaytoUris: [String]?
+ var exchangeCreditAccountDetails: [WithdrawalExchangeAccountDetails]?
/// Details for bank-integrated withdrawals:
/// Whether the bank has confirmed the withdrawal.
@@ -265,13 +305,20 @@ struct WithdrawalTransaction : Sendable{
var details: WithdrawalTransactionDetails
}
+struct RefundInfo: Decodable {
+ var amountEffective: Amount
+ var amountRaw: Amount
+ var transactionId: String
+ var timestamp: Timestamp
+}
+
struct PaymentTransactionDetails: Decodable {
var info: OrderShortInfo
var proposalId: String
var totalRefundRaw: Amount
var totalRefundEffective: Amount
var refundPending: Amount?
- var refunds: [String]? // array of refund txIDs for this payment
+ var refunds: [RefundInfo]? // array of refund txIDs for this
payment
var refundQueryActive: Bool?
var posConfirmation: String?
}
@@ -313,15 +360,24 @@ enum RefreshReason: String, Decodable {
case refund
case abortPay = "abort-pay"
case abortDeposit = "abort-deposit"
+ case abortPeerPushDebit = "abort-peer-push-debit"
case recoup
case backupRestored = "backup-restored"
case scheduled
}
+struct RefreshError: Decodable {
+ var code: Int
+ var when: Timestamp
+ var hint: String
+ var numErrors: Int
+ var errors: [HTTPError]
+}
struct RefreshTransactionDetails: Decodable {
var refreshReason: RefreshReason
var originatingTransactionId: String?
var refreshInputAmount: Amount
var refreshOutputAmount: Amount
+ var error: RefreshError?
}
struct RefreshTransaction : Sendable{
@@ -438,8 +494,8 @@ enum Transaction: Decodable, Hashable, Identifiable,
Sendable {
var isP2pIncoming: Bool { isSendInvoice || isRcvCoins}
var isPending : Bool { common.txState.major == .pending }
- var isPendingReady : Bool { common.txState.minor == .ready }
- var isPendingKYC : Bool { common.txState.minor == .kyc }
+ var isPendingReady : Bool { isPending && common.txState.minor == .ready }
+ var isPendingKYC : Bool { isPending && common.txState.minor == .kyc }
var isDone : Bool { common.txState.major == .done }
var isAborting : Bool { common.txState.major == .aborting }
var isAborted : Bool { common.txState.major == .aborted }
@@ -452,6 +508,7 @@ enum Transaction: Decodable, Hashable, Identifiable,
Sendable {
var isAbortable : Bool { common.txActions.contains(.abort) }
var isFailable : Bool { common.txActions.contains(.fail) }
var isDeleteable : Bool { common.txActions.contains(.delete) }
+ var isRetryable : Bool { common.txActions.contains(.retry) }
var isResumable : Bool { common.txActions.contains(.resume) }
var isSuspendable: Bool { common.txActions.contains(.suspend) }
diff --git a/TalerWallet1/Model/WalletModel.swift
b/TalerWallet1/Model/WalletModel.swift
index 288f35d..b309c38 100644
--- a/TalerWallet1/Model/WalletModel.swift
+++ b/TalerWallet1/Model/WalletModel.swift
@@ -158,7 +158,7 @@ extension WalletModel {
// MARK: -
/// A request to initialize Wallet-core
fileprivate struct ResetRequest: WalletBackendFormattedRequest {
- func operation() -> String { "reset" }
+ func operation() -> String { "clearDb" }
func args() -> Args { Args() }
struct Args: Encodable {} // no arguments needed
diff --git a/TalerWallet1/Quickjs/quickjs.swift
b/TalerWallet1/Quickjs/quickjs.swift
index 54f42bd..89728ac 100644
--- a/TalerWallet1/Quickjs/quickjs.swift
+++ b/TalerWallet1/Quickjs/quickjs.swift
@@ -23,20 +23,21 @@ func logging_callback(userdata:
Optional<UnsafeMutableRawPointer>,
tag: Optional<UnsafePointer<Int8>>,
message: Optional<UnsafePointer<Int8>>) {
let native = Unmanaged<Quickjs>.fromOpaque(userdata!).takeUnretainedValue()
+ let logger = native.logger
let theTag = String(cString: tag!)
let theMessage = String(cString: message!)
switch level {
case TALER_WALLET_LOG_ERROR:
- native.logger.error("\(theTag, privacy: .public) \(theMessage,
privacy: .public)")
+ logger.error("\(theTag, privacy: .public) \(theMessage, privacy:
.public)")
case TALER_WALLET_LOG_WARN:
- native.logger.warning("\(theTag, privacy: .public) \(theMessage,
privacy: .public)")
+ logger.warning("\(theTag, privacy: .public) \(theMessage,
privacy: .public)")
case TALER_WALLET_LOG_MESSAGE:
- native.logger.notice("\(theTag, privacy: .public) \(theMessage,
privacy: .public)")
+ logger.notice("\(theTag, privacy: .public) \(theMessage, privacy:
.public)")
case TALER_WALLET_LOG_INFO:
- native.logger.info("\(theTag, privacy: .public) \(theMessage,
privacy: .public)")
+ logger.info("\(theTag, privacy: .public) \(theMessage, privacy:
.public)")
case TALER_WALLET_LOG_TRACE:
- native.logger.trace("\(theTag, privacy: .public) \(theMessage,
privacy: .public)")
+ logger.trace("\(theTag, privacy: .public) \(theMessage, privacy:
.public)")
default: break
}
}
diff --git a/TalerWallet1/Views/Balances/BalanceRowView.swift
b/TalerWallet1/Views/Balances/BalanceRowView.swift
index c851611..130c379 100644
--- a/TalerWallet1/Views/Balances/BalanceRowView.swift
+++ b/TalerWallet1/Views/Balances/BalanceRowView.swift
@@ -5,38 +5,53 @@
import SwiftUI
import taler_swift
-struct BalanceButton: View {
- let amountStr: String
+struct BalanceCell: View {
+ let amount: Amount
let sizeCategory: ContentSizeCategory
let rowAction: () -> Void
+ let balanceDest: LazyView<TransactionsListView>?
@Environment(\.colorSchemeContrast) private var colorSchemeContrast
@AppStorage("iconOnly") var iconOnly: Bool = false
+ /// Renders the Balance button. "Balance" leading, amountStr trailing. If
it doesn't fit in one row then
+ /// amount (trailing) goes underneath "Balance" (leading).
var body: some View {
- let balanceTitleStr = String(localized: "Balance:", comment: "Main
view")
- Button(action: rowAction) {
- SingleAxisGeometryReader { width in // e.g. 301 instead of
313 => padding = 6
- Group {
- let title = iconOnly ? "" : balanceTitleStr
- let titles = [(balanceTitleStr, TalerFont.uiFont(.title2)),
- (amountStr, TalerFont.uiFont(.title))]
- let fitsSideBySide = iconOnly ||
Self.fitsSideBySide(titles, availableWidth: width,
-
sizeCategory: sizeCategory,
- padding: 20,
sameSize: false)
- AmountRowV(amountStr: amountStr, amountColor: .primary,
doneOrPending: true, largeAmountFont: true,
- fitsHorizontal: fitsSideBySide, vertAlignment:
.lastTextBaseline) {
- Text(title)
- .accessibilityFont(.title2)
- .foregroundColor(colorSchemeContrast == .increased
? .primary : .secondary)
- }
+ let amountV = AmountV(amount: amount, large: true)
+ .foregroundColor(.primary)
+ let hLayout = HStack(spacing: 0) {
+ Spacer(minLength: 0)
+ amountV
+ }
+ let balanceCell = Group {
+ if iconOnly {
+ hLayout
+ } else {
+ let balanceText = Text("Balance:", comment: "Main view")
+ .accessibilityFont(.title2)
+ .foregroundColor(colorSchemeContrast == .increased ?
.primary : .secondary)
+ let vLayout = VStack(alignment: .leading, spacing: 0) {
+ balanceText
+ hLayout
}
+
+ if #available(iOS 16.0, *) {
+ ViewThatFits(in: .horizontal) {
+ HStack(spacing: HSPACING) {
+ balanceText
+ hLayout
+ }
+ vLayout
+ }
+ } else { vLayout } // view for iOS 15
}
- } .disabled(false)
- .buttonStyle(TalerButtonStyle(type: iconOnly ? .plain : .balance,
aligned: .trailing))
- .accessibilityElement(children: .combine)
- .accessibilityHint("Will go to main transactions list.")
+ }
+ NavigationLink { balanceDest } label: {
+ balanceCell
+ .accessibilityElement(children: .combine)
+ .accessibilityHint("Will go to main transactions list.")
// .accessibilityLabel(balanceTitleStr + " " + amountStr) //
TODO: CurrencyFormatter!
+ }
}
}
@@ -45,10 +60,12 @@ struct BalanceButton: View {
/// Balance: amount
/// [Send Money] [Request Payment]
struct BalanceRowView: View {
+ let stack: CallStack
let amount: Amount
let sendAction: () -> Void
let recvAction: () -> Void
let rowAction: () -> Void
+ let balanceDest: LazyView<TransactionsListView>?
@Environment(\.sizeCategory) var sizeCategory
@EnvironmentObject private var controller: Controller
@@ -65,12 +82,12 @@ struct BalanceRowView: View {
let requestTitle1 = String(localized: "RequestButton_Full", defaultValue:
"Request\tPayment",
comment: "`Request Payment´ in Balances - set
exactly 1 \\t for line break")
var body: some View {
- VStack (alignment: .trailing) {
- let currencyInfo = controller.info(for: amount.currencyStr,
controller.currencyTicker)
- let amountStr = amount.string(currencyInfo)
- BalanceButton(amountStr: amountStr,
- sizeCategory: sizeCategory,
- rowAction: rowAction)
+ VStack (alignment: .trailing, spacing: 6) {
+ BalanceCell(amount: amount,
+ sizeCategory: sizeCategory,
+ rowAction: rowAction,
+ balanceDest: balanceDest)
+// .border(.red)
let sendTitle = iconOnly ? sendTitle0 : sendTitle1
let requTitle = iconOnly ? requestTitle0 : requestTitle1
@@ -86,7 +103,9 @@ struct BalanceRowView: View {
HStack(spacing: HSPACING) {
twoRowButtons.makeCopy(fitsSideBySide: true)
}
+// .border(.red)
VStack { twoRowButtons }
+// .border(.red)
}
} else { // view for iOS 15
VStack { twoRowButtons }
@@ -102,14 +121,14 @@ struct BalanceRowView_Previews: PreviewProvider {
var body: some View {
let test = Amount(currency: TESTCURRENCY, cent: 123)
let demo = Amount(currency: DEMOCURRENCY, cent: 123456)
-// let testStr = test.string(testInfo)
-// let demoStr = demo.string(demoInfo)
List {
Section {
- BalanceRowView(amount: demo, sendAction: {}, recvAction: {},
rowAction: {})
+ BalanceRowView(stack: CallStack("Preview"), amount: demo,
+ sendAction: {}, recvAction: {}, rowAction: {},
balanceDest: nil)
}
- BalanceRowView(amount: test, sendAction: {}, recvAction: {},
rowAction: {})
+ BalanceRowView(stack: CallStack("Preview"), amount: test,
+ sendAction: {}, recvAction: {}, rowAction: {},
balanceDest: nil)
}
}
}
diff --git a/TalerWallet1/Views/Balances/BalancesListView.swift
b/TalerWallet1/Views/Balances/BalancesListView.swift
index b425948..43cceb9 100644
--- a/TalerWallet1/Views/Balances/BalancesListView.swift
+++ b/TalerWallet1/Views/Balances/BalancesListView.swift
@@ -14,10 +14,6 @@ struct BalancesListView: View {
let navTitle: String
@Binding var balances: [Balance]
@Binding var shouldReloadBalances: Int
-#if TABBAR // Taler Wallet
-#else // GNU Taler
- let hamburgerAction: () -> Void
-#endif
@EnvironmentObject private var model: WalletModel
@@ -93,21 +89,15 @@ struct BalancesListView: View {
}
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
-#endif
-#if TABBAR // Taler Wallet
- let hamburger: HamburgerButton? = nil
-#else // GNU Taler
- let hamburger: HamburgerButton = HamburgerButton(action:
hamburgerAction)
#endif
Content(symLog: symLog, stack: stack.push(), balances: $balances,
amountToTransfer: $amountToTransfer, summary: $summary,
reloadBalances: reloadBalances)
.navigationTitle(navTitle)
- .navigationBarItems(leading: hamburger,
- trailing: QRButton(action:
checkCameraAvailable))
+ .navigationBarItems(trailing: QRButton(action:
checkCameraAvailable))
.alert("Scanning QR-codes requires access to the camera",
isPresented: $showCameraAlert,
actions: { openSettingsButton
@@ -137,7 +127,7 @@ extension BalancesListView {
var reloadBalances: (_ stack: CallStack, _ invalidateCache: Bool)
async -> Int
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog?.vlog() // just to get the # to compare it
with .onAppear & onDisappear
#endif
diff --git a/TalerWallet1/Views/Balances/BalancesSectionView.swift
b/TalerWallet1/Views/Balances/BalancesSectionView.swift
index f6306d1..0667225 100644
--- a/TalerWallet1/Views/Balances/BalancesSectionView.swift
+++ b/TalerWallet1/Views/Balances/BalancesSectionView.swift
@@ -26,6 +26,7 @@ struct BalancesSectionView {
@EnvironmentObject private var controller: Controller
@AppStorage("iconOnly") var iconOnly: Bool = false
+ @State private var showSpendingHint = true
@State private var isShowingDetailView = false
@State private var transactions: [Transaction] = []
@State private var completedTransactions: [Transaction] = []
@@ -53,7 +54,7 @@ struct BalancesSectionView {
extension BalancesSectionView: View {
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -61,11 +62,14 @@ extension BalancesSectionView: View {
let currencyInfo = controller.info(for: currency,
controller.currencyTicker)
Section {
- if "KUDOS" == currency && !balance.available.isZero {
- Text(iconOnly ? "Visit the [Demo
Shop](https://shop.demo.taler.net)" :
- "You can spend these KUDOS in the [Demo
Shop](https://shop.demo.taler.net), or send them to another wallet.")
- .accessibilityFont(.body)
- .multilineTextAlignment(.leading)
+ let showSpendingButton = "KUDOS" == currency &&
!balance.available.isZero
+ if showSpendingButton {
+ if !iconOnly && showSpendingHint {
+ Text("You can spend these KUDOS in the Demo Shop, or send
them to another wallet.")
+ .accessibilityFont(.body)
+ .multilineTextAlignment(.leading)
+ .listRowSeparator(.hidden)
+ }
}
BalancesNavigationLinksView(symLog: symLog,
stack: stack.push(),
@@ -75,6 +79,16 @@ extension BalancesSectionView: View {
completedTransactions: $completedTransactions,
reloadAllAction: reloadCompleted,
reloadOneAction: reloadOneAction)
+ if showSpendingButton {
+ let title = String(localized: "LinkTitle_DEMOSHOP",
defaultValue: "Spend test money")
+ Button(title) {
+ showSpendingHint = false
+ UIApplication.shared.open(URL(string:DEMOSHOP)!, options:
[:])
+ }
+ .buttonStyle(TalerButtonStyle(type: .bordered, narrow: false,
aligned: .center))
+ .accessibilityHint("Will go to the demo shop website.")
+ .listRowSeparator(.hidden)
+ }
if pendingTransactions.count > 0 {
BalancesPendingRowView(symLog: symLog,
stack: stack.push(),
@@ -225,7 +239,7 @@ fileprivate struct BalancesNavigationLinksView: View {
}, tag: 2, selection: $buttonSelected
) { EmptyView() }.frame(width: 0).opacity(0).hidden() //
RequestPayment
- NavigationLink(destination: LazyView {
+ let balanceDest = LazyView {
TransactionsListView(stack: stack.push(),
navTitle: String(localized: "Transactions",
comment: "ViewTitle of TransactionList"),
currency: currency,
@@ -233,17 +247,19 @@ fileprivate struct BalancesNavigationLinksView: View {
showUpDown: true,
reloadAllAction: reloadAllAction,
reloadOneAction: reloadOneAction)
- }, tag: 3, selection: $buttonSelected
- ) { EmptyView() }.frame(width: 0).opacity(0).hidden() //
TransactionsListView
+ }
+ NavigationLink(destination: balanceDest, tag: 3, selection:
$buttonSelected)
+ { EmptyView() }.frame(width: 0).opacity(0).hidden()
// TransactionsListView
- BalanceRowView(amount: balance.available,
- sendAction: {
+ BalanceRowView(stack: stack.push(),
+ amount: balance.available,
+ sendAction: {
selectAndUpdate(1) // trigger SendAmount
NavigationLink
- }, recvAction: {
+ }, recvAction: {
selectAndUpdate(2) // trigger RequestPayment
NavigationLink
- }, rowAction: {
+ }, rowAction: {
buttonSelected = 3 // trigger TransactionList
NavigationLink
- })
+ }, balanceDest: balanceDest)
}
}
}
diff --git a/TalerWallet1/Views/Balances/PendingRowView.swift
b/TalerWallet1/Views/Balances/PendingRowView.swift
index 62f3292..65bf5d6 100644
--- a/TalerWallet1/Views/Balances/PendingRowView.swift
+++ b/TalerWallet1/Views/Balances/PendingRowView.swift
@@ -12,8 +12,7 @@ struct PendingRowView: View {
let shouldConfirm: Bool
let needsKYC: Bool
- @Environment(\.sizeCategory) var sizeCategory
- @EnvironmentObject private var controller: Controller
+// @Environment(\.sizeCategory) var sizeCategory
@AppStorage("iconOnly") var iconOnly: Bool = false
let inTitle0 = String(localized: "TitleIncoming_Short", defaultValue:
"Incoming",
@@ -34,11 +33,8 @@ struct PendingRowView: View {
let outTitle = iconOnly ? outTitle0 : outTitle1
let pendingTitle = incoming ? inTitle : outTitle
- let currencyInfo = controller.info(for: amount.currencyStr,
controller.currencyTicker)
- let amountText = Text(amount.string(currencyInfo))
+ let amountText = AmountV(amount)
.foregroundColor(pendingColor)
- .accessibilityFont(.title2)
- .monospacedDigit()
// this is the default view for iOS 15
let vStack = VStack {
@@ -72,19 +68,6 @@ struct PendingRowView: View {
}
// MARK: -
#if DEBUG
-func PreviewCurrencyInfo(_ currency: String, digits: Int) -> CurrencyInfo {
- let unitName = digits == 0 ? "テ" : "ク" // do not use real currency
symbols like "¥" : "€"
- let scope = ScopeInfo(type: .global, currency: currency)
- let specs = CurrencySpecification(name: currency,
- code: currency,
- fractionalInputDigits: digits,
- fractionalNormalDigits: digits,
- fractionalTrailingZeroDigits: digits,
- altUnitNames: [0 : unitName])
- let previewFormatter = CurrencyFormatter.formatter(scope: scope, specs:
specs)
- return CurrencyInfo(scope: scope, specs: specs, formatter:
previewFormatter)
-}
-
@MainActor
fileprivate struct Preview_Content: View {
var body: some View {
diff --git a/TalerWallet1/Views/Exchange/ExchangeListView.swift
b/TalerWallet1/Views/Banking/ExchangeListView.swift
similarity index 92%
rename from TalerWallet1/Views/Exchange/ExchangeListView.swift
rename to TalerWallet1/Views/Banking/ExchangeListView.swift
index 75b7156..718291f 100644
--- a/TalerWallet1/Views/Exchange/ExchangeListView.swift
+++ b/TalerWallet1/Views/Banking/ExchangeListView.swift
@@ -12,10 +12,6 @@ struct ExchangeListView: View {
let stack: CallStack
@Binding var balances: [Balance]
let navTitle: String
-#if TABBAR // Taler Wallet
-#else // GNU Taler
- var hamburgerAction: () -> Void
-#endif
@EnvironmentObject private var model: WalletModel
@State var showAlert: Bool = false
@@ -35,11 +31,6 @@ struct ExchangeListView: View {
}
var body: some View {
-#if TABBAR // Taler Wallet
- let hamburger: HamburgerButton? = nil
-#else // GNU Taler
- let hamburger = HamburgerButton(action: hamburgerAction)
-#endif
let accessibilityLabelStr = String(localized: "Add Exchange", comment:
"accessibilityLabel for the + button")
let plusButton = PlusButton(accessibilityLabelStr:
accessibilityLabelStr) {
showAlert = true
@@ -49,7 +40,7 @@ struct ExchangeListView: View {
if #available(iOS 16.0, *) {
ExchangeListCommonV(symLog: symLog, stack: stack.push(), balances:
$balances)
.navigationTitle(navTitle)
- .navigationBarItems(leading: hamburger, trailing: plusButton)
+ .navigationBarItems(trailing: plusButton)
.alert(addTitleStr, isPresented: $showAlert) {
TextField("Exchange address", text: $newExchange)
// .textFieldStyle(.roundedBorder) Yikes: when adding
style the alert will stop showing the textfield! Don't do this.
@@ -63,7 +54,7 @@ struct ExchangeListView: View {
} else { // iOS 15 cannot have a textfield in an alert, so we must
ExchangeListCommonV(symLog: symLog, stack: stack.push(), balances:
$balances)
.navigationTitle(navTitle)
- .navigationBarItems(leading: hamburger, trailing: plusButton)
+ .navigationBarItems(trailing: plusButton)
.textFieldAlert(isPresented: $showAlert,
title: addTitleStr,
doneText: addButtonStr,
@@ -91,7 +82,7 @@ struct ExchangeListCommonV: View {
}
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog?.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -129,7 +120,8 @@ extension ExchangeListCommonV {
var currencies: [String : [Exchange]] = [:]
for exchange in exchanges {
- let currency = exchange.currency ?? "Unknown"
+ let currency = exchange.scopeInfo?.currency
+ ?? exchange.currency ?? "Unknown"
if currencies[currency] != nil {
currencies[currency]!.append(exchange)
} else {
diff --git a/TalerWallet1/Views/Exchange/ExchangeRowView.swift
b/TalerWallet1/Views/Banking/ExchangeRowView.swift
similarity index 84%
rename from TalerWallet1/Views/Exchange/ExchangeRowView.swift
rename to TalerWallet1/Views/Banking/ExchangeRowView.swift
index aa93dff..a16e6ad 100644
--- a/TalerWallet1/Views/Exchange/ExchangeRowView.swift
+++ b/TalerWallet1/Views/Banking/ExchangeRowView.swift
@@ -34,7 +34,7 @@ struct ExchangeRowView: View {
}
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
let delay: UInt = 0 // set to 5 to test delayed currency
information
@@ -73,18 +73,23 @@ struct ExchangeRowView: View {
sendAction: { selectAndUpdate(1) },
recvAction: { selectAndUpdate(2) })
Group {
- HStack(spacing: 0) { // can't use the built in Label because it
adds the accessory arrow
- Text(baseURL.trimURL())
- .accessibilityFont(.headline)
+ NavigationLink(destination: showToS) {
+ VStack(alignment: .leading) {
+ Text(baseURL.trimURL())
+ .accessibilityFont(.headline)
+ if !iconOnly {
+ Text("Terms of Service") // VIEW_WITHDRAW_TOS
+ .accessibilityFont(.body)
+ }
+ }
+ }
+ .background( Group {
NavigationLink(destination: deposit, tag: 1, selection:
$buttonSelected)
- { EmptyView() }.frame(width: 0).opacity(0)
+ { EmptyView() }.frame(width: 0).opacity(0).hidden()
NavigationLink(destination: manualWithdraw, tag: 2, selection:
$buttonSelected)
- { EmptyView() }.frame(width: 0).opacity(0)
- }.listRowSeparator(.hidden)
- NavigationLink(destination: showToS) {
- Text("Terms of Service") // VIEW_WITHDRAW_TOS
- .accessibilityFont(.body)
- }.listRowSeparator(.hidden)
+ { EmptyView() }.frame(width: 0).opacity(0).hidden()
+ })
+ .listRowSeparator(.hidden)
if #available(iOS 16.0, *) {
ViewThatFits(in: .horizontal) {
HStack(spacing: HSPACING) {
@@ -97,12 +102,13 @@ struct ExchangeRowView: View {
}
}
.task {
- if !controller.hasInfo(for: currency) {
+ if controller.hasInfo(for: currency) == nil {
symLog.log("fetching info for \(currency)")
// FIXME: remove fake ScopeInfo once the REAL one is in
exchange.scopeInfo
- let scopeInfo = exchange.scopeInfo ?? ScopeInfo(type: .global,
currency: currency)
+ let scopeInfo = exchange.scopeInfo
+ ?? ScopeInfo(type: .global, currency: currency)
do {
- let info = try await model.getCurrencyInfo(scope:
scopeInfo, delay: delay)
+ let info = try await model.getCurrencyInfoM(scope:
scopeInfo, delay: delay)
// logger.info("got info: \(scope.currency, privacy:
.public)")
await controller.setInfo(info)
} catch { // TODO: error handling - couldn't get
CurrencyInfo
@@ -119,15 +125,16 @@ fileprivate struct ExchangeRow_Container : View {
// let amount = Amount(currency: LONGCURRENCY, cent: 123456)
var body: some View {
+ let scopeInfo = ScopeInfo(type: .exchange, currency: LONGCURRENCY)
let exchange1 = Exchange(exchangeBaseUrl: ARS_AGE_EXCHANGE,
- currency: LONGCURRENCY,
+ scopeInfo: scopeInfo,
paytoUris: [],
tosStatus: .pending,
exchangeEntryStatus: .preset,
exchangeUpdateStatus: .initial,
ageRestrictionOptions: [12,16])
let exchange2 = Exchange(exchangeBaseUrl: ARS_EXP_EXCHANGE,
- currency: LONGCURRENCY,
+ scopeInfo: scopeInfo,
paytoUris: [],
tosStatus: .proposed,
exchangeEntryStatus: .ephemeral,
diff --git a/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
b/TalerWallet1/Views/Banking/ExchangeSectionView.swift
similarity index 69%
rename from TalerWallet1/Views/Exchange/ExchangeSectionView.swift
rename to TalerWallet1/Views/Banking/ExchangeSectionView.swift
index 0f48133..09bfa7e 100644
--- a/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
+++ b/TalerWallet1/Views/Banking/ExchangeSectionView.swift
@@ -13,9 +13,10 @@ struct ExchangeSectionView: View {
let currency: String // this is the currency to be
used
let exchanges: [Exchange]
@Binding var amountToTransfer: Amount // does still have the wrong
currency
+ @AppStorage("iconOnly") var iconOnly: Bool = false
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
// let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -25,6 +26,21 @@ struct ExchangeSectionView: View {
exchange: exchange,
currency: currency, // TODO:
(balance.available) amount.isZero to disable Deposit-button
amountToTransfer: $amountToTransfer) // does still have
the wrong currency
+ .listRowSeparator(.hidden)
+ }
+ if "KUDOS" == currency {
+ let bankingHint = String(localized: "Since the demo bank
supports the Taler integration, you can start a withdrawal directly on the")
+ let linkTitle = String(localized: "LinkTitle_DEMOBANK",
defaultValue: "Demo Bank Website")
+ VStack {
+ if !iconOnly {
+ Text(bankingHint)
+ }
+ Link(linkTitle, destination: URL(string: DEMOBANK)!)
+ .buttonStyle(TalerButtonStyle(type: .bordered, narrow:
false, aligned: .center))
+ }
+ .accessibilityElement(children: .combine)
+ .accessibilityLabel(bankingHint + " " + linkTitle)
+ .padding(.top)
}
} header: {
BarGraphHeader(stack: stack.push(), currency: currency)
@@ -39,15 +55,16 @@ fileprivate struct ExchangeRow_Container : View {
// let amount = Amount(currency: LONGCURRENCY, cent: 123456)
var body: some View {
+ let scopeInfo = ScopeInfo(type: .exchange, currency: LONGCURRENCY)
let exchange1 = Exchange(exchangeBaseUrl: ARS_AGE_EXCHANGE,
- currency: LONGCURRENCY,
+ scopeInfo: scopeInfo,
paytoUris: [],
tosStatus: .pending,
exchangeEntryStatus: .preset,
exchangeUpdateStatus: .initial,
ageRestrictionOptions: [12,16])
let exchange2 = Exchange(exchangeBaseUrl: ARS_EXP_EXCHANGE,
- currency: LONGCURRENCY,
+ scopeInfo: scopeInfo,
paytoUris: [],
tosStatus: .proposed,
exchangeEntryStatus: .ephemeral,
diff --git a/TalerWallet1/Views/Exchange/ManualWithdraw.swift
b/TalerWallet1/Views/Banking/ManualWithdraw.swift
similarity index 70%
rename from TalerWallet1/Views/Exchange/ManualWithdraw.swift
rename to TalerWallet1/Views/Banking/ManualWithdraw.swift
index 9cf3534..f63751e 100644
--- a/TalerWallet1/Views/Exchange/ManualWithdraw.swift
+++ b/TalerWallet1/Views/Banking/ManualWithdraw.swift
@@ -6,7 +6,8 @@ import SwiftUI
import taler_swift
import SymLog
-// Will be called by the user tapping "Withdraw Coins" in the exchange list
+// Will be called by either the user tapping "Withdraw Coins" in the exchange
list
+// or from WithdrawExchangeV after a withdraw-exchange QR was scanned
struct ManualWithdraw: View {
private let symLog = SymLogV(0)
let stack: CallStack
@@ -23,7 +24,7 @@ struct ManualWithdraw: View {
// @State var selectedAge = 0
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -47,23 +48,24 @@ struct ManualWithdraw: View {
// restrictAge: restrictAge)
}
let disabled = amountToTransfer.isZero || someCoins.invalid ||
someCoins.tooMany
+ let tosAccepted = exchange.tosStatus == .accepted
ScrollView { VStack(alignment: .trailing) {
Text("via \(exchange.exchangeBaseUrl.trimURL())")
.multilineTextAlignment(.center)
.accessibilityFont(.body)
- CurrencyInputView(amount: $amountToTransfer,
- available: nil,
- title: iconOnly ? String(localized:
"Amount:")
- : String(localized: "Amount
to withdraw:"),
- shortcutAction: nil)
- .padding(.top)
- QuiteSomeCoins(someCoins: someCoins,
- shouldShowFee: true, // TODO: set to
false if we never charge withdrawal fees
- currency: currency,
- currencyInfo: currencyInfo,
- amountEffective:
withdrawalAmountDetails?.amountEffective)
-// agePicker
- if exchange.tosStatus == .accepted {
+ if tosAccepted {
+ CurrencyInputView(amount: $amountToTransfer,
+ available: nil,
+ title: iconOnly ? String(localized:
"Amount:")
+ : String(localized:
"Amount to withdraw:"),
+ shortcutAction: nil)
+ .padding(.top)
+ QuiteSomeCoins(someCoins: someCoins,
+ shouldShowFee: true, // TODO: set to
false if we never charge withdrawal fees
+ currency: currency,
+ currencyInfo: currencyInfo,
+ amountEffective:
withdrawalAmountDetails?.amountEffective)
+// agePicker
NavigationLink(destination: destination) {
Text("Confirm Withdrawal") // VIEW_WITHDRAW_ACCEPT
}
@@ -90,24 +92,27 @@ struct ManualWithdraw: View {
symLog.log("❗️ \(navTitle) onDisappear")
}
} else {
- WithdrawProgressView(message: exchangeBaseUrl.trimURL())
- .navigationTitle("Contacting Exchange")
+ LoadingView(url: nil, message: exchangeBaseUrl.trimURL())
}
} .task(id: amountToTransfer.value) { // re-run this whenever
amountToTransfer changes
- symLog.log("getExchangeByUrl(\(exchangeBaseUrl))")
- do {
+ symLog.log("getExchangeByUrl(\(exchangeBaseUrl))")
+ if exchange == nil || exchange?.tosStatus != .accepted {
if let exc = await model.getExchangeByUrl(url:
exchangeBaseUrl) {
exchange = exc
- if !amountToTransfer.isZero {
- let details = try await
model.loadWithdrawalDetailsForAmountM(exchangeBaseUrl,
-
amount: amountToTransfer)
- withdrawalAmountDetails = details
+ } else {
+ // TODO: Error "Can't get Exchange Info"
+ }
+ }
+ if !amountToTransfer.isZero {
+ do {
+ let details = try await
model.loadWithdrawalDetailsForAmountM(exchangeBaseUrl,
+
amount: amountToTransfer)
+ withdrawalAmountDetails = details
// agePicker.setAges(ages:
withdrawalAmountDetails?.ageRestrictionOptions)
- }
+ } catch { // TODO: error
+ symLog.log(error.localizedDescription)
+ withdrawalAmountDetails = nil
}
- } catch { // TODO: error
- symLog.log(error.localizedDescription)
- withdrawalAmountDetails = nil
}
}
}
diff --git a/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
b/TalerWallet1/Views/Banking/ManualWithdrawDone.swift
similarity index 77%
rename from TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
rename to TalerWallet1/Views/Banking/ManualWithdrawDone.swift
index 039f6e4..d4fa7ec 100644
--- a/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
+++ b/TalerWallet1/Views/Banking/ManualWithdrawDone.swift
@@ -23,9 +23,12 @@ struct ManualWithdrawDone: View {
func reloadOneAction(_ transactionId: String) async throws -> Transaction {
return try await model.getTransactionByIdT(transactionId)
}
+ func dismissTopAnimated(_ stack: CallStack) {
+ dismissTop()
+ }
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -35,7 +38,7 @@ struct ManualWithdrawDone: View {
transactionId: transactionId,
reloadAction: reloadOneAction,
navTitle: navTitle,
- doneAction: ViewState.shared.popToRootView,
+ doneAction: dismissTopAnimated, //
ViewState.shared.popToRootView,
abortAction: nil,
deleteAction: nil,
failAction: nil,
@@ -45,19 +48,20 @@ struct ManualWithdrawDone: View {
.interactiveDismissDisabled() // can only use "Done"
button to dismiss
// .navigationTitle(navTitle)
} else {
- WithdrawProgressView(message:
exchange.exchangeBaseUrl.trimURL())
- .navigationTitle("Loading " + navTitle)
+ LoadingView(url: nil, message:
exchange.exchangeBaseUrl.trimURL())
}
}.onAppear() {
symLog.log("onAppear")
DebugViewC.shared.setViewID(VIEW_WITHDRAW_ACCEPT, stack:
stack.push())
}.task {
- do {
- let result = try await
model.sendAcceptManualWithdrawalM(exchange.exchangeBaseUrl,
-
amount: amountToTransfer, restrictAge: 0)
- transactionId = result!.transactionId
- } catch { // TODO: error
- symLog.log(error.localizedDescription)
+ if transactionId == nil {
+ do {
+ let result = try await
model.sendAcceptManualWithdrawalM(exchange.exchangeBaseUrl,
+
amount: amountToTransfer, restrictAge: 0)
+ transactionId = result!.transactionId
+ } catch { // TODO: error
+ symLog.log(error.localizedDescription)
+ }
}
}
}
@@ -69,8 +73,9 @@ struct ManualWithdrawDone_Container : View {
@State private var amountToTransfer = Amount(currency: LONGCURRENCY, cent:
510)
var body: some View {
+ let scopeInfo = ScopeInfo(type: .exchange, currency: LONGCURRENCY)
let exchange = Exchange(exchangeBaseUrl: DEMOEXCHANGE,
- currency: LONGCURRENCY,
+ scopeInfo: scopeInfo,
paytoUris: [],
tosStatus: .pending,
exchangeEntryStatus: .preset,
diff --git a/TalerWallet1/Views/Exchange/QuiteSomeCoins.swift
b/TalerWallet1/Views/Banking/QuiteSomeCoins.swift
similarity index 100%
rename from TalerWallet1/Views/Exchange/QuiteSomeCoins.swift
rename to TalerWallet1/Views/Banking/QuiteSomeCoins.swift
diff --git a/TalerWallet1/Views/HelperViews/AmountRowV.swift
b/TalerWallet1/Views/HelperViews/AmountRowV.swift
index 19f99dc..ed5f6df 100644
--- a/TalerWallet1/Views/HelperViews/AmountRowV.swift
+++ b/TalerWallet1/Views/HelperViews/AmountRowV.swift
@@ -5,92 +5,76 @@
import SwiftUI
import taler_swift
-// calculate the width of the amountStr (with Font)
-// calculate the width of 'content' in compact form
-// if it fits side by side, then render HStack(content(compact), Spacer(),
amountStr)
-// else render VStack(content(wide), HStack(Spacer(), amountStr))
-
-struct AmountRowV<Content: View>: View {
- let amountStr: String
- let amountColor: Color
- let doneOrPending: Bool
- let largeAmountFont: Bool
- let fitsHorizontal: Bool
- let vertAlignment: VerticalAlignment
-
- var content: () -> Content
+// Title and Amount
+struct AmountRowV: View {
+ let title: String
+ let amount: Amount
+ let color: Color
+ let large: Bool // set to false for QR or IBAN
var body: some View {
- let text = Text(amountStr)
-// .strikethrough(!doneOrPending)
- .foregroundColor(amountColor)
- .accessibilityFont(largeAmountFont ? .title : .title2)
- .monospacedDigit()
- if fitsHorizontal {
- HStack(alignment: vertAlignment, spacing: 0) {
- content()
- Spacer(minLength: 0)
- text
+ let titleV = Text(title)
+ .multilineTextAlignment(.leading)
+ .accessibilityFont(.body)
+ let amountV = AmountV(amount: amount, large: large)
+ .foregroundColor(color)
+ let verticalV = VStack(alignment: .leading) {
+ titleV
+ HStack(alignment: .lastTextBaseline) {
+ Spacer(minLength: 2)
+ amountV
}
- } else {
- VStack(alignment: .leading, spacing: 0) {
- content()
- HStack {
- Spacer(minLength: 0)
- text
+ }
+ Group {
+ if #available(iOS 16.0, *) {
+ ViewThatFits(in: .horizontal) {
+ HStack(alignment: .lastTextBaseline) {
+ titleV//.border(.orange)
+ Spacer(minLength: 2)
+ amountV//.border(.gray)
+ }
+ HStack(alignment: .lastTextBaseline) {
+ titleV//.border(.blue)
+ .lineLimit(2, reservesSpace: true)
+ .fixedSize(horizontal: false, vertical: true)
+ Spacer(minLength: 2)
+ amountV//.border(.gray)
+ }
+ verticalV
}
+ } else { // view for iOS 15
+ verticalV
}
}
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .listRowSeparator(.hidden)
}
}
-// MARK: -
-#if DEBUG
-
-struct PreviewSectionWithAmountRow: View {
- @Environment(\.sizeCategory) var sizeCategory
- @Environment(\.colorSchemeContrast) private var colorSchemeContrast
-
- var body: some View {
- let testInfo = PreviewCurrencyInfo(TESTCURRENCY, digits: 0)
- let demoInfo = PreviewCurrencyInfo(DEMOCURRENCY, digits: 2)
- let test = Amount(currency: TESTCURRENCY, cent: 123)
- let demo = Amount(currency: DEMOCURRENCY, cent: 123456)
- let testStr = test.string(testInfo)
- let demoStr = demo.string(demoInfo)
- List {
- Section {
- AmountRowV(amountStr: demoStr, amountColor: .primary,
doneOrPending: true, largeAmountFont: true,
- fitsHorizontal: true, vertAlignment:
.lastTextBaseline) {
- Text(verbatim: "Balance")
- .foregroundColor(colorSchemeContrast == .increased ?
.primary : .secondary)
- .accessibilityFont(.title2)
- }
- AmountRowV(amountStr: demoStr, amountColor: .primary,
doneOrPending: true, largeAmountFont: true,
- fitsHorizontal: false, vertAlignment:
.lastTextBaseline) {
- Text(verbatim: "Balance")
- .foregroundColor(colorSchemeContrast == .increased ?
.primary : .secondary)
- .accessibilityFont(.title2)
- }
- }
- Section {
- AmountRowV(amountStr: testStr, amountColor: .primary,
doneOrPending: false, largeAmountFont: false,
- fitsHorizontal: true, vertAlignment:
.lastTextBaseline) {
- Text(verbatim: "Balance")
- .foregroundColor(colorSchemeContrast == .increased ?
.primary : .secondary)
- .accessibilityFont(.title2)
- }
- AmountRowV(amountStr: testStr, amountColor: .secondary,
doneOrPending: false, largeAmountFont: false,
- fitsHorizontal: false, vertAlignment:
.lastTextBaseline) {
- Text(verbatim: "Balance")
- .foregroundColor(colorSchemeContrast == .increased ?
.primary : .secondary)
- .accessibilityFont(.title2)
- }
- }
- }
+extension AmountRowV {
+ init(title: String, amount: Amount, color: Color) {
+ self.title = title
+ self.amount = amount
+ self.color = color
+ self.large = true
}
}
+// MARK: -
+fileprivate func talerFromStr(_ from: String) -> Amount {
+ do {
+ let amount = try Amount(fromString: from)
+ return amount
+ } catch {
+ return Amount(currency: "Taler", cent: 480)
+ }
+}
#Preview {
- PreviewSectionWithAmountRow()
+ List {
+ let fee = Amount(currency: "Taler", cent: 20)
+ AmountRowV(title: "Fee", amount: fee, color: Color("Outgoing"), large:
false)
+ let cents = Amount(currency: "Taler", cent: 480)
+ AmountRowV(title: "Cents", amount: cents, color: Color("Incoming"))
+ let amount = talerFromStr("Taler:4.80")
+ AmountRowV(title: "Chosen amount to withdraw", amount: amount, color:
Color("Incoming"))
+ }
}
-#endif
diff --git a/TalerWallet1/Views/HelperViews/AmountV.swift
b/TalerWallet1/Views/HelperViews/AmountV.swift
new file mode 100644
index 0000000..5a7760f
--- /dev/null
+++ b/TalerWallet1/Views/HelperViews/AmountV.swift
@@ -0,0 +1,38 @@
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
+import SwiftUI
+import taler_swift
+
+struct AmountV: View {
+ let amount: Amount
+ let large: Bool // set to false for QR or IBAN
+
+ @EnvironmentObject private var controller: Controller
+
+ var amountStr: String {
+ if let currencyInfo = controller.info(for: amount.currencyStr) {
+ return amount.string(currencyInfo)
+ }
+ return amount.readableDescription
+ }
+
+ var body: some View {
+ Text(amountStr)
+ .multilineTextAlignment(.center)
+ .accessibilityFont(large ? .title : .title2)
+// .fontWeight(large ? .medium : .regular) // @available(iOS
16.0, *)
+ .monospacedDigit()
+ }
+}
+extension AmountV {
+ init(_ amount: Amount) {
+ self.amount = amount
+ self.large = false
+ }
+}
+// MARK: -
+//#Preview {
+// AmountV()
+//}
diff --git a/TalerWallet1/Views/HelperViews/AmountView.swift
b/TalerWallet1/Views/HelperViews/AmountView.swift
deleted file mode 100644
index 2015115..0000000
--- a/TalerWallet1/Views/HelperViews/AmountView.swift
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
- * See LICENSE.md
- */
-import SwiftUI
-
-struct AmountView: View {
- let title: String
- let value: String
- let color: Color
- let large: Bool // set to false for QR or IBAN
-
- var body: some View {
- VStack(alignment: .leading) {
- Text(title)
- .fixedSize(horizontal: false, vertical: true) // wrap in
scrollview
- .multilineTextAlignment(.leading)
- .accessibilityFont(.body)
- HStack {
- Spacer()
- Text(value)
- .fixedSize(horizontal: false, vertical: true) // wrap in
scrollview
- .multilineTextAlignment(.center)
- .foregroundColor(color)
- .accessibilityFont(large ? .title : .title2)
-// .fontWeight(large ? .medium : .regular) //
@available(iOS 16.0, *)
- .monospacedDigit()
- }
- }
- .frame(maxWidth: .infinity, alignment: .leading)
- .listRowSeparator(.hidden)
- }
-}
-
-struct AmountView_Previews: PreviewProvider {
- static var previews: some View {
- List {
- AmountView(title: "Fee", value: "- 0,2 Taler",
- color: Color("Outgoing"), large: true)
- AmountView(title: "Coins", value: "4,8 Taler",
- color: Color("Incoming"), large: false)
- }
- }
-}
diff --git a/TalerWallet1/Views/HelperViews/BarGraph.swift
b/TalerWallet1/Views/HelperViews/BarGraph.swift
index 92c6e69..e7b60ef 100644
--- a/TalerWallet1/Views/HelperViews/BarGraph.swift
+++ b/TalerWallet1/Views/HelperViews/BarGraph.swift
@@ -16,6 +16,7 @@ struct BarGraphHeader: View {
@Environment(\.colorSchemeContrast) private var colorSchemeContrast
@State private var completedTransactions: [Transaction] = []
+ @ScaledMetric var barHeight = 9 // relative to fontSize
var body: some View {
HStack (alignment: .center, spacing: 10) {
@@ -23,7 +24,7 @@ struct BarGraphHeader: View {
.accessibilityFont(.title2)
// .foregroundColor(colorSchemeContrast == .increased ?
.primary : .secondary)
BarGraph(transactions: $completedTransactions,
- maxBars: MAXBARS, barHeight: 10) // TODO: barHeight
relative to fontSize
+ maxBars: MAXBARS, barHeight: barHeight)
}
.task {
symLog.log(".task for BarGraphHeader(\(currency)) - reload
Transactions")
@@ -38,7 +39,7 @@ struct BarGraphHeader: View {
struct BarGraph: View {
@Binding var transactions: [Transaction]
let maxBars: Int
- let barHeight : Double
+ let barHeight: Double
func maxValue(_ someTransactions: [Transaction]) -> Double {
var maxValue = 0.0
@@ -67,15 +68,16 @@ struct BarGraph: View {
let valueTransparent = barHeight - valueColored
// let _ = print("max: \(maxValue), ", incoming ? "+" :
"-", netto)
VStack(spacing: 0) {
+ let width = barHeight / 3
Rectangle()
.opacity(0.001)
- .frame (width: 3, height: incoming ?
valueTransparent : barHeight )
+ .frame (width: width, height: incoming ?
valueTransparent : barHeight )
Rectangle()
.foregroundColor(incoming ? .green : .red)
- .frame (width: 3, height: valueColored )
+ .frame (width: width, height: valueColored )
Rectangle()
.opacity(0.001)
- .frame (width: 3, height: incoming ? barHeight :
valueTransparent)
+ .frame (width: width, height: incoming ? barHeight
: valueTransparent)
}
}
}
diff --git a/TalerWallet1/Views/HelperViews/Buttons.swift
b/TalerWallet1/Views/HelperViews/Buttons.swift
index 9ab4a4b..7d9a788 100644
--- a/TalerWallet1/Views/HelperViews/Buttons.swift
+++ b/TalerWallet1/Views/HelperViews/Buttons.swift
@@ -110,8 +110,8 @@ struct TalerButtonStyle: ButtonStyle {
public func makeBody(configuration: ButtonStyle.Configuration) -> some
View {
// configuration.role = type == .prominent ? .primary : .normal
Only on macOS
MyBigButton(//type: type,
- foreColor: foreColor(type: type, pressed:
configuration.isPressed),
- backColor: backColor(type: type, pressed:
configuration.isPressed),
+ foreColor: foreColor(type: type, pressed:
configuration.isPressed, disabled: disabled),
+ backColor: backColor(type: type, pressed:
configuration.isPressed, disabled: disabled),
dimmed: dimmed,
configuration: configuration,
disabled: disabled,
@@ -120,14 +120,14 @@ struct TalerButtonStyle: ButtonStyle {
badge: badge)
}
- func foreColor(type: TalerButtonStyleType, pressed: Bool) -> Color {
+ func foreColor(type: TalerButtonStyleType, pressed: Bool, disabled: Bool)
-> Color {
return type == .plain ? WalletColors().fieldForeground : //
primary text color
WalletColors().buttonForeColor(pressed: pressed,
disabled: disabled,
prominent: type == .prominent,
balance: type == .balance)
}
- func backColor(type: TalerButtonStyleType, pressed: Bool) -> Color {
+ func backColor(type: TalerButtonStyleType, pressed: Bool, disabled: Bool)
-> Color {
return type == .plain && !pressed ? Color.clear :
WalletColors().buttonBackColor(pressed: pressed,
disabled: disabled,
diff --git a/TalerWallet1/Views/HelperViews/CopyShare.swift
b/TalerWallet1/Views/HelperViews/CopyShare.swift
index 500759b..7d0ebfb 100644
--- a/TalerWallet1/Views/HelperViews/CopyShare.swift
+++ b/TalerWallet1/Views/HelperViews/CopyShare.swift
@@ -8,12 +8,15 @@ import SymLog
struct CopyButton: View {
private let symLog = SymLogV(0)
- @Environment(\.isEnabled) private var isEnabled: Bool
let textToCopy: String
let vertical: Bool
+ @Environment(\.isEnabled) private var isEnabled: Bool
+ @EnvironmentObject private var controller: Controller
+
func copyAction() -> Void {
symLog.log(textToCopy)
+ controller.hapticFeedback(.medium)
UIPasteboard.general.setValue(textToCopy,
forPasteboardType:
UTType.plainText.identifier)
}
@@ -44,12 +47,14 @@ struct CopyButton: View {
@MainActor
struct ShareButton: View {
private let symLog = SymLogV(0)
- @Environment(\.isEnabled) private var isEnabled: Bool
-
let textToShare: String
+ @Environment(\.isEnabled) private var isEnabled: Bool
+ @EnvironmentObject private var controller: Controller
+
func shareAction() -> Void {
symLog.log(textToShare)
+ controller.hapticFeedback(.soft)
ShareSheet.shareSheet(url: textToShare)
}
diff --git a/TalerWallet1/Views/HelperViews/CurrencyField.swift
b/TalerWallet1/Views/HelperViews/CurrencyField.swift
index 7b74114..37d7342 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyField.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyField.swift
@@ -52,7 +52,7 @@ struct CurrencyField: View {
}
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog(amount.description) // just to get the # to
compare it with .onAppear & onDisappear
#endif
diff --git a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
index 3c9cbae..4822954 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
@@ -149,7 +149,7 @@ struct CurrencyInputView: View {
// print("❗️Yikes: CurrencyInputView hasBeenShown")
} else {
print("❗️CurrencyInputView❗️")
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
hasBeenShown = true
if !currencyField.becomeFirstResponder() {
print("❗️Yikes❗️")
diff --git a/TalerWallet1/Views/HelperViews/LaunchAnimationView.swift
b/TalerWallet1/Views/HelperViews/LaunchAnimationView.swift
index 8e923ca..4bb91e6 100644
--- a/TalerWallet1/Views/HelperViews/LaunchAnimationView.swift
+++ b/TalerWallet1/Views/HelperViews/LaunchAnimationView.swift
@@ -18,7 +18,7 @@ struct RotatingTaler: View {
let size: CGFloat
@Binding var rotationEnabled: Bool
@State private var rotationDirection = false
-#if TABBAR // Taler Wallet
+#if TALER_WALLET
let logo = "taler-logo-2023-blue"
#else // GNU Taler
let logo = "taler-logo-2023-red"
diff --git a/TalerWallet1/Views/HelperViews/LoadingView.swift
b/TalerWallet1/Views/HelperViews/LoadingView.swift
index 10450fc..b156cc0 100644
--- a/TalerWallet1/Views/HelperViews/LoadingView.swift
+++ b/TalerWallet1/Views/HelperViews/LoadingView.swift
@@ -7,22 +7,49 @@ import SymLog
struct LoadingView: View {
private let symLog = SymLogV(0)
- let navTitle = String(localized: "Loading...")
- let backButtonHidden: Bool
+ let url:URL?
+ let message: String?
+
+// let backButtonHidden: Bool
+ let navTitle = String(localized: "Loading…")
+
+ @State private var rotationEnabled = true
var body: some View {
- LaunchAnimationView()
- .navigationBarBackButtonHidden(backButtonHidden)
- .navigationTitle(navTitle)
- .frame(maxWidth: .infinity, maxHeight: .infinity, alignment:
.center)
-//
.background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
+ VStack(alignment: .center) {
+ Spacer()
+ RotatingTaler(size: 100, rotationEnabled: $rotationEnabled)
+ .onTapGesture(count: 2) {
+ rotationEnabled.toggle()
+ }
+ Spacer()
+ if let url {
+ if let urlStr = url.host {
+ Text(urlStr)
+ } else {
+ Text("Error in URL: \(url)")
+ }
+ Spacer()
+ }
+ if let message {
+ Text(message)
+ } else {
+ Text(EMPTYSTRING)
+ }
+ Spacer()
+ Spacer()
+ }
+ .frame(maxWidth: .infinity)
+ .accessibilityFont(.title)
+ .navigationTitle("Loading…")
+ .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
}
}
// MARK: -
struct LoadingView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
- LoadingView(backButtonHidden: true)
+ LoadingView(url: nil, message: "test message") // ,
backButtonHidden: true)
.navigationBarTitleDisplayMode(.automatic)
}.navigationViewStyle(.stack)
}
diff --git a/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift
b/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift
index 2af6d8a..0a2dcfc 100644
--- a/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift
+++ b/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift
@@ -10,12 +10,12 @@ import AVFoundation
struct QRCodeDetailView: View {
let talerURI: String
let incoming: Bool
- let amount: Amount?
+ let amount: Amount
var body: some View {
if talerURI.count > 10 {
Section {
- Text("Either", comment: "Either (copy/share)")
+ Text("Either", comment: "Either (copy/share the payment link
to the ...)")
.multilineTextAlignment(.leading)
.accessibilityFont(.title3)
// .padding(.vertical)
@@ -28,7 +28,8 @@ struct QRCodeDetailView: View {
let otherParty = incoming ? String(localized: "payer",
comment: "the payment link to the (otherParty), or")
: String(localized: "payee",
comment: "the payment link to the (otherParty), or")
- Text("the payment link to the \(otherParty), or", comment:
"...the payment link to the (payer/payee), or")
+ Text("the payment link to the \(otherParty), or",
+ comment: "(Either copy/share the payment link) to the
(payer/payee), or")
.multilineTextAlignment(.leading)
.accessibilityFont(.title3)
.listRowSeparator(.hidden)
@@ -41,14 +42,11 @@ struct QRCodeDetailView: View {
}
.listRowSeparator(.hidden)
- // TODO: use currency formatter instead of .readableDescription
- let hintStr = (amount == nil) ?
- (incoming ? String(localized: "let the payer scan this QR
code to pay.")
- : String(localized: "let the payee scan this QR
code to receive."))
- : (incoming ? String(localized: "let the payer scan this QR
code to pay \(amount!.readableDescription).",
- comment: "e.g. '5,3 €'")
- : String(localized: "let the payee scan this QR
code to receive \(amount!.readableDescription).",
- comment: "e.g. '$ 7.41'"))
+ let amountStr = amount.readableDescription // TODO:
currency formatter?
+ let hintStr = incoming ? String(localized: "let the payer scan
this QR code to pay \(amountStr).",
+ comment: "e.g. '5,3 €'")
+ : String(localized: "let the payee scan
this QR code to receive \(amountStr).",
+ comment: "e.g. '$ 7.41'")
Text(hintStr)
.fixedSize(horizontal: false, vertical: true) //
wrap in scrollview
.accessibilityFont(.title3)
@@ -62,8 +60,9 @@ fileprivate struct ContentView: View {
@State var talerURI: String =
"taler://pay-push/exchange.demo.taler.net/95ZG4D1AGFGZQ7CNQ1V49D3FT18HXKA6HQT4X3XME9YSJQVFQ520"
var body: some View {
+ let amount = Amount(currency: LONGCURRENCY, cent: 123)
List {
- QRCodeDetailView(talerURI: talerURI, incoming: false, amount: nil)
+ QRCodeDetailView(talerURI: talerURI, incoming: false, amount:
amount)
}
}
}
diff --git a/TalerWallet1/Views/HelperViews/TransactionButton.swift
b/TalerWallet1/Views/HelperViews/TransactionButton.swift
index 40b0c5e..2f1674a 100644
--- a/TalerWallet1/Views/HelperViews/TransactionButton.swift
+++ b/TalerWallet1/Views/HelperViews/TransactionButton.swift
@@ -7,12 +7,30 @@ import taler_swift
import AVFoundation
struct TransactionButton: View {
- let transactionId : String
- let command : TxAction
+ let transactionId: String
+ let command: TxAction
+ let warning: String?
let action: (_ transactionId: String) async throws -> Void
- @State var disabled: Bool = false
- @State var executed: Bool = false
+ @AppStorage("shouldShowWarning") var shouldShowWarning: Bool = true
+
+ @State private var disabled: Bool = false
+ @State private var executed: Bool = false
+ @State private var showAlert: Bool = false
+
+ private func doAction() {
+ disabled = true // don't try this more than once
+ Task { // runs on MainActor
+ do {
+ try await action(transactionId)
+// symLog.log("\(executed) \(transactionId)")
+ executed = true
+ } catch { // TODO: error
+// symLog.log(error.localizedDescription)
+ }
+ }
+ }
+
var body: some View {
let isDestructive = (command == .delete) || (command == .fail)
let isCancel = (command == .abort)
@@ -21,15 +39,10 @@ struct TransactionButton: View {
: nil
Button(role: role, action: {
if !disabled {
- disabled = true // don't try this more than once
- Task { // runs on MainActor
- do {
- try await action(transactionId)
-// symLog.log("\(executed) \(transactionId)")
- executed = true
- } catch { // TODO: error
-// symLog.log(error.localizedDescription)
- }
+ if shouldShowWarning && (isDestructive || isCancel) {
+ showAlert = true
+ } else {
+ doAction()
}
}
}, label: {
@@ -46,6 +59,16 @@ struct TransactionButton: View {
.buttonStyle(.bordered)
.controlSize(.large)
.disabled(disabled)
+ .alert(warning ?? EMPTYSTRING, isPresented: $showAlert, actions: {
+ Button("Cancel", role: .cancel) {
+ showAlert = false
+ }
+ Button(command.localizedActionTitle) {
+ showAlert = false
+ doAction()
+ }
+ }, message: { Text("This operation cannot be undone") }
+ )
}
}
// MARK: -
@@ -58,7 +81,9 @@ struct TransactionButton_Previews: PreviewProvider {
static var previews: some View {
List {
- TransactionButton(transactionId: "Button pressed", command:
.abort, action: action)
+ TransactionButton(transactionId: "Button pressed", command: .abort,
+ warning: "Are you sure you want to abort this
transaction?",
+ action: action)
}
}
}
diff --git a/TalerWallet1/Views/Main/MainView.swift
b/TalerWallet1/Views/Main/MainView.swift
index 43c6906..ce54497 100644
--- a/TalerWallet1/Views/Main/MainView.swift
+++ b/TalerWallet1/Views/Main/MainView.swift
@@ -20,10 +20,10 @@ struct MainView: View {
let stack: CallStack
@Binding var soundPlayed: Bool
- @EnvironmentObject private var viewState: ViewState //
popToRootView()
@EnvironmentObject private var controller: Controller
@AppStorage("talerFont") var talerFont: Int = 0 // extension
mustn't define this, so it must be here
- @AppStorage("playSounds") var playSounds: Int = 1 // extension
mustn't define this, so it must be here
+ @AppStorage("playSoundsI") var playSoundsI: Int = 1 // extension
mustn't define this, so it must be here
+ @AppStorage("playSoundsB") var playSoundsB: Bool = true
@State private var sheetPresented = false
@State private var urlToOpen: URL? = nil
@@ -32,19 +32,19 @@ struct MainView: View {
logger.info("sheet dismiss")
}
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
Group {
if controller.backendState == .ready {
Content(logger: logger, stack: stack.push("Content"),
talerFont: $talerFont)
- // any change to rootViewId triggers popToRootView behaviour
- .id(viewState.rootViewId)
.onAppear() {
- if playSounds != 0 && !soundPlayed {
+#if DEBUG
+ if playSoundsI != 0 && playSoundsB && !soundPlayed {
controller.playSound(1008) // Startup chime
}
+#endif
soundPlayed = true
}
} else if controller.backendState == .error {
@@ -88,10 +88,10 @@ extension MainView {
@AppStorage("iconOnly") var iconOnly: Bool = false
@EnvironmentObject private var controller: Controller
@EnvironmentObject private var model: WalletModel
+ @EnvironmentObject private var viewState: ViewState //
popToRootView()
let balancesTitle = String(localized: "TitleBalances", defaultValue:
"Balances")
- let exchangesTitle = String(localized: "TitleExchanges", defaultValue:
"Exchanges")
+ let exchangesTitle = String(localized: "TitleExchanges", defaultValue:
"Banking")
let settingsTitle = String(localized: "TitleSettings", defaultValue:
"Settings")
-#if TABBAR // Taler Wallet
@State private var selectedTab: Tab = .balances
@State private var showKycAlert: Bool = false
@State private var kycURI: URL?
@@ -128,57 +128,23 @@ extension MainView {
self.selectedTab = tappedTab
}
}
-#else // GNU Taler
- @State var sidebarVisible: Bool = false
- func hamburgerAction() {
- withAnimation(.easeInOut(duration: 0.25)) {
- sidebarVisible = !sidebarVisible
- }
- }
-
- var views: [SidebarItem] {[
- SidebarItem(name: balancesTitle,
- sysImage: "chart.bar.xaxis", // creditcard.fill //
TODO: Wallet Icon
- view: AnyView(BalancesListView(stack:
stack.push(balancesTitle),
- navTitle: balancesTitle,
- balances: $balances,
- shouldReloadBalances:
$shouldReloadBalances,
- hamburgerAction: hamburgerAction)
- )),
- SidebarItem(name: exchangesTitle,
- sysImage: "arrow.triangle.2.circlepath",
- view: AnyView(ExchangeListView(stack:
stack.push(exchangesTitle),
-// balances: $balances,
- navTitle: exchangesTitle,
- hamburgerAction: hamburgerAction)
- )),
- SidebarItem(name: settingsTitle, // TODO: "About"?
- sysImage: "gearshape.fill",
- view: AnyView(SettingsView(stack:
stack.push(settingsTitle),
- navTitle: settingsTitle,
- hamburgerAction: hamburgerAction)
- ))
- ]}
- @State var currentView: Int = 0
-#endif
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
// "@self" marks that the view value itself has changed, and
"@identity" marks that the
// identity of the view has changed (that is, that the persistent
data associated with
// the view has been recycled for a new instance of the same type)
-// if #available(iOS 17.1, *) {
-// // logs at INFO level, “com.apple.SwiftUI” subsystem,
category “Changed Body Properties”
-// let _ = Self._logChanges()
-// } else {
+ if #available(iOS 17.1, *) {
+ // logs at INFO level, “com.apple.SwiftUI” subsystem, category
“Changed Body Properties”
+ let _ = Self._logChanges()
+ } else {
let _ = Self._printChanges()
-// }
+ }
let delay: UInt = 0 // set to 5 to test delayed currency
information
#else
- let delay: UInt = 0
+ let delay: UInt = 0 // no delay for release builds
#endif
Group {
-#if TABBAR // Taler Wallet
// let labelStyle = iconOnly ? IconOnlyLabelStyle() :
TitleAndIconLabelStyle() // labelStyle doesn't work
TabView(selection: tabSelection()) {
NavigationView {
@@ -186,9 +152,10 @@ extension MainView {
navTitle: balancesTitle,
balances: $balances,
shouldReloadBalances: $shouldReloadBalances)
- }.navigationViewStyle(.stack)
+ }.id(viewState.rootViewId) // any change to
rootViewId triggers popToRootView behaviour
+ .navigationViewStyle(.stack)
.tabItem {
- Image(systemName: "chart.bar.xaxis") //
creditcard system will automatically use filled variant
+ Image(systemName: "chart.bar.xaxis") // iOS
will automatically use filled variant
.accessibilityLabel(balancesTitle)
if !iconOnly { Text(balancesTitle) }
}
@@ -201,7 +168,7 @@ extension MainView {
navTitle: exchangesTitle)
}.navigationViewStyle(.stack)
.tabItem {
- Image(systemName: "arrow.triangle.2.circlepath")
+ Image(systemName: "building.columns") //
"arrow.triangle.2.circlepath")
.accessibilityLabel(exchangesTitle)
if !iconOnly { Text(exchangesTitle) }
}
@@ -218,35 +185,6 @@ extension MainView {
.tag(Tab.settings)
}
// .animation(.linear(duration: LAUNCHDURATION), value:
selectedTab) doesn't work. Needs CustomTabView
-#else // GNU Taler
- ZStack(alignment: .leading) {
- NavigationView { // the one and only for all non-sheet views
- VStack(alignment: .leading) { // only needed for
backslide transition
- views[currentView].view
- .id(views[currentView].name)
- .frame(maxWidth: .infinity, maxHeight: .infinity,
alignment: .center)
- .transition(.backslide)
- } .id(talerFont)
- .navigationBarTitleDisplayMode(.automatic)
- .background(NavigationBarBuilder {
navigationController in
- //
navigationController.navigationBar.barTintColor = .red
-
navigationController.navigationBar.titleTextAttributes =
- [.font: TalerFont.uiFont(talerFont, size: 24,
relativeTo: .title2)]
-
navigationController.navigationBar.largeTitleTextAttributes =
- [.font: TalerFont.uiFont(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,
- hamburgerAction: hamburgerAction)
- }
-#endif
} .onNotification(.KYCrequired) { notification in
// show an alert with the KYC link (button) which opens in Safari
if let transition =
notification.userInfo?[TRANSACTIONTRANSITION] as? TransactionTransition {
@@ -276,16 +214,17 @@ extension MainView {
logger.info(".onNotification(.TransactionExpired) ==> reload")
shouldReloadBalances += 1
}
+ .onNotification(.TransactionDone) {
+ shouldReloadBalances += 1
+ selectedTab = .balances
+ }
.onChange(of: balances) { newArray in
for balance in newArray {
let scope = balance.scopeInfo
- logger.info("balance changed: \(scope.currency, privacy:
.public)")
- if !controller.hasInfo(for: scope.currency) {
+ if controller.hasInfo(for: scope.currency) == nil {
Task { // runs on MainActor
- logger.info("Task to get info for:
\(scope.currency, privacy: .public)")
do {
- let info = try await
model.getCurrencyInfo(scope: scope, delay: delay)
- logger.info("got info: \(scope.currency,
privacy: .public)")
+ let info = try await
model.getCurrencyInfoM(scope: scope, delay: delay)
await controller.setInfo(info)
} catch { // TODO: error handling - couldn't
get CurrencyInfo
logger.error("Couldn't get info for:
\(scope.currency, privacy: .public)\n\(error)")
diff --git a/TalerWallet1/Views/Main/SideBarView.swift
b/TalerWallet1/Views/Main/SideBarView.swift
deleted file mode 100644
index 0ae3b1d..0000000
--- a/TalerWallet1/Views/Main/SideBarView.swift
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
- * See LICENSE.md
- */
-import SwiftUI
-import SymLog
-
-fileprivate let sidebarWidth = 200.0
-
-struct SidebarItem {
- var name: String
- var sysImage: String?
- var view: AnyView
-}
-
-struct SideBarView: View {
- private let symLog = SymLogV(0)
- let stack: CallStack
- let views: [SidebarItem]
- @Binding var currentView: Int
- let sidebarVisible: Bool
- let hamburgerAction: () -> Void
- @State private var rotationEnabled = false
-
- var body: some View {
- HStack { // sideView left, clear dismiss target right
- EqualIconWidthDomain {
- VStack(spacing: 10) {
- let gnuTaler = String("GNU Taler") // this should NOT be
translated
- Link(gnuTaler, destination:
URL(string:"https://taler.net")!)
- .accessibilityFont(.largeTitle)
- .padding(.top, 30)
- RotatingTaler(size: 100, rotationEnabled: $rotationEnabled)
- .onTapGesture {
- rotationEnabled.toggle()
- }
- ForEach(0..<views.count, id: \.self) { i in
- Button {
- symLog.log("sidebar item \"\(views[i].name)\"
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)
- .frame(maxWidth: sidebarWidth, alignment:
.leading)
- } else {
- Text(views[i].name)
- .frame(maxWidth: sidebarWidth)
- }
- }
- .padding()
- .buttonStyle(.borderless)
- .accessibilityFont(.title2)
- .disabled(i == currentView)
- .accessibilityHidden(i == currentView) // don't
suggest the current item
- }
- Spacer()
- }
- .background(WalletColors().sideBackground)
- .frame(width: sidebarWidth, alignment: .center)
- // TODO: use leading instead of sidebarWidth for right-to-left
- .offset(x: sidebarVisible ? 0 : -sidebarWidth)
- // .onAppear can NOT be used here, because we don't show or
dismiss this view,
- // but only slide it left or right - so it is always there.
- }
- // this is just a target for a tap gesture outside the sidebar to
dismiss it
- Color.clear
- .frame(maxWidth: sidebarVisible ? .infinity : 0, maxHeight:
.infinity, alignment: .leading)
- // TODO: right-to-left ?
- .offset(x: sidebarVisible ? sidebarWidth : 0)
- .contentShape(Rectangle())
- .onTapGesture {
- hamburgerAction() // slide sidebar to the left
- }
- }
- }
-}
-// MARK: -
-#if DEBUG
-fileprivate struct BindingViewContainer : View {
- @State var currentView: Int = 0
- @State var sidebarVisible: Bool = true
- var views: [SidebarItem]
-
- var body: some View {
- ZStack(alignment: .leading) {
- views[currentView].view
- .frame(maxWidth: .infinity, maxHeight: .infinity, alignment:
.center)
- SideBarView(stack: CallStack("Preview"), views: views,
currentView: $currentView,
- sidebarVisible: sidebarVisible,
- hamburgerAction: { sidebarVisible = !sidebarVisible })
- }
- }
-}
-
-struct SideBarView_Previews: PreviewProvider {
- static var views: [SidebarItem] {[
- SidebarItem(name: "Balances",
- sysImage: "creditcard.fill", // TODO: Wallet Icon
- view: AnyView(WalletEmptyView(stack:
CallStack("Preview")))),
- SidebarItem(name: "Settings",
- sysImage: "gearshape.fill",
- view: AnyView(WalletEmptyView(stack:
CallStack("Preview"))))
- ]}
- static var previews: some View {
- BindingViewContainer(views: views)
- }
-}
-#endif
diff --git a/TalerWallet1/Views/Main/WalletEmptyView.swift
b/TalerWallet1/Views/Main/WalletEmptyView.swift
index f38b657..6caaf15 100644
--- a/TalerWallet1/Views/Main/WalletEmptyView.swift
+++ b/TalerWallet1/Views/Main/WalletEmptyView.swift
@@ -19,7 +19,7 @@ struct WalletEmptyView: View {
Text("There is no digital cash in your wallet.")
.accessibilityFont(.title3)
.listRowSeparator(.hidden)
- let title = String(localized: "LinkTitle_DEMOBANK",
defaultValue: "Get some test money")
+ let title = String(localized: "LinkTitle_Test_Money",
defaultValue: "Get some test money")
Link(title, destination: URL(string: DEMOBANK)!)
.buttonStyle(TalerButtonStyle(type: .prominent, narrow:
false, aligned: .center))
.padding(.vertical)
diff --git a/TalerWallet1/Views/Peer2peer/P2PReadyV.swift
b/TalerWallet1/Views/Peer2peer/P2PReadyV.swift
index b448eaa..e155bd8 100644
--- a/TalerWallet1/Views/Peer2peer/P2PReadyV.swift
+++ b/TalerWallet1/Views/Peer2peer/P2PReadyV.swift
@@ -32,7 +32,7 @@ struct P2PReadyV: View {
}
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -51,7 +51,7 @@ struct P2PReadyV: View {
.navigationBarBackButtonHidden(true)
.interactiveDismissDisabled() // can only use "Done"
button to dismiss
} else {
- WithdrawProgressView(message: "Loading...")
+ LoadingView(url: nil, message: "for
\(amountToTransfer.currencyStr)")
}
}
.navigationTitle(navTitle)
diff --git a/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
b/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
index d6a788c..4ee9f03 100644
--- a/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
+++ b/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
@@ -51,7 +51,7 @@ struct P2PSubjectV: View {
}
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog(amountToTransfer.readableDescription) //
just to get the # to compare it with .onAppear & onDisappear
#endif
@@ -87,7 +87,7 @@ struct P2PSubjectV: View {
.textFieldStyle(.roundedBorder)
.onAppear {
symLog.log("dispatching kbd...")
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
isFocused = true // make first responder -
raise keybord
symLog.log("...kbd isFocused")
}
@@ -129,7 +129,7 @@ struct P2PSubjectV: View {
// print("❗️ P2PSubjectV onDisappear")
}
.task(id: amountToTransfer.value) {
- if feeLabel == nil {
+ if amountToSend && feeLabel == nil {
do {
let ppCheck = try await
model.checkPeerPushDebitM(amountToTransfer)
if let feeAmount = p2pFee(ppCheck: ppCheck) {
diff --git a/TalerWallet1/Views/Peer2peer/RequestPayment.swift
b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
index f30bb0f..5de2e1d 100644
--- a/TalerWallet1/Views/Peer2peer/RequestPayment.swift
+++ b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
@@ -29,7 +29,7 @@ struct RequestPayment: View {
}
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
diff --git a/TalerWallet1/Views/Peer2peer/SendAmount.swift
b/TalerWallet1/Views/Peer2peer/SendAmount.swift
index d74d264..b5c4f5c 100644
--- a/TalerWallet1/Views/Peer2peer/SendAmount.swift
+++ b/TalerWallet1/Views/Peer2peer/SendAmount.swift
@@ -45,7 +45,7 @@ struct SendAmount: View {
}
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -150,7 +150,7 @@ fileprivate struct Preview_Content: View {
@State private var summary: String = ""
var body: some View {
- let amount = Amount(currency: TESTCURRENCY, integer: 10, fraction: 0)
+ let amount = Amount(currency: TESTCURRENCY, cent: 1000)
SendAmount(stack: CallStack("Preview"),
amountAvailable: amount,
amountToTransfer: $amountToPreview,
diff --git a/TalerWallet1/Views/Settings/AboutView.swift
b/TalerWallet1/Views/Settings/AboutView.swift
index ae5fe33..a00230f 100644
--- a/TalerWallet1/Views/Settings/AboutView.swift
+++ b/TalerWallet1/Views/Settings/AboutView.swift
@@ -24,7 +24,7 @@ struct AboutView: View {
@State private var listID = UUID()
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -44,7 +44,7 @@ struct AboutView: View {
.accessibilityAddTraits(.isLink)
.accessibilityRemoveTraits(.isStaticText)
.onTapGesture() {
-
UIApplication.shared.open(URL(string:"https://taler.net")!, options: [:])
+ UIApplication.shared.open(URL(string:TALER_NET)!,
options: [:])
}
SettingsItem(name: "App Version", id1: "app") {
@@ -53,15 +53,15 @@ struct AboutView: View {
SettingsItem(name: "Wallet Core Version", id1: "wallet-core") {
Text(verbatim: "\(walletCore.versionInfo?.version ??
"unknown")")
}
- SettingsItem(name: "Supported Exchange Versions", id1:
"exchange") {
- Text(verbatim: "\(walletCore.versionInfo?.exchange ??
"unknown")")
- }
- SettingsItem(name: "Supported Merchant Versions", id1:
"merchant") {
- Text(verbatim: "\(walletCore.versionInfo?.merchant ??
"unknown")")
- }
- SettingsItem(name: "Used Bank", id1: "bank") {
- Text(verbatim: "\(walletCore.versionInfo?.bank ??
"unknown")")
- }
+// SettingsItem(name: "Supported Exchange Versions", id1:
"exchange") {
+// Text(verbatim: "\(walletCore.versionInfo?.exchange ??
"unknown")")
+// }
+// SettingsItem(name: "Supported Merchant Versions", id1:
"merchant") {
+// Text(verbatim: "\(walletCore.versionInfo?.merchant ??
"unknown")")
+// }
+// SettingsItem(name: "Used Bank", id1: "bank") {
+// Text(verbatim: "\(walletCore.versionInfo?.bank ??
"unknown")")
+// }
}
.id(listID)
.listStyle(myListStyle.style).anyView
diff --git a/TalerWallet1/Views/Settings/SettingsItem.swift
b/TalerWallet1/Views/Settings/SettingsItem.swift
index 4c4bc72..595425e 100644
--- a/TalerWallet1/Views/Settings/SettingsItem.swift
+++ b/TalerWallet1/Views/Settings/SettingsItem.swift
@@ -20,9 +20,13 @@ struct SettingsItem<Content: View>: View {
var body: some View {
HStack {
VStack {
+ let isWeb = id1?.hasPrefix("web") ?? false
+ let foreColor = isWeb ? WalletColors().tint
+ : .primary
Text(name)
.id(id1)
.frame(maxWidth: .infinity, alignment: .leading)
+ .foregroundColor(foreColor)
.accessibilityFont(.title2)
.padding([.bottom], 0.01)
if let desc = description {
diff --git a/TalerWallet1/Views/Settings/SettingsView.swift
b/TalerWallet1/Views/Settings/SettingsView.swift
index a81a9ab..06d552d 100644
--- a/TalerWallet1/Views/Settings/SettingsView.swift
+++ b/TalerWallet1/Views/Settings/SettingsView.swift
@@ -29,17 +29,15 @@ struct SettingsView: View {
@AppStorage("developerMode") var developerMode: Bool = false
#endif
@AppStorage("useHaptics") var useHaptics: Bool = true
- @AppStorage("playSounds") var playSounds: Int = 1
+ @AppStorage("playSoundsI") var playSoundsI: Int = 1
+ @AppStorage("playSoundsB") var playSoundsB: Bool = true
+ @AppStorage("shouldShowWarning") var shouldShowWarning: Bool = true
+// @AppStorage("increaseContrast") var increaseContrast: Bool = false
@AppStorage("talerFont") var talerFont: Int = 0
@AppStorage("developDelay") var developDelay: Bool = false
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
@AppStorage("iconOnly") var iconOnly: Bool = false
-#if TABBAR // Taler Wallet
-#else // GNU Taler
- var hamburgerAction: () -> Void
-#endif
-
@State private var checkDisabled = false
@State private var withDrawDisabled = false
#if DEBUG
@@ -80,14 +78,9 @@ struct SettingsView: View {
}
}
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
-#endif
-#if TABBAR // Taler Wallet
- let hamburger: HamburgerButton? = nil
-#else // GNU Taler
- let hamburger: HamburgerButton = HamburgerButton(action:
hamburgerAction)
#endif
let walletCore = WalletCore.shared
Group {
@@ -100,12 +93,13 @@ struct SettingsView: View {
description: hideDescriptions ? nil :
String(localized: "More info about this app...")) {}
}
#if DEBUG
- Group {
+ if showDevelopItems {
Text("https://bank.taler.grothoff.org/")
Text("https://bank.taler.fdold.eu/")
Text("https://bank.head.taler.net/")
Text("https://bank.test.taler.net/")
Text("https://bank.demo.taler.net/")
+ Text("https://bank.taler.ar/")
}
#endif
if controller.hapticCapability.supportsHaptics {
@@ -113,11 +107,26 @@ struct SettingsView: View {
description: hideDescriptions ? nil :
String(localized: "Vibration Feedback"))
.id("playHaptics")
}
- SettingsSpeaker(name: String(localized: "Play Payment
Sounds"), value: $playSounds,
- description: hideDescriptions ? nil :
String(localized: "When a transaction finished"))
- .id("playSounds")
- SettingsFont(title: String(localized: "Font:"), value:
talerFont, action: redraw)
- .id("font")
+ let playToggle = SettingsToggle(name: String(localized: "Play
Payment Sounds"), value: $playSoundsB,
+ description: hideDescriptions ? nil :
String(localized: "When a transaction finished"))
+ .id("playSounds")
+#if DEBUG
+ if Double.random(in: -100.0...100.0) > 0 {
+ SettingsSpeaker(name: String(localized: "Play Payment
Sounds"), value: $playSoundsI,
+ description: hideDescriptions ? nil :
String(localized: "When a transaction finished"))
+ .id("playSounds")
+ } else { playToggle }
+#else
+ playToggle
+#endif
+// SettingsToggle(name: String(localized: "Increase Contrast"),
value: $increaseContrast, id1: "contrast",
+// description: hideDescriptions ? nil :
String(localized: "If you don't want to set it globally in Settings.app"))
+// .id("increaseContrast")
+ SettingsToggle(name: String(localized: "Show Warnings"),
value: $shouldShowWarning, id1: "warnings",
+ description: hideDescriptions ? nil :
String(localized: "For Delete, Fail & Abort buttons"))
+ .id("showWarnings")
+// SettingsFont(title: String(localized: "Font:"), value:
talerFont, action: redraw)
+// .id("font")
SettingsStyle(title: String(localized: "Liststyle:"),
myListStyle: $myListStyle)
.id("liststyle")
SettingsToggle(name: String(localized: "Minimalistic"), value:
$iconOnly, id1: "minimal",
@@ -237,6 +246,23 @@ struct SettingsView: View {
.buttonStyle(.bordered)
.disabled(checkDisabled)
}.id("test2runTest")
+ SettingsItem(name: String("Run Infinite Transaction
Loop"), id1: "runInfinite",
+ description: hideDescriptions ? nil :
String("Check DB in background")) {
+ let title = "Loop"
+ Button(title) {
+ checkDisabled = true // don't run twice
+ Task { // runs on MainActor
+ symLog.log("Running Infinite Transaction
Loop")
+ do {
+ try await
model.testingInfiniteTransaction(delayMs: 10_000, shouldFetch: true)
+ } catch { // TODO: show error
+ symLog.log(error.localizedDescription)
+ }
+ }
+ }
+ .buttonStyle(.bordered)
+ .disabled(checkDisabled)
+ }.id("runInfiniteLoop")
SettingsItem(name: String("Save Logfile"), id1: "save",
description: hideDescriptions ? nil :
String("Help debugging wallet-core")) {
Button("Save") {
@@ -261,7 +287,6 @@ struct SettingsView: View {
.listStyle(myListStyle.style).anyView
}
.navigationTitle(navTitle)
- .navigationBarItems(leading: hamburger)
.onAppear() {
showDevelopItems = developerMode
hideDescriptions = iconOnly
@@ -292,11 +317,7 @@ struct SettingsView: View {
#if DEBUG
struct SettingsView_Previews: PreviewProvider {
static var previews: some View {
-#if TABBAR // Taler Wallet
SettingsView(stack: CallStack("Preview"), navTitle: "Settings")
-#else // GNU Taler
- SettingsView(stack: CallStack("Preview"), navTitle: "Settings") { }
-#endif
}
}
#endif
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
index 0088fdc..51cd0d4 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
@@ -22,9 +22,12 @@ struct P2pAcceptDone: View {
func reloadOneAction(_ transactionId: String) async throws -> Transaction {
return try await model.getTransactionByIdT(transactionId)
}
-
+ func dismissTopAnimated(_ stack: CallStack) {
+ dismissTop()
+ }
+
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -34,7 +37,7 @@ struct P2pAcceptDone: View {
transactionId: transactionId,
reloadAction: reloadOneAction,
navTitle: navTitle,
- doneAction: { dismissTop() },
+ doneAction: dismissTopAnimated,
abortAction: nil,
deleteAction: nil,
failAction: nil,
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
index 7d4b384..3b64c21 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
@@ -36,7 +36,17 @@ struct P2pPayURIView: View {
bottomAbbrev: String(localized: "Effective:"),
bottomAmount: effective,
large: false, pending: false, incoming:
false,
- baseURL: nil)
+ baseURL: nil,
+ status: nil,
+ summary:
peerPullDebitResponse.contractTerms.summary)
+ let expiration =
peerPullDebitResponse.contractTerms.purse_expiration
+ let (dateString, date) = TalerDater.dateString(from:
expiration)
+ let accessibilityDate = TalerDater.accessibilityDate(date)
?? dateString
+ let accessibilityLabel = String(localized: "Expires:
\(accessibilityDate)")
+ Text("Expires: \(dateString)")
+ .accessibilityFont(.body)
+ .accessibilityLabel(accessibilityLabel)
+// .foregroundColor(colorSchemeContrast == .increased ?
.primary : .secondary)
}
.listStyle(myListStyle.style).anyView
.navigationTitle(navTitle)
@@ -51,25 +61,21 @@ struct P2pPayURIView: View {
.buttonStyle(TalerButtonStyle(type: .prominent))
.padding(.horizontal)
} else {
- WithdrawProgressView(message: url.host ?? "Yikes❗️ no valid
URL")
- .navigationTitle("Contacting Exchange")
+ LoadingView(url: url, message: nil)
+ .task { do {
+ symLog.log(".task")
+ let ppDebitResponse = try await
model.preparePeerPullDebitM(url.absoluteString)
+ peerPullDebitResponse = ppDebitResponse
+ } catch { // TODO: error
+ symLog.log(error.localizedDescription)
+ peerPullDebitResponse = nil
+ } }
}
}
.onAppear() {
symLog.log("onAppear")
DebugViewC.shared.setSheetID(SHEET_PAY_P2P)
}
- .task {
- do { // TODO: cancelled
- symLog.log(".task")
- let ppDebitResponse = try await
model.preparePeerPullDebitM(url.absoluteString)
- peerPullDebitResponse = ppDebitResponse
- } catch { // TODO: error
- symLog.log(error.localizedDescription)
- peerPullDebitResponse = nil
- }
- }
-
}
}
// MARK: -
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
index 05da8fb..f810da7 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
@@ -23,9 +23,15 @@ struct P2pReceiveURIView: View {
@State private var exchange: Exchange? = nil
var body: some View {
- let badURL = "Error in URL: \(url)"
VStack {
if let peerPushCreditResponse {
+ let tosAccepted = exchange?.tosStatus == .accepted
+ if !tosAccepted {
+ ToSButtonView(stack: stack.push(),
+ exchangeBaseUrl:
peerPushCreditResponse.exchangeBaseUrl,
+ viewID: SHEET_RCV_P2P_TOS,
+ p2p: true)
+ }
List {
let raw = peerPushCreditResponse.amountRaw
let effective = peerPushCreditResponse.amountEffective
@@ -38,11 +44,20 @@ struct P2pReceiveURIView: View {
bottomAbbrev: String(localized: "Effective:"),
bottomAmount: effective,
large: false, pending: false, incoming:
true,
- baseURL: nil)
+ baseURL: nil,
+ status: nil,
+ summary:
peerPushCreditResponse.contractTerms.summary)
+ let expiration =
peerPushCreditResponse.contractTerms.purse_expiration
+ let (dateString, date) = TalerDater.dateString(from:
expiration)
+ let accessibilityDate = TalerDater.accessibilityDate(date)
?? dateString
+ let accessibilityLabel = String(localized: "Expires:
\(accessibilityDate)")
+ Text("Expires: \(dateString)")
+ .accessibilityFont(.body)
+ .accessibilityLabel(accessibilityLabel)
+// .foregroundColor(colorSchemeContrast == .increased ?
.primary : .secondary)
}
.listStyle(myListStyle.style).anyView
.navigationTitle(navTitle)
- let tosAccepted = exchange?.tosStatus == .accepted
if tosAccepted {
NavigationLink(destination: LazyView {
P2pAcceptDone(stack: stack.push(),
@@ -53,16 +68,9 @@ struct P2pReceiveURIView: View {
}
.buttonStyle(TalerButtonStyle(type: .prominent))
.padding(.horizontal)
- } else {
- ToSButtonView(stack: stack.push(),
- exchangeBaseUrl:
peerPushCreditResponse.exchangeBaseUrl,
- viewID: SHEET_RCV_P2P_TOS,
- p2p: true)
}
} else {
- // Yikes no details or no baseURL
- WithdrawProgressView(message: url.host ?? badURL)
- .navigationTitle("Contacting Exchange")
+ LoadingView(url: url, message: nil)
}
}
.onAppear() {
diff --git a/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
b/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
index 0a7028e..019152b 100644
--- a/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
+++ b/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
@@ -66,7 +66,9 @@ struct PayTemplateView: View {
bottomAbbrev: String(localized: "Effective:"),
bottomAmount: effective,
large: false, pending: false, incoming:
false,
- baseURL: baseURL)
+ baseURL: baseURL,
+ status: nil,
+ summary: nil)
// TODO: payment: popup with all possible exchanges, check
fees
} else if let balanceDetails = preparePayResult.balanceDetails
{ // Insufficient
Text("You don't have enough \(currency)")
@@ -78,7 +80,9 @@ struct PayTemplateView: View {
bottomAbbrev: String(localized: "Available:"),
bottomAmount: balanceDetails.balanceAvailable,
large: false, pending: false, incoming:
false,
- baseURL: baseURL)
+ baseURL: baseURL,
+ status: nil,
+ summary: nil)
} else {
// TODO: Error - neither effective nor balanceDetails
Text("Error")
@@ -103,9 +107,7 @@ struct PayTemplateView: View {
DebugViewC.shared.setSheetID(SHEET_PAY_TEMPLATE)
}
} else {
- let badURL = "Error in Link: \(url)"
- WithdrawProgressView(message: url.host ?? badURL)
- .navigationTitle("Find Exchange")
+ LoadingView(url: url, message: nil)
.task {
do {
symLog.log(".task")
diff --git a/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
b/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
index 4ce2970..1124006 100644
--- a/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
+++ b/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
@@ -45,8 +45,7 @@ struct PaymentView: View {
let effective = preparePayResult.amountEffective
let terms = preparePayResult.contractTerms
List {
- Text(terms.summary)
- .accessibilityFont(.title3)
+ // TODO: show balanceDetails.balanceAvailable
let baseURL =
preparePayResult.contractTerms.exchanges.first?.url
let raw = preparePayResult.amountRaw
let currency = raw.currencyStr
@@ -62,11 +61,13 @@ struct PaymentView: View {
bottomAbbrev: String(localized: "Effective:"),
bottomAmount: effective,
large: false, pending: false, incoming:
false,
- baseURL: baseURL)
+ baseURL: baseURL,
+ status: nil,
+ summary: terms.summary)
// 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)
+ .accessibilityFont(.headline)
ThreeAmountsV(topTitle: topTitle,
topAbbrev: topAbbrev,
topAmount: raw, fee: nil,
@@ -74,7 +75,9 @@ struct PaymentView: View {
bottomAbbrev: String(localized: "Available:"),
bottomAmount: balanceDetails.balanceAvailable,
large: false, pending: false, incoming:
false,
- baseURL: baseURL)
+ baseURL: baseURL,
+ status: nil,
+ summary: terms.summary)
} else {
// TODO: Error - neither effective nor balanceDetails
Text("Error")
@@ -99,9 +102,7 @@ struct PaymentView: View {
DebugViewC.shared.setSheetID(SHEET_PAYMENT)
}
} else {
- let badURL = "Error in Link: \(url)"
- WithdrawProgressView(message: url.host ?? badURL)
- .navigationTitle("Find Exchange")
+ LoadingView(url: url, message: nil)
.task {
do {
symLog.log(".task")
diff --git a/TalerWallet1/Views/Sheets/QRSheet.swift
b/TalerWallet1/Views/Sheets/QRSheet.swift
index 17cf9f2..33aab47 100644
--- a/TalerWallet1/Views/Sheets/QRSheet.swift
+++ b/TalerWallet1/Views/Sheets/QRSheet.swift
@@ -19,7 +19,7 @@ struct QRSheet: View {
if let scannedURL = URL(string: scannedCode!) {
let scheme = scannedURL.scheme
- if scheme == "taler" {
+ if scheme?.lowercased() == "taler" {
URLSheet(stack: stack.push(), urlToOpen: scannedURL)
} else {
// let _ = print(scannedURL) // TODO: logging
diff --git a/TalerWallet1/Views/Sheets/Refund/RefundURIView.swift
b/TalerWallet1/Views/Sheets/Refund/RefundURIView.swift
index edebcff..66d1870 100644
--- a/TalerWallet1/Views/Sheets/Refund/RefundURIView.swift
+++ b/TalerWallet1/Views/Sheets/Refund/RefundURIView.swift
@@ -34,9 +34,7 @@ struct RefundURIView: View {
suspendAction: model.suspendTransaction,
resumeAction: model.resumeTransaction)
} else {
- let badURL = "Error in Link: \(url)"
- WithdrawProgressView(message: url.host ?? badURL)
- .navigationTitle("Find Exchange")
+ LoadingView(url: url, message: nil)
.task {
do {
symLog.log(".task")
diff --git a/TalerWallet1/Views/Sheets/URLSheet.swift
b/TalerWallet1/Views/Sheets/URLSheet.swift
index 8e38872..09a0fc2 100644
--- a/TalerWallet1/Views/Sheets/URLSheet.swift
+++ b/TalerWallet1/Views/Sheets/URLSheet.swift
@@ -16,7 +16,7 @@ struct URLSheet: View {
@State private var amountToTransfer = Amount.zero(currency: "")
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -26,8 +26,8 @@ struct URLSheet: View {
switch urlCommand {
case .withdraw:
WithdrawURIView(stack: stack.push(), url: urlToOpen)
-// case .withdrawExchange:
-// ManualWithdraw(stack: stack.push(), url: urlToOpen,
exchange: nil, amountToTransfer: $amountToTransfer)
+ case .withdrawExchange:
+ WithdrawExchangeV(stack: stack.push(), url: urlToOpen)
case .pay:
PaymentView(stack: stack.push(), url: urlToOpen)
case .payPull:
diff --git
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift
index 048351d..a15ebd3 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift
@@ -22,9 +22,12 @@ struct WithdrawAcceptDone: View {
func reloadOneAction(_ transactionId: String) async throws -> Transaction {
return try await model.getTransactionByIdT(transactionId)
}
+ func dismissTopAnimated(_ stack: CallStack) {
+ dismissTop()
+ }
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -34,7 +37,7 @@ struct WithdrawAcceptDone: View {
transactionId: transactionId,
reloadAction: reloadOneAction,
navTitle: navTitle,
- doneAction: { dismissTop() },
+ doneAction: dismissTopAnimated,
abortAction: nil,
deleteAction: nil,
failAction: nil,
@@ -44,8 +47,8 @@ struct WithdrawAcceptDone: View {
.interactiveDismissDisabled() // can only use "Done"
button to dismiss
.navigationTitle(navTitle)
} else {
- WithdrawProgressView(message: "Bank Confirmation")
- .navigationTitle("Loading...")
+ LoadingView(url: nil, message: exchangeBaseUrl?.trimURL()
+ ?? "Bank Confirmation")
}
}.onAppear() {
symLog.log("onAppear")
diff --git
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawProgressView.swift
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawProgressView.swift
deleted file mode 100644
index 0c7e906..0000000
---
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawProgressView.swift
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
- * See LICENSE.md
- */
-import SwiftUI
-
-struct WithdrawProgressView: View {
- let message: String
-
- var body: some View {
- VStack {
- Spacer()
- ProgressView()
- Spacer()
- HStack {
- Spacer()
- Text(message)
- .accessibilityFont(.title)
- Spacer()
- }
- Spacer()
- Spacer()
- }
- .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
- }
-}
-
-struct WithdrawProgressView_Previews: PreviewProvider {
- static var previews: some View {
- WithdrawProgressView(message: "message")
- }
-}
diff --git
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
index 0eba89c..7cba37b 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
@@ -4,6 +4,7 @@
*/
import SwiftUI
import SymLog
+import MarkdownUI
struct WithdrawTOSView: View {
private let symLog = SymLogV(0)
@@ -21,12 +22,34 @@ struct WithdrawTOSView: View {
@State var exchangeTOS: ExchangeTermsOfService?
+ func loadToS(_ language: String) async {
+ do {
+ if let exchangeBaseUrl {
+ let acceptedFormat: [String] = [MARKDOWN, PLAINTEXT]
+ let someTOS = try await
model.loadExchangeTermsOfServiceM(exchangeBaseUrl,
+ acceptedFormat:
acceptedFormat,
+ acceptLanguage:
language)
+ exchangeTOS = someTOS
+ } else {
+ // TODO: Yikes! No baseURL
+
+
+
+ }
+ } catch { // TODO: error
+ symLog.log(error.localizedDescription)
+ }
+ }
+
var body: some View {
- VStack {
- Content(symLog: symLog, exchangeTOS: exchangeTOS, myListStyle:
$myListStyle) {
+ let languageCode = Locale.preferredLanguageCode
+// let languageName = Locale.current.localizedString(forLanguageCode:
languageCode)
+ if let exchangeTOS {
+ Content(symLog: symLog, tos: exchangeTOS, myListStyle:
$myListStyle,
+ language: languageCode, languageAction: loadToS) {
Task { // runs on MainActor
do {
- if let exchangeBaseUrl, let exchangeTOS {
+ if let exchangeBaseUrl {
_ = try await
model.setExchangeTOSAcceptedM(exchangeBaseUrl, etag: exchangeTOS.currentEtag)
if acceptAction != nil {
acceptAction!()
@@ -42,76 +65,104 @@ struct WithdrawTOSView: View {
}
}
.navigationTitle(navTitle)
- .overlay {
- if exchangeTOS == nil {
- if let exchangeBaseUrl {
- WithdrawProgressView(message:
exchangeBaseUrl.trimURL())
- .navigationTitle("Loading " + navTitle)
- } else {
- // Yikes!
- WithdrawProgressView(message: "No exchangeBaseUrl!")
- .navigationTitle("Loading " + navTitle)
- }
+ .onAppear() {
+ if viewID > SHEET_WITHDRAWAL {
+ DebugViewC.shared.setSheetID(SHEET_WITHDRAW_TOS)
+ } else {
+ DebugViewC.shared.setViewID(VIEW_WITHDRAW_TOS, stack:
stack.push())
}
}
- }.onAppear() {
- if viewID > SHEET_WITHDRAWAL {
- DebugViewC.shared.setSheetID(SHEET_WITHDRAW_TOS)
- } else {
- DebugViewC.shared.setViewID(VIEW_WITHDRAW_TOS, stack:
stack.push())
- }
- }.task {
- do {
- if let exchangeBaseUrl {
- let acceptedFormat: [String] = [MARKDOWN, PLAINTEXT]
- let someTOS = try await
model.loadExchangeTermsOfServiceM(exchangeBaseUrl,
-
acceptedFormat: acceptedFormat)
- exchangeTOS = someTOS
+ } else {
+ LoadingView(url: nil, message: exchangeBaseUrl?.trimURL() ?? "No
exchangeBaseUrl!")
+ .task {
+ await loadToS(languageCode)
}
- } catch { // TODO: error
- symLog.log(error.localizedDescription)
- }
}
}
}
// MARK: -
extension WithdrawTOSView {
+ struct plaintextToSV: View {
+ let plaintext: String
+
+ var body: some View {
+ let components = plaintext.components(separatedBy: "\n\n")
+
+ ForEach (components, id: \.self) { term0 in
+ let term1 = term0.replacingOccurrences(of: "\n ", with: "
") // newline + 5 blanks
+ let term2 = term1.replacingOccurrences(of: "\n ", with: "
") // newline + 4 blanks
+ let term3 = term2.replacingOccurrences(of: "\n ", with: " ")
// newline + 3 blanks
+ let term4 = term3.replacingOccurrences(of: "\n ", with: " ")
// newline + 2 blanks
+ let term5 = term4.replacingOccurrences(of: "\n ", with: " ")
// newline + 1 blank
+ let term6 = term5.replacingOccurrences(of: "\n", with: " ")
// remove all other linebreaks
+ let term7 = term6.replacingOccurrences(of: " ====", with:
"\n====") // add them back for underscoring
+ let term8 = term7.replacingOccurrences(of: " ----", with:
"\n----") // special for "Highlights:"
+ let term9 = term8.replacingOccurrences(of: " ****", with:
"\n****") // special for "Terms of Service:"
+ Section {
+ Text(term9)
+ .accessibilityFont(.footnote)
+ .foregroundColor(Color(UIColor.label))
+ }
+ } // for
+ }
+ }
+
struct Content: View {
let symLog: SymLogV
- var exchangeTOS: ExchangeTermsOfService?
+ var tos: ExchangeTermsOfService
@Binding var myListStyle: MyListStyle
+ let language: String
+ var languageAction: (String) async -> ()
var acceptAction: () -> ()
+ @State private var selectedLanguage = Locale.preferredLanguageCode
+
var body: some View {
- if let tos = exchangeTOS {
+ let title = String(localized: "Language:", comment: "title of ToS
language selection")
+ List {
+ if tos.tosAvailableLanguages.count > 1 {
+ Picker(title, selection: $selectedLanguage) {
+ ForEach(tos.tosAvailableLanguages, id: \.self) { code
in
+ let languageName =
Locale.current.localizedString(forLanguageCode: code)
+ Text(languageName ?? code)
+ }
+ }
+ .accessibilityFont(.title3)
+ .pickerStyle(.menu)
+ .onAppear() {
+ withAnimation { selectedLanguage = language }
+ }
+ .onChange(of: selectedLanguage) { selected in
+ Task {
+ await languageAction(selected)
+ }
+ }
+ }
+ if tos.contentType == MARKDOWN {
+ Section {
+ let content = MarkdownContent(tos.content)
+ Markdown(content)
+ }
+ } else {
let components = tos.content.components(separatedBy: "\n\n")
-
- List (components, id: \.self) { term0 in
- if tos.contentType == PLAINTEXT {
+ ForEach (components, id: \.self) { term0 in
let term1 = term0.replacingOccurrences(of: "\n ",
with: " ") // newline + 5 blanks
- let term2 = term1.replacingOccurrences(of: "\n" ,
with: " ") // remove all other linebreaks
- let term3 = term2.replacingOccurrences(of: " ====",
with: "\n====") // add them back for underscoring
- let term4 = term3.replacingOccurrences(of: " ----",
with: "\n----") // special for "Highlights:"
- let term5 = term4.replacingOccurrences(of: " ****",
with: "\n****") // special for "Terms of Service:"
-// Text("term0")
- if #available(iOS 16.0, *) {
+ let term2 = term1.replacingOccurrences(of: "\n ",
with: " ") // newline + 4 blanks
+ let term3 = term2.replacingOccurrences(of: "\n ",
with: " ") // newline + 3 blanks
+ let term4 = term3.replacingOccurrences(of: "\n ",
with: " ") // newline + 2 blanks
+ let term5 = term4.replacingOccurrences(of: "\n ",
with: " ") // newline + 1 blank
+ let term6 = term5.replacingOccurrences(of: "\n", with:
" ") // remove all other linebreaks
+ let term7 = term6.replacingOccurrences(of: " ====",
with: "\n====") // add them back for underscoring
+ let term8 = term7.replacingOccurrences(of: " ----",
with: "\n----") // special for "Highlights:"
+ let term9 = term8.replacingOccurrences(of: " ****",
with: "\n****") // special for "Terms of Service:"
Section {
- Text(term5)
- .accessibilityFont(.footnote)
- .foregroundColor(Color(UIColor.label))
- }
- } else {
- Text(term5)
+ Text(term9)
.accessibilityFont(.footnote)
.foregroundColor(Color(UIColor.label))
}
- } else { // MarkDown
- Section {
- Text(term0)
- .accessibilityFont(.body)
- }
- }
- }.safeAreaInset(edge: .bottom) {
+ } // for
+ } // plain text
+ }.safeAreaInset(edge: .bottom) {
let currentEtag = tos.currentEtag
let showButton = tos.acceptedEtag == nil ? true
: tos.acceptedEtag! == tos.currentEtag ?
false
@@ -121,11 +172,8 @@ extension WithdrawTOSView {
.buttonStyle(TalerButtonStyle(type: .prominent))
.padding(.horizontal)
}
- }
- .listStyle(myListStyle.style).anyView
- } else {
- ErrorView(errortext: String(localized: "unknown ToS")) //
TODO: ???
}
+ .listStyle(myListStyle.style).anyView
}
}
}
diff --git
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
index 9ae8592..284d94d 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
@@ -25,9 +25,15 @@ struct WithdrawURIView: View {
@State private var exchange: Exchange? = nil
var body: some View {
- let badURL = "Error in URL: \(url)"
VStack {
if let withdrawalAmountDetails, let exchange {
+ let tosAccepted = exchange.tosStatus == .accepted
+ if !tosAccepted {
+ ToSButtonView(stack: stack.push(),
+ exchangeBaseUrl: exchange.exchangeBaseUrl,
+ viewID: SHEET_WITHDRAW_TOS,
+ p2p: false)
+ }
List {
let raw = withdrawalAmountDetails.amountRaw
let effective = withdrawalAmountDetails.amountEffective
@@ -44,7 +50,9 @@ struct WithdrawURIView: View {
bottomAbbrev: String(localized: "Effective:"),
bottomAmount: effective,
large: false, pending: false, incoming:
true,
- baseURL: exchange.exchangeBaseUrl)
+ baseURL: exchange.exchangeBaseUrl,
+ status: nil, //
common.txState.major.localizedState
+ summary: nil)
let someCoins = SomeCoins(details: withdrawalAmountDetails)
QuiteSomeCoins(someCoins: someCoins,
shouldShowFee: true, // TODO: set to
false if we never charge withdrawal fees
@@ -54,7 +62,7 @@ struct WithdrawURIView: View {
}
.listStyle(myListStyle.style).anyView
.navigationTitle(navTitle)
- if exchange.tosStatus == .accepted {
+ if tosAccepted {
NavigationLink(destination: LazyView {
WithdrawAcceptDone(stack: stack.push(),
exchangeBaseUrl: exchange.exchangeBaseUrl,
@@ -64,16 +72,9 @@ struct WithdrawURIView: View {
}
.buttonStyle(TalerButtonStyle(type: .prominent))
.padding(.horizontal)
- } else {
- ToSButtonView(stack: stack.push(),
- exchangeBaseUrl: exchange.exchangeBaseUrl,
- viewID: SHEET_WITHDRAW_TOS,
- p2p: false)
}
- } else {
- // Yikes no details or no exchange
- WithdrawProgressView(message: url.host ?? badURL)
- .navigationTitle("Contacting Exchange")
+ } else { // no details or no exchange
+ LoadingView(url: url, message: nil)
}
}
.onAppear() {
diff --git a/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift
b/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift
new file mode 100644
index 0000000..dc32e73
--- /dev/null
+++ b/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift
@@ -0,0 +1,53 @@
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
+import SwiftUI
+import taler_swift
+import SymLog
+
+struct WithdrawExchangeV: View {
+ private let symLog = SymLogV(0)
+ let stack: CallStack
+ let navTitle = String(localized: "Checking Link")
+ var url: URL
+
+ @EnvironmentObject private var controller: Controller
+ @EnvironmentObject private var model: WalletModel
+ @State private var exchangeBaseUrl: String?
+ @State private var amountToTransfer = Amount.zero(currency: "")
+
+ var body: some View {
+#if PRINT_CHANGES
+ let _ = Self._printChanges()
+ let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
+#endif
+ Group {
+ if let exchangeBaseUrl {
+ ManualWithdraw(stack: stack.push(),
+ exchangeBaseUrl: exchangeBaseUrl,
+ amountToTransfer: $amountToTransfer)
+ } else {
+ LoadingView(url: url, message: "No exchangeBaseUrl!")
+ }
+ }
+ .task {
+ do { // TODO: cancelled
+ symLog.log(".task")
+ let withdrawExchange = try await
model.loadWithdrawalExchangeForUriM(url.absoluteString)
+ let baseUrl = withdrawExchange.exchangeBaseUrl
+ exchangeBaseUrl = baseUrl
+ if let amount = withdrawExchange.amount {
+ amountToTransfer = amount
+ } else {
+ // is already Amount.zero(currency: "")
+ }
+ // let the controller collect CurrencyInfo from this formerly
unknown exchange
+ let _ = await controller.getInfo(from: baseUrl, model: model)
+ } catch { // TODO: error
+ symLog.log(error.localizedDescription)
+ exchangeBaseUrl = nil
+ }
+ }
+ }
+}
diff --git a/TalerWallet1/Views/Transactions/ManualDetailsV.swift
b/TalerWallet1/Views/Transactions/ManualDetailsV.swift
index 42f827f..9099f45 100644
--- a/TalerWallet1/Views/Transactions/ManualDetailsV.swift
+++ b/TalerWallet1/Views/Transactions/ManualDetailsV.swift
@@ -3,64 +3,151 @@
* See LICENSE.md
*/
import SwiftUI
+import OrderedCollections
import taler_swift
+struct AccountPicker: View {
+ let title: String
+ let value: Int
+ let accountDetails: [WithdrawalExchangeAccountDetails]
+ let action: (Int) -> Void
+
+ @State private var selectedAccount = 0
+
+ var body: some View {
+ Picker(title, selection: $selectedAccount, content: {
+ ForEach(0..<accountDetails.count, id: \.self, content: { index in
+ let detail = accountDetails[index]
+ let bankName = detail.bankName ?? "BankName " + String(index)
+ let amountStr = detail.transferAmount.readableDescription
+ Text(bankName + ": " + amountStr)
+ .tag(index)
+ })
+ })
+ .accessibilityFont(.title3)
+ .pickerStyle(.menu)
+ .onAppear() {
+ withAnimation { selectedAccount = value }
+ }
+ .onChange(of: selectedAccount) { selected in
+ action(selected)
+ }
+ }
+}
+
+struct TransferRestrictionsV: View {
+ let amountStr: String
+ let obtainStr: String
+ let restrictions: [AccountRestriction]?
+
+ @AppStorage("iconOnly") var iconOnly: Bool = false
+
+ @State private var selectedLanguage = Locale.preferredLanguageCode
+
+ var body: some View {
+ VStack(alignment: .leading) {
+ Text(iconOnly ? "Transfer \(amountStr) to the Exchange."
+ : "You need to transfer \(amountStr) from your
regular bank account to the Exchange to receive \(obtainStr) as electronic cash
in this wallet.")
+ .multilineTextAlignment(.leading)
+ if let restrictions {
+ ForEach(restrictions) { restriction in
+ if let hintsI18 = restriction.human_hint_i18n {
+// let sortedDict = OrderedDictionary(uniqueKeys:
hintsI18.keys, values: hintsI18.values)
+// var sorted: OrderedDictionary<String:String>
+ let sortedDict =
OrderedDictionary(uncheckedUniqueKeysWithValues: hintsI18.sorted { $0.key <
$1.key })
+ Picker("Restriction:", selection: $selectedLanguage) {
+ ForEach(sortedDict.keys, id: \.self) {
+ Text(sortedDict[$0] ?? "missing hint")
+ }
+ }
+ } else if let hint = restriction.human_hint {
+ Text(hint)
+ }
+ }
+ }
+ }
+ }
+}
+
struct ManualDetailsV: View {
var common : TransactionCommon
var details : WithdrawalDetails
@AppStorage("iconOnly") var iconOnly: Bool = false
+ @State private var accountID = 0
+ @State private var listID = UUID()
+
+ func redraw(_ newAccount: Int) -> Void {
+ if newAccount != accountID {
+ accountID = newAccount
+ withAnimation { listID = UUID() }
+ }
+ }
var body: some View {
- if let paytoUris = details.exchangePaytoUris {
- let payto = paytoUris[0]
+ if let accountDetails = details.exchangeCreditAccountDetails {
+ if !iconOnly {
+ Text("The Exchange is waiting for your wire-transfer.")
+ .multilineTextAlignment(.leading)
+ .listRowSeparator(.hidden)
+ }
+ AccountPicker(title: String(localized: "Bank"), value: accountID,
+ accountDetails: accountDetails, action: redraw)
+ let account = accountDetails[accountID]
+ let payto = account.paytoUri
let payURL = URL(string: payto)
let iban = payURL?.iban ?? "unknown IBAN"
- let amountStr = common.amountRaw.readableDescription //
TODO: formatter
+ let amountStr = account.transferAmount.readableDescription
// TODO: formatter?
+ let obtainStr = common.amountRaw.readableDescription
+
+ let cryptocode = HStack {
+ Text(details.reservePub)
+ .monospacedDigit()
+ .accessibilityLabel("Cryptocode")
+ Spacer()
+ CopyButton(textToCopy: details.reservePub, vertical: true)
+ .accessibilityLabel("Copy the cryptocode")
+ .disabled(false)
+ } .padding(.leading)
+ let ibanCode = HStack {
+ Text(iban)
+ .monospacedDigit()
+ .accessibilityLabel("IBAN of the exchange")
+ Spacer()
+ CopyButton(textToCopy: iban, vertical: true)
+ .accessibilityLabel("Copy the IBAN")
+ .disabled(false)
+ } .padding(.leading)
+ .padding(.top, -8)
+
+ let step1 = Text(iconOnly ? "**Step 1:** Copy+Paste this subject:"
+ : "**Step 1:** Copy this code and paste
it into the subject/purpose field in your banking app or bank website:")
+ .multilineTextAlignment(.leading)
+ let mandatory = Text("This is mandatory, otherwise your money will
not arrive in this wallet.")
+ .bold()
+ .multilineTextAlignment(.leading)
+ .listRowSeparator(.hidden)
+ let step2 = Text(iconOnly ? "**Step 2:** Copy+Paste this IBAN:"
+ : "**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 (and save it as favourite
for the next time):")
+ .multilineTextAlignment(.leading)
+ .padding(.top)
+ let step3 = Text(iconOnly ? "**Step 3:** Transfer \(amountStr)."
+ : "**Step 3:** Finish the wire transfer
of \(amountStr) in your banking app or website, then this withdrawal will
proceed automatically.")
+ .multilineTextAlignment(.leading)
+ .padding(.top)
Group {
- Text(iconOnly ? "Transfer \(amountStr) to the Exchange."
- : "You need to transfer \(amountStr) from your
regular bank account to the Exchange.")
- Text(iconOnly ? "**Step 1:** Copy+Paste this subject:"
- : "**Step 1:** Copy this code and paste it into
the subject/purpose field in your banking app or bank website:")
- .multilineTextAlignment(.leading)
- .listRowSeparator(.hidden)
+ TransferRestrictionsV(amountStr: amountStr,
+ obtainStr: obtainStr,
+ restrictions: account.creditRestrictions)
+ .listRowSeparator(.visible)
+ step1.listRowSeparator(.hidden)
if !iconOnly {
- Text("This is mandatory, otherwise your money will not
arrive in this wallet.")
- .bold()
- .multilineTextAlignment(.leading)
- .listRowSeparator(.hidden)
+ mandatory
}
- 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(iconOnly ? "**Step 2:** Copy+Paste this IBAN:"
- : "**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 (and save it as favourite for the
next time):")
- .multilineTextAlignment(.leading)
- .padding(.top)
- .listRowSeparator(.hidden)
- HStack {
- Text(iban)
- .monospacedDigit()
- .accessibilityLabel("IBAN of the exchange")
- Spacer()
- CopyButton(textToCopy: iban, vertical: true)
- .accessibilityLabel("Copy the IBAN")
- .disabled(false)
- } .padding(.leading)
- .padding(.top, -8)
- .listRowSeparator(.hidden)
- Text(iconOnly ? "**Step 3:** Transfer \(amountStr)."
- : "**Step 3:** Finish the wire transfer of
\(amountStr) in your banking app or website, then this withdrawal will proceed
automatically.")
- .multilineTextAlignment(.leading)
- .padding(.top)
- .listRowSeparator(.visible)
+ cryptocode.listRowSeparator(.hidden)
+ step2.listRowSeparator(.hidden)
+ ibanCode.listRowSeparator(.hidden)
+ step3.listRowSeparator(.visible)
Text(iconOnly ? "**Alternative:** Use this PayTo-Link:"
: "**Alternative:** If your bank already
supports PayTo, you can use this PayTo-Link instead:")
.multilineTextAlignment(.leading)
@@ -76,8 +163,10 @@ struct ManualDetailsV: View {
.disabled(false)
Spacer()
} .listRowSeparator(.automatic)
- }
+ }.id(listID)
.accessibilityFont(.body)
+ } else {
+ // YIKES No exchangeCreditAccountDetails
}
}
}
@@ -92,8 +181,11 @@ struct ManualDetails_Previews: PreviewProvider {
transactionId: "someTxID",
timestamp: Timestamp(from:
1_666_666_000_000),
txActions: [])
- let details = WithdrawalDetails(type: .manual, reservePub:
"ReSeRvEpUbLiC_KeY_FoR_WiThDrAwAl", reserveIsReady: false,
-
exchangePaytoUris:["payto://iban/SANDBOXX/DE159593?receiver-name=Exchange+Company"])
+ let payto =
"payto://iban/SANDBOXX/DE159593?receiver-name=Exchange+Company"
+ let details = WithdrawalDetails(type: .manual,
+ reservePub:
"ReSeRvEpUbLiC_KeY_FoR_WiThDrAwAl",
+ reserveIsReady: false,
+ confirmed: false)
List {
ManualDetailsV(common: common, details: details)
}
diff --git a/TalerWallet1/Views/Transactions/ThreeAmountsV.swift
b/TalerWallet1/Views/Transactions/ThreeAmountsV.swift
index 0ff8f06..7442714 100644
--- a/TalerWallet1/Views/Transactions/ThreeAmountsV.swift
+++ b/TalerWallet1/Views/Transactions/ThreeAmountsV.swift
@@ -13,6 +13,7 @@ struct ThreeAmountsSheet: View {
var bottomAbbrev: String?
let baseURL: String?
let large: Bool // set to false for QR or IBAN
+ let summary: String?
var body: some View {
let raw = common.amountRaw
@@ -35,7 +36,8 @@ struct ThreeAmountsSheet: View {
bottomAmount: incomplete ? nil : effective,
large: large, pending: pending, incoming: incoming,
baseURL: baseURL,
- status: common.txState.major.localizedState)
+ status: common.txState.major.localizedState,
+ summary: summary)
}
}
// MARK: -
@@ -51,7 +53,8 @@ struct ThreeAmountsV: View {
let pending: Bool
let incoming: Bool
let baseURL: String?
- var status: String?
+ let status: String?
+ let summary: String?
@AppStorage("iconOnly") var iconOnly: Bool = false
@@ -60,24 +63,30 @@ struct ThreeAmountsV: View {
let foreColor = pending ? WalletColors().pendingColor(incoming)
: WalletColors().transactionColor(incoming)
Section {
- AmountView(title: iconOnly ? topAbbrev : topTitle,
- value: topAmount.readableDescription,
+ if let summary {
+ Text(summary)
+ .accessibilityFont(.title2)
+ .lineLimit(4)
+ .padding(.bottom)
+ }
+ AmountRowV(title: iconOnly ? topAbbrev : topTitle,
+ amount: topAmount,
color: labelColor,
large: large)
.padding(.bottom, 4)
.accessibilityElement(children: .combine)
if let fee {
- AmountView(title: iconOnly ? String(localized: "Fee:")
+ AmountRowV(title: iconOnly ? String(localized: "Fee:")
: String(localized: "Exchange
fee:"),
- value: fee.readableDescription,
+ amount: fee,
color: labelColor,
large: false)
.padding(.bottom, 4)
.accessibilityElement(children: .combine)
}
if let bottomAmount {
- AmountView(title: iconOnly ? bottomAbbrev : bottomTitle,
- value: bottomAmount.readableDescription,
+ AmountRowV(title: iconOnly ? bottomAbbrev : bottomTitle,
+ amount: bottomAmount,
color: foreColor,
large: large)
.accessibilityElement(children: .combine)
@@ -122,7 +131,8 @@ struct ThreeAmounts_Previews: PreviewProvider {
Group {
List {
ThreeAmountsSheet(common: common, topAbbrev: "Withdrawal",
- topTitle: "Withdrawal", baseURL: DEMOEXCHANGE,
large: 1==0)
+ topTitle: "Withdrawal", baseURL: DEMOEXCHANGE,
+ large: 1==0, summary: nil)
.safeAreaInset(edge: .bottom) {
Button(String(localized: "Accept"), action: {})
.buttonStyle(TalerButtonStyle(type: .prominent))
diff --git a/TalerWallet1/Views/Transactions/TransactionDetailView.swift
b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
index 4e68137..162cfb7 100644
--- a/TalerWallet1/Views/Transactions/TransactionDetailView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
@@ -27,7 +27,7 @@ struct TransactionDetailView: View {
let transactionId: String
let reloadAction: ((_ transactionId: String) async throws -> Transaction)
let navTitle: String?
- let doneAction: (() -> Void)?
+ let doneAction: ((_ stack: CallStack) -> Void)?
let abortAction: ((_ transactionId: String) async throws -> Void)?
let deleteAction: ((_ transactionId: String) async throws -> Void)?
let failAction: ((_ transactionId: String) async throws -> Void)?
@@ -45,15 +45,6 @@ struct TransactionDetailView: View {
@State var transaction: Transaction = Transaction(dummyCurrency:
DEMOCURRENCY)
@State var viewId = UUID()
- func accessibilityDate(_ date: Date?) -> String? {
- if let date {
- let formatted = date.formatted(date: .long, time: .shortened)
-// print(formatted)
- return formatted
- }
- return nil
- }
-
func loadTransaction() async {
do {
let reloadedTransaction = try await reloadAction(transactionId)
@@ -71,7 +62,7 @@ struct TransactionDetailView: View {
if let transition = notification.userInfo?[TRANSACTIONTRANSITION]
as? TransactionTransition {
if transition.transactionId ==
transaction.common.transactionId { // is the transition for THIS
transaction?
symLog.log(logStr)
- doneAction() // if this view is in a sheet then
dissmiss the sheet
+ doneAction(stack.push()) // if this view is in a
sheet then dissmiss the sheet
return true
}
}
@@ -99,7 +90,7 @@ struct TransactionDetailView: View {
}
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -107,18 +98,18 @@ struct TransactionDetailView: View {
let pending = transaction.isPending
let locale = TalerDater.shared.locale
let (dateString, date) = TalerDater.dateString(from: common.timestamp)
- let accessibilityDate = accessibilityDate(date) ?? dateString
+ let accessibilityDate = TalerDater.accessibilityDate(date) ??
dateString
let navTitle2 = transaction.localizedType
VStack {
List {
if developerMode {
if transaction.isSuspendable { if let suspendAction {
TransactionButton(transactionId: common.transactionId,
- command: .suspend, action:
suspendAction)
+ command: .suspend, warning: nil,
action: suspendAction)
} }
if transaction.isResumable { if let resumeAction {
TransactionButton(transactionId: common.transactionId,
- command: .resume, action:
resumeAction)
+ command: .resume, warning: nil,
action: resumeAction)
} }
} // Suspend + Resume buttons
Text(dateString)
@@ -126,36 +117,48 @@ struct TransactionDetailView: View {
.accessibilityLabel(accessibilityDate)
.foregroundColor(colorSchemeContrast == .increased ?
.primary : .secondary)
.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()
- Text("Status: \(common.txState.major.localizedState)")
+ VStack(alignment: .trailing) {
+ let majorState = common.txState.major.localizedState
+ let minorState = common.txState.minor?.localizedState ??
nil
+ let state = transaction.isPending ? minorState ??
majorState
+ : majorState
+ HStack {
+ Text(verbatim: "|") // only reason for this
leading-aligned text is to get a nice full length listRowSeparator
+ .accessibilityHidden(true)
+ .foregroundColor(Color.clear)
+ Spacer()
+ Text("Status: \(state)")
+ .multilineTextAlignment(.trailing)
+ }
} .listRowSeparator(.automatic)
.accessibilityFont(.title)
TypeDetail(transaction: $transaction, hasDone: doneAction !=
nil)
+// if transaction.isRetryable { if let retryAction {
+// TransactionButton(transactionId: common.transactionId,
command: .retry,
+// warning: nil, action: abortAction)
+// } } // Retry button
if transaction.isAbortable { if let abortAction {
- TransactionButton(transactionId: common.transactionId,
- command: .abort, action: abortAction)
+ TransactionButton(transactionId: common.transactionId,
command: .abort,
+ warning: String(localized: "Are you sure
you want to abort this transaction?"),
+ action: abortAction)
} } // Abort button
if transaction.isFailable { if let failAction {
- TransactionButton(transactionId: common.transactionId,
- command: .fail, action: failAction)
+ TransactionButton(transactionId: common.transactionId,
command: .fail,
+ warning: String(localized: "Are you sure
you want to fail this transaction?"),
+ action: failAction)
} } // Fail button
if transaction.isDeleteable { if let deleteAction {
- TransactionButton(transactionId: common.transactionId,
- command: .delete, action: deleteAction)
+ TransactionButton(transactionId: common.transactionId,
command: .delete,
+ warning: String(localized: "Are you sure
you want to delete this transaction?"),
+ action: deleteAction)
} } // Delete button
if let doneAction {
- Button(transaction.shouldConfirm ? "Confirm later" :
"Done", action: doneAction)
+ Button(transaction.shouldConfirm ? "Confirm later" :
"Done", action: { doneAction(stack.push()) } )
.buttonStyle(TalerButtonStyle(type:
transaction.shouldConfirm ? .bordered : .prominent))
} // Done button
}.id(viewId) // change viewId to enforce a draw update
.listStyle(myListStyle.style).anyView
-// .safeAreaInset(edge: .bottom) {
-// }
} // Group
.onNotification(.TransactionExpired) { notification in
// TODO: Alert user that this tx just expired
@@ -217,12 +220,12 @@ struct TransactionDetailView: View {
var body: some View {
VStack(alignment: .leading) { // Show Hint that User should
Confirm on bank website
if !iconOnly {
- Text("Waiting for bank confirmation")
- .fixedSize(horizontal: false, vertical: true) //
wrap in scrollview
+ Text("The bank is waiting for your confirmation.")
+// .fixedSize(horizontal: false, vertical: true)
// wrap in scrollview
.multilineTextAlignment(.leading) //
otherwise
.listRowSeparator(.hidden)
}
- Link("Confirm with bank", destination: destination)
+ Link("Confirm now", destination: destination)
.buttonStyle(TalerButtonStyle(type: .prominent, badge:
CONFIRM_BANK))
.accessibilityHint("Will go to bank website to confirm
this withdrawal.")
}
@@ -239,10 +242,10 @@ struct TransactionDetailView: View {
Group {
switch transaction {
case .dummy(_):
- let title = ""
+ let title = EMPTYSTRING
Text(title)
.accessibilityFont(.body)
- case .withdrawal(let withdrawalTransaction):
+ case .withdrawal(let withdrawalTransaction): Group {
let details = withdrawalTransaction.details
if pending {
if transaction.isPendingKYC {
@@ -257,50 +260,57 @@ struct TransactionDetailView: View {
case .manual: // "Make a wire
transfer of \(amount) to"
ManualDetailsV(common: common, details:
withdrawalDetails)
- case .bankIntegrated: // "Confirm with
bank"
- if !transaction.isPendingKYC {
// both should never happen, but...
+ case .bankIntegrated: // "Confirm now"
(with bank)
+ if !transaction.isPendingKYC {
// cannot confirm if KYC is needed first
let confirmed =
withdrawalDetails.confirmed ?? false
if !confirmed {
if let confirmationUrl =
withdrawalDetails.bankConfirmationUrl {
if let destination =
URL(string: confirmationUrl) {
ConfirmationButton(destination: destination)
- }
- }
- }
- }
+ } } } }
} // switch
- } // ManualDetails or Confirm with bank
+ } // ManualDetails or Confirm now (with bank)
ThreeAmountsSheet(common: common, topAbbrev:
String(localized: "Chosen:"),
topTitle: String(localized: "Chosen
amount to withdraw:"),
- baseURL: details.exchangeBaseUrl,
large: false)
- case .payment(let paymentTransaction):
+ baseURL: details.exchangeBaseUrl,
large: false, summary: nil)
+ }
+ case .payment(let paymentTransaction): Group {
let details = paymentTransaction.details
Text(details.info.summary)
.accessibilityFont(.title3)
ThreeAmountsSheet(common: common, topAbbrev:
String(localized: "Pay:"),
topTitle: String(localized: "Sum to be
paid:"),
- baseURL: nil, large: true) //
TODO: baseURL
- case .refund(let refundTransaction):
+ baseURL: nil, large: true, summary:
details.info.summary) // TODO: baseURL
+ }
+ case .refund(let refundTransaction): Group {
let details = refundTransaction.details
// TODO: more details
ThreeAmountsSheet(common: common, topAbbrev:
String(localized: "Refunded:"),
topTitle: String(localized: "Refunded
amount:"),
- baseURL: nil, large: true) //
TODO: baseURL
- case .reward(let rewardTransaction):
- let details = rewardTransaction.details
// TODO: more details
+ baseURL: nil, large: true, summary:
nil) // TODO: baseURL, summary
+ }
+ case .reward(let rewardTransaction): Group {
+ let details = rewardTransaction.details
ThreeAmountsSheet(common: common, topAbbrev:
String(localized: "Reward:"),
topTitle: String(localized: "Received
Reward:"),
- baseURL: details.exchangeBaseUrl,
large: true)
- case .refresh(let refreshTransaction):
+ baseURL: details.exchangeBaseUrl,
large: true, summary: nil) // TODO: summary
+ }
+ case .refresh(let refreshTransaction): Group {
let details = refreshTransaction.details
// TODO: details
+ Text(details.refreshReason.rawValue)
ThreeAmountsSheet(common: common, topAbbrev:
String(localized: "Refreshed:"),
topTitle: String(localized: "Refreshed
amount:"),
- baseURL: nil, large: true) //
TODO: baseURL
- case .peer2peer(let p2pTransaction):
- let details = p2pTransaction.details
// TODO: details
- Text(details.info.summary)
- .accessibilityFont(.title2)
- .lineLimit(4)
- .padding(.bottom)
+ baseURL: nil, large: true, summary:
nil) // TODO: baseURL
+ }
+ case .peer2peer(let p2pTransaction): Group {
+ let details = p2pTransaction.details
+ let expiration = details.info.expiration
+ let (dateString, date) = TalerDater.dateString(from:
expiration)
+ let accessibilityDate =
TalerDater.accessibilityDate(date) ?? dateString
+ let accessibilityLabel = String(localized: "Expires:
\(accessibilityDate)")
+ Text("Expires: \(dateString)")
+ .accessibilityFont(.body)
+ .accessibilityLabel(accessibilityLabel)
+// .foregroundColor(colorSchemeContrast == .increased ?
.primary : .secondary)
// 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 {
if transaction.isPendingReady {
@@ -320,7 +330,9 @@ struct TransactionDetailView: View {
let colon = ":"
ThreeAmountsSheet(common: common, topAbbrev:
transaction.localizedType + colon,
topTitle: transaction.localizedType +
colon,
- baseURL: details.exchangeBaseUrl,
large: false)
+ baseURL: details.exchangeBaseUrl,
large: false,
+ summary: details.info.summary)
+ } // p2p
} // switch
} // Group
}
diff --git a/TalerWallet1/Views/Transactions/TransactionRowView.swift
b/TalerWallet1/Views/Transactions/TransactionRowView.swift
index aafc403..1b80bc2 100644
--- a/TalerWallet1/Views/Transactions/TransactionRowView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionRowView.swift
@@ -35,74 +35,13 @@ struct IconBadge: View {
}.accessibilityHidden(true)
}
}
-struct TransactionRowContentV: View {
- var centerTop: String
- var centerBottom: String
- let isHorizontal: Bool
- let pending: Bool
- let incoming: Bool
- let shouldConfirm: Bool
- let needsKYC: Bool
- let done: Bool
- let foreColor:Color
-
- @Environment(\.colorSchemeContrast) private var colorSchemeContrast
-
- public static func width(titles: (String, String?), isHorizontal: Bool,
- sizeCategory: ContentSizeCategory) -> CGFloat {
- let imageFont = TalerFont.uiFont(.largeTitle)
- let uiFont1 = TalerFont.uiFont(.headline)
- let uiFont2 = TalerFont.uiFont(.callout)
-
- let image = "++"
- let imageWidth = image.widthOfString(usingUIFont: imageFont,
sizeCategory) + 8.0 // spacing: 8
- let (title1, title2) = titles
- let title1Width = title1.widthOfString(usingUIFont: uiFont1,
sizeCategory)
- var title2Width = 0.0
- var totalWidth = title1Width
- if let title2 {
- title2Width = title2.widthOfString(usingUIFont: uiFont2,
sizeCategory)
- let blankStr = " "
- totalWidth += blankStr.widthOfString(usingUIFont: uiFont1,
sizeCategory) + title2Width
- }
-
-// let logStr = String(format: "image: %.2f title: %.2f total: %.2f",
imageWidth, max(title1Width, title2Width), totalWidth)
-// print(logStr)
- return imageWidth + (isHorizontal ? totalWidth
- : max(title1Width, title2Width))
- }
-
- var body: some View {
- let iconBadge = IconBadge(foreColor: foreColor, done: done, incoming:
incoming,
- shouldConfirm: shouldConfirm, needsKYC: needsKYC)
- let doneOrPending = done || pending
- let increasedContrast = colorSchemeContrast == .increased
- let textColor = doneOrPending ? .primary :
- increasedContrast ? .secondary : WalletColors().gray3
- HStack(spacing: 8) {
- iconBadge
- VStack(alignment: .leading) {
- Text(centerTop)
- .foregroundColor(textColor)
- .strikethrough(!doneOrPending, color: .red)
- .accessibilityFont(.headline)
-// .fontWeight(.medium) iOS 16
- .padding(.bottom, -2.0)
- .accessibilityLabel(doneOrPending ? centerTop : centerTop
+ ", canceled")
- Text(centerBottom)
- .foregroundColor(textColor)
- .accessibilityFont(.callout)
- }
- }
- }
-}
struct TransactionRowView: View {
let transaction : Transaction
let currency: String
@Environment(\.sizeCategory) var sizeCategory
- @EnvironmentObject private var controller: Controller
+ @Environment(\.colorSchemeContrast) private var colorSchemeContrast
func needVStack(available: CGFloat, contentWidth: CGFloat, valueWidth:
CGFloat) -> Bool {
available < (contentWidth + valueWidth + 40)
@@ -110,11 +49,12 @@ struct TransactionRowView: View {
var body: some View {
let common = transaction.common
- let amount = common.amountEffective
let pending = transaction.isPending
let needsKYC = transaction.isPendingKYC
let shouldConfirm = transaction.shouldConfirm
let done = transaction.isDone
+ let doneOrPending = done || pending
+ let increasedContrast = colorSchemeContrast == .increased
let details = transaction.detailsToShow()
let keys = details.keys
@@ -122,32 +62,91 @@ struct TransactionRowView: View {
let incoming = common.incoming()
let foreColor = pending ? WalletColors().pendingColor(incoming)
: done ? WalletColors().transactionColor(incoming)
- : WalletColors().incompleteColor
- let currencyInfo = controller.info(for: currency,
controller.currencyTicker)
- SingleAxisGeometryReader { width in
- Group {
- let amountStr = amount.string(currencyInfo)
- let amountWidth = amountStr.width(largeAmountFont: false,
sizeCategory)
+ : WalletColors().uncompletedColor
+ let textColor = doneOrPending ? .primary :
+ increasedContrast ? .secondary : Color(.tertiaryLabel)
- let contentWidth = TransactionRowContentV.width(titles:
(transaction.localizedType, dateString),
- isHorizontal: false,
sizeCategory: sizeCategory)
- let needVStack = needVStack(available: width, contentWidth:
contentWidth, valueWidth: amountWidth)
+ let iconBadge = IconBadge(foreColor: foreColor, done: done, incoming:
incoming,
+ shouldConfirm: shouldConfirm, needsKYC:
needsKYC)
+ let amountV = AmountV(common.amountEffective)
+ .foregroundColor(foreColor)
- AmountRowV(amountStr: amountStr, amountColor: foreColor,
doneOrPending: done || pending,
- largeAmountFont: false, fitsHorizontal: !needVStack,
vertAlignment: .center) {
+ let topString = transaction.localizedType
+ let centerTop = Text(topString)
+ .foregroundColor(textColor)
+ .strikethrough(!doneOrPending, color: .red)
+ .accessibilityFont(.headline)
+// .fontWeight(.medium) iOS 16
+ .padding(.bottom, -2.0)
+ .accessibilityLabel(doneOrPending ? topString : topString + ",
canceled")
+ let centerBottom = Text(dateString)
+ .foregroundColor(textColor)
+ .accessibilityFont(.callout)
- TransactionRowContentV(centerTop:
transaction.localizedType,
- centerBottom: dateString,
isHorizontal: true,
- pending: pending, incoming:
incoming,
- shouldConfirm: shouldConfirm, needsKYC:
needsKYC,
- done: done, foreColor:
foreColor)
+ let layout1 = HStack(spacing: 0) {
+ HStack(spacing: -4) {
+ iconBadge
+ Spacer(minLength: 0)
+ VStack(alignment: .leading) {
+ centerTop
+ centerBottom
}
- .accessibilityElement(children: .combine)
- .accessibilityValue(needsKYC ? ". Needs K Y C" :
- shouldConfirm ? ". Needs bank confirmation"
: EMPTYSTRING)
- .accessibilityHint("Will go to detail view.")
+ Spacer(minLength: 2)
}
+ amountV
+ }
+
+ let layout2 = VStack(spacing: 0) {
+ centerTop
+ HStack(spacing: 8) {
+ HStack(spacing: 0) {
+ iconBadge//.border(.blue)
+ Spacer(minLength: 0)
+ centerBottom
+ Spacer(minLength: 2)
+ }//.border(.green)
+ amountV//.border(.red)
+ }//.border(.orange)
+ }
+
+ let layout3 = VStack(spacing: 0) {
+ HStack(spacing: 8) {
+ HStack(spacing: 0) {
+ iconBadge
+ Spacer(minLength: 0)
+ centerTop
+ Spacer(minLength: 2)
+ }
+ amountV
+ }
+ centerBottom
+ }
+
+ let layout4 = VStack(spacing: 0) {
+ centerTop
+ HStack(spacing: -4) {
+ iconBadge//.border(.green)
+ Spacer(minLength: 2
+ )
+ amountV//.border(.green)
+ }//.border(.orange)
+ centerBottom
+ }
+
+ Group {
+ if #available(iOS 16.0, *) {
+ ViewThatFits(in: .horizontal) {
+ layout1//.border(.green)
+ layout2
+ layout3//.border(.red)
+ layout4//.border(.blue)
+ }
+ } else { layout4 } // view for iOS 15
}
+ .accessibilityElement(children: .combine)
+ .accessibilityValue(needsKYC ? ". Needs K Y C" :
+ shouldConfirm ? ". Needs bank confirmation" :
EMPTYSTRING)
+ .accessibilityHint("Will go to detail view.")
}
}
// MARK: -
@@ -187,7 +186,7 @@ extension Transaction { // for PreViews
:
WithdrawalDetails.WithdrawalType.bankIntegrated,
reservePub:
"PuBlIc_KeY_oF_tHe_ReSeRvE",
reserveIsReady: false,
- exchangePaytoUris: pending ? [payto]
: nil)
+ confirmed: false)
let wDetails = WithdrawalTransactionDetails(exchangeBaseUrl:
DEMOEXCHANGE,
withdrawalDetails:
withdrawalDetails)
self = .withdrawal(WithdrawalTransaction(common: common, details:
wDetails))
diff --git a/TalerWallet1/Views/Transactions/TransactionsListView.swift
b/TalerWallet1/Views/Transactions/TransactionsListView.swift
index cce00e6..7614fe5 100644
--- a/TalerWallet1/Views/Transactions/TransactionsListView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionsListView.swift
@@ -21,7 +21,7 @@ struct TransactionsListView: View {
@State private var upAction: () -> Void = {}
@State private var downAction: () -> Void = {}
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
@@ -87,7 +87,7 @@ struct TransactionsArraySliceV: View {
@EnvironmentObject private var model: WalletModel
var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
let _ = Self._printChanges()
let _ = symLog?.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
diff --git a/Taler_Wallet Info.plist b/Taler_Wallet Info.plist
index 811f070..11d4a04 100644
--- a/Taler_Wallet Info.plist
+++ b/Taler_Wallet Info.plist
@@ -18,7 +18,6 @@
<string>taler</string>
<string>ext+taler</string>
<string>web+taler</string>
- <string>payto</string>
</array>
</dict>
</array>
diff --git a/TestFlight/WhatToTest.en-US.txt b/TestFlight/WhatToTest.en-US.txt
index 82745ef..1b2e711 100644
--- a/TestFlight/WhatToTest.en-US.txt
+++ b/TestFlight/WhatToTest.en-US.txt
@@ -1,3 +1,37 @@
+Version 0.9.4 (1)
+
+- now accepts uppercase TALER://PAY URIs
+
+
+Version 0.9.4 (0)
+
+- bug fixes
+- first release of 0.9.4
+
+
+Version 0.9.3 (34)
+
+• New feature: Demo Bank Website button in Banking
+- Balances is shown after a transaction finished
+
+
+Version 0.9.3 (33)
+
+• New feature: ToS with Markdown
+- background colors adapted for WCAG AA
+
+
+Version 0.9.3 (32)
+
+• New feature: Currency Conversion
+• New feature: ToS with multiple languages
+• New feature: Landscape
+• New feature: Warnings for Delete, Fail & Abort
+
+- Bugfix: Manual withdrawals no longer create multiple identical transactions
+- "Banking" instead of "Exchanges" - withdraw and deposit
+- Haptic feedback for Copy/Share buttons
+
Version 0.9.3 (31)
diff --git a/taler-swift/Sources/taler-swift/Amount.swift
b/taler-swift/Sources/taler-swift/Amount.swift
index 301f555..9866301 100644
--- a/taler-swift/Sources/taler-swift/Amount.swift
+++ b/taler-swift/Sources/taler-swift/Amount.swift
@@ -66,6 +66,7 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
public static let decimalSeparator = "."
+ /// 3-char ISO 4217 code for global currency. Regional MUST be >= 4 letters
/// The currency of the amount. Cannot be changed later, except...
private var currency: String
@@ -250,7 +251,7 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
public init(currency: String, cent: UInt64) {
self.currency = currency
self.integer = cent / 100 // For existing currencies, fractional
digits could be 0, 2 or 3
- self.fraction = UInt32(cent - (self.integer * 100))
+ self.fraction = UInt32(cent - (self.integer * 100)) *
(Self.fractionalBase() / 100)
}
/// Initializes an amount from a decoder.
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [taler-taler-ios] branch master updated (a123c3a -> 4167b6d),
gnunet <=
- [taler-taler-ios] 06/69: withdraw-exchange, gnunet, 2024/01/19
- [taler-taler-ios] 01/69: WithdrawExchangeV, gnunet, 2024/01/19
- [taler-taler-ios] 04/69: AccountPicker, remove exchangePaytoUris, gnunet, 2024/01/19
- [taler-taler-ios] 03/69: CurrencyInfo from Exchange, gnunet, 2024/01/19
- [taler-taler-ios] 25/69: colors adapted for WCAG AA, gnunet, 2024/01/19
- [taler-taler-ios] 05/69: ToS language, gnunet, 2024/01/19
- [taler-taler-ios] 19/69: cleanup, gnunet, 2024/01/19
- [taler-taler-ios] 02/69: LoadingView, gnunet, 2024/01/19
- [taler-taler-ios] 12/69: PRINT_CHANGES, gnunet, 2024/01/19
- [taler-taler-ios] 07/69: Haptics, gnunet, 2024/01/19