[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-taler-ios] branch master updated (e082595 -> e79881c)
From: |
gnunet |
Subject: |
[taler-taler-ios] branch master updated (e082595 -> e79881c) |
Date: |
Mon, 13 Nov 2023 21:27:05 +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 e082595 Bump version to 0.9.3 (24)
new 27dd64c Startup Chime
new a93686d Transaction, L10N
new 5764915 Exchange with scopeInfo
new 592a2ab DD51 fractional base
new 7950b99 fix tests
new e0dc795 RequestPurpose
new 93bdf94 Amount(currency:cent:)
new d039dbd Cleanup
new 9ce01d5 cleanup, zero
new 1ff1bc3 @Published currencyTicker
new 2b0249b Debugging
new d02a14e DD51 Currency rendering
new 9da5f4b once at very first startup
new 73c64bd Debugging: Delay currency info
new 8197093 About with explicit link to taler.net
new 23dd90a TransactionsArraySliceV
new 53ee8de amountToTransfer Balances
new 8a27e5e amountToTransfer Transactions
new 0d09d7d amountToTransfer Pending
new 1afc225 amountToTransfer Request
new e18bc10 amountToTransfer Send
new 3125395 amountToTransfer ManualWithdraw
new ac45ed6 amountToTransfer Exchange
new 04db105 Exchange needs Balance for Deposit
new 02e3908 amountToTransfer Currency
new b274d07 Minimalistic
new fe47941 Debugging
new ed0c4f3 Cleanup property wrappers
new b6575a3 less logging
new 4d6934b Bugfix
new 65b61e9 Use `te´and `ku´ for previews
new 5f681aa Shortcuts (50,25,10,5)
new a5a8a71 Announce shouldn't change screen
new e55bb04 Previews
new a286bb9 Check Available, accessibility
new e79881c Bump version to 0.9.3 (25)
The 36 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
TalerWallet.xcodeproj/project.pbxproj | 18 +--
TalerWallet1/Backend/WalletCore.swift | 6 +-
TalerWallet1/Controllers/Controller.swift | 51 ++++--
TalerWallet1/Controllers/DebugViewC.swift | 2 +-
TalerWallet1/Controllers/TalerWallet1App.swift | 1 -
TalerWallet1/Helper/CurrencySpecification.swift | 40 ++++-
TalerWallet1/Model/Model+Exchange.swift | 17 +-
TalerWallet1/Model/Transaction.swift | 72 ++++++---
TalerWallet1/Views/Balances/BalanceRowView.swift | 23 ++-
TalerWallet1/Views/Balances/BalancesListView.swift | 13 +-
.../Views/Balances/BalancesSectionView.swift | 89 ++++++-----
TalerWallet1/Views/Balances/PendingRowView.swift | 39 +++--
TalerWallet1/Views/Exchange/ExchangeListView.swift | 22 +--
TalerWallet1/Views/Exchange/ExchangeRowView.swift | 13 +-
.../Views/Exchange/ExchangeSectionView.swift | 24 +--
TalerWallet1/Views/Exchange/ManualWithdraw.swift | 39 ++---
.../Views/Exchange/ManualWithdrawDone.swift | 9 +-
TalerWallet1/Views/HelperViews/AmountRowV.swift | 4 +-
TalerWallet1/Views/HelperViews/BarGraph.swift | 3 +-
TalerWallet1/Views/HelperViews/CurrencyField.swift | 115 +++++++-------
.../Views/HelperViews/CurrencyInputView.swift | 76 +++++----
.../Views/HelperViews/TransactionButton.swift | 71 +++------
.../Views/HelperViews/View+fitsSideBySide.swift | 2 +-
TalerWallet1/Views/Main/MainView.swift | 36 +++--
TalerWallet1/Views/Peer2peer/RequestPayment.swift | 47 +++---
.../{PaymentPurpose.swift => RequestPurpose.swift} | 37 ++---
TalerWallet1/Views/Peer2peer/SendAmount.swift | 87 +++++++----
TalerWallet1/Views/Peer2peer/SendDoneV.swift | 5 +-
TalerWallet1/Views/Peer2peer/SendPurpose.swift | 20 +--
TalerWallet1/Views/Settings/AboutView.swift | 71 +++++----
TalerWallet1/Views/Settings/SettingsView.swift | 5 +-
.../Views/Sheets/P2P_Sheets/P2pAcceptDone.swift | 2 +-
.../Views/Sheets/P2P_Sheets/P2pPayURIView.swift | 2 +-
.../Sheets/P2P_Sheets/P2pReceiveURIView.swift | 7 +-
.../Views/Sheets/Payment/PayTemplateView.swift | 5 +-
.../Views/Sheets/Payment/PaymentView.swift | 76 +++++----
TalerWallet1/Views/Sheets/URLSheet.swift | 1 +
.../WithdrawAcceptDone.swift | 2 +-
.../WithdrawBankIntegrated/WithdrawTOSView.swift | 9 +-
.../WithdrawBankIntegrated/WithdrawURIView.swift | 4 +-
.../Views/Transactions/ManualDetailsV.swift | 4 +-
.../Views/Transactions/ThreeAmountsV.swift | 4 +-
.../Views/Transactions/TransactionDetailView.swift | 2 +-
.../Views/Transactions/TransactionRowView.swift | 28 ++--
.../Views/Transactions/TransactionsListView.swift | 26 ++--
TestFlight/WhatToTest.en-US.txt | 11 ++
taler-swift/Sources/taler-swift/Amount.swift | 172 ++++++++++++++++-----
.../Tests/taler-swiftTests/AmountTests.swift | 14 +-
48 files changed, 820 insertions(+), 606 deletions(-)
rename TalerWallet1/Views/Peer2peer/{PaymentPurpose.swift =>
RequestPurpose.swift} (75%)
diff --git a/TalerWallet.xcodeproj/project.pbxproj
b/TalerWallet.xcodeproj/project.pbxproj
index 83e73fd..242ba39 100644
--- a/TalerWallet.xcodeproj/project.pbxproj
+++ b/TalerWallet.xcodeproj/project.pbxproj
@@ -67,7 +67,7 @@
4E3EAE472A990778009F1BE8 /* QuiteSomeCoins.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4EBA82AC2A3F580500E5F39A /*
QuiteSomeCoins.swift */; };
4E3EAE482A990778009F1BE8 /* PayTemplateView.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4EBA56402A7FF5200084948B /*
PayTemplateView.swift */; };
4E3EAE492A990778009F1BE8 /* ManualWithdrawDone.swift in Sources
*/ = {isa = PBXBuildFile; fileRef = 4EB431662A1E55C700C5690E /*
ManualWithdrawDone.swift */; };
- 4E3EAE4A2A990778009F1BE8 /* PaymentPurpose.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4E9320462A164BC700A87B0E /*
PaymentPurpose.swift */; };
+ 4E3EAE4A2A990778009F1BE8 /* RequestPurpose.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4E9320462A164BC700A87B0E /*
RequestPurpose.swift */; };
4E3EAE4B2A990778009F1BE8 /* ShareSheet.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E753A072A0B6A5F002D9328 /* ShareSheet.swift */;
};
4E3EAE4C2A990778009F1BE8 /* AmountView.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4EB095492989CBFE0043A8A1 /* AmountView.swift */;
};
4E3EAE4D2A990778009F1BE8 /* P2pAcceptDone.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E3B4BC22A42252300CC88B8 /* P2pAcceptDone.swift
*/; };
@@ -167,7 +167,7 @@
4E8E25332A1CD39700A27BFA /* EqualIconWidthDomain.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4E8E25322A1CD39700A27BFA /*
EqualIconWidthDomain.swift */; };
4E9320432A14F6EA00A87B0E /* WalletColors.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E9320422A14F6EA00A87B0E /* WalletColors.swift
*/; };
4E9320452A1645B600A87B0E /* RequestPayment.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4E9320442A1645B600A87B0E /*
RequestPayment.swift */; };
- 4E9320472A164BC700A87B0E /* PaymentPurpose.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4E9320462A164BC700A87B0E /*
PaymentPurpose.swift */; };
+ 4E9320472A164BC700A87B0E /* RequestPurpose.swift in Sources */
= {isa = PBXBuildFile; fileRef = 4E9320462A164BC700A87B0E /*
RequestPurpose.swift */; };
4E9796902A3765ED006F73BC /* AgePicker.swift in Sources */ =
{isa = PBXBuildFile; fileRef = 4E97968F2A3765ED006F73BC /* AgePicker.swift */;
};
4E983C292ADBDD3500FA9CC5 /* SingleAxisGeometryReader.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4E983C282ADBDD3500FA9CC5 /*
SingleAxisGeometryReader.swift */; };
4E983C2A2ADBDD3500FA9CC5 /* SingleAxisGeometryReader.swift in
Sources */ = {isa = PBXBuildFile; fileRef = 4E983C282ADBDD3500FA9CC5 /*
SingleAxisGeometryReader.swift */; };
@@ -344,7 +344,7 @@
4E8E25322A1CD39700A27BFA /* EqualIconWidthDomain.swift */ =
{isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path =
EqualIconWidthDomain.swift; sourceTree = "<group>"; };
4E9320422A14F6EA00A87B0E /* WalletColors.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= WalletColors.swift; sourceTree = "<group>"; };
4E9320442A1645B600A87B0E /* RequestPayment.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= RequestPayment.swift; sourceTree = "<group>"; };
- 4E9320462A164BC700A87B0E /* PaymentPurpose.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= PaymentPurpose.swift; sourceTree = "<group>"; };
+ 4E9320462A164BC700A87B0E /* RequestPurpose.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= RequestPurpose.swift; sourceTree = "<group>"; };
4E97968F2A3765ED006F73BC /* AgePicker.swift */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path
= AgePicker.swift; sourceTree = "<group>"; };
4E983C282ADBDD3500FA9CC5 /* SingleAxisGeometryReader.swift */ =
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType =
sourcecode.swift; path = SingleAxisGeometryReader.swift; sourceTree =
"<group>"; };
4E983C2B2ADC416800FA9CC5 /* View+fitsSideBySide.swift */ = {isa
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift;
path = "View+fitsSideBySide.swift"; sourceTree = "<group>"; };
@@ -742,8 +742,8 @@
4EB095472989CBFE0043A8A1 /* Buttons.swift */,
4EF840A62A0B85F400EE0D47 /* CopyShare.swift */,
4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */,
- 4E53A33629F50B7B00830EC2 /* CurrencyField.swift
*/,
4EA551242A2C923600FEC9A8 /*
CurrencyInputView.swift */,
+ 4E53A33629F50B7B00830EC2 /* CurrencyField.swift
*/,
4EEC157229F8242800D46A03 /*
QRGeneratorView.swift */,
4E5A88F42A38A4FD00072618 /*
QRCodeDetailView.swift */,
4E6EDD862A363D8D0031D520 /* ListStyle.swift */,
@@ -775,7 +775,7 @@
4E7940DD29FC307C00A9AEA1 /* SendPurpose.swift
*/,
4EB3136029FEE79B007D68BC /* SendDoneV.swift */,
4E9320442A1645B600A87B0E /*
RequestPayment.swift */,
- 4E9320462A164BC700A87B0E /*
PaymentPurpose.swift */,
+ 4E9320462A164BC700A87B0E /*
RequestPurpose.swift */,
);
path = Peer2peer;
sourceTree = "<group>";
@@ -1090,7 +1090,7 @@
4E3EAE472A990778009F1BE8 /*
QuiteSomeCoins.swift in Sources */,
4E3EAE482A990778009F1BE8 /*
PayTemplateView.swift in Sources */,
4E3EAE492A990778009F1BE8 /*
ManualWithdrawDone.swift in Sources */,
- 4E3EAE4A2A990778009F1BE8 /*
PaymentPurpose.swift in Sources */,
+ 4E3EAE4A2A990778009F1BE8 /*
RequestPurpose.swift in Sources */,
4E3EAE4B2A990778009F1BE8 /* ShareSheet.swift in
Sources */,
4EC4008F2AE8019700DF72C7 /*
ExchangeRowView.swift in Sources */,
4E3EAE4C2A990778009F1BE8 /* AmountView.swift in
Sources */,
@@ -1199,7 +1199,7 @@
4EBA82AD2A3F580500E5F39A /*
QuiteSomeCoins.swift in Sources */,
4EBA56412A7FF5200084948B /*
PayTemplateView.swift in Sources */,
4EB431672A1E55C700C5690E /*
ManualWithdrawDone.swift in Sources */,
- 4E9320472A164BC700A87B0E /*
PaymentPurpose.swift in Sources */,
+ 4E9320472A164BC700A87B0E /*
RequestPurpose.swift in Sources */,
4E753A082A0B6A5F002D9328 /* ShareSheet.swift in
Sources */,
4EC400902AE8019700DF72C7 /*
ExchangeRowView.swift in Sources */,
4EB0956C2989CBFE0043A8A1 /* AmountView.swift in
Sources */,
@@ -1496,7 +1496,7 @@
CODE_SIGN_ENTITLEMENTS =
"$(TARGET_NAME).entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 24;
+ CURRENT_PROJECT_VERSION = 25;
DEVELOPMENT_TEAM = GUDDQ9428Y;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -1538,7 +1538,7 @@
CODE_SIGN_ENTITLEMENTS =
"$(TARGET_NAME).entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 24;
+ CURRENT_PROJECT_VERSION = 25;
DEVELOPMENT_TEAM = GUDDQ9428Y;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
diff --git a/TalerWallet1/Backend/WalletCore.swift
b/TalerWallet1/Backend/WalletCore.swift
index 3a02aaf..350f194 100644
--- a/TalerWallet1/Backend/WalletCore.swift
+++ b/TalerWallet1/Backend/WalletCore.swift
@@ -190,7 +190,7 @@ extension WalletCore {
if let type = TransactionType(rawValue: components[1]) {
guard type != .refresh else { return }
if decoded.newTxState.major == .done {
- logger.log("Done: \(decoded.transactionId,
privacy: .private(mask: .hash))")
+ logger.info("Done: \(decoded.transactionId,
privacy: .private(mask: .hash))")
Controller.shared.playSound(type.isIncoming ? 2 :
1)
} else if decoded.newTxState.major == .expired {
logger.log("Expired: \(decoded.transactionId,
privacy: .private(mask: .hash))")
@@ -202,7 +202,7 @@ extension WalletCore {
}
} else {
// TODO: Same state usually means that an error is transmitted
- logger.log("No State change: \(decoded.transactionId, privacy:
.private(mask: .hash))")
+ logger.info("No State change: \(decoded.transactionId,
privacy: .private(mask: .hash))")
}
} catch { // rethrows
symLog.log(jsonData) // TODO: .error
@@ -339,7 +339,7 @@ extension WalletCore {
sendRequest(request: reqData) { requestId, timeSent, result, error
in
let timeUsed = Date.now - timeSent
let millisecs = timeUsed.milliseconds
- self.logger.log("Request \(requestId) took \(millisecs) ms")
+ self.logger.info("Request \(requestId) took \(millisecs) ms")
var err: Error? = nil
if let json = result {
do {
diff --git a/TalerWallet1/Controllers/Controller.swift
b/TalerWallet1/Controllers/Controller.swift
index fe55625..23964cc 100644
--- a/TalerWallet1/Controllers/Controller.swift
+++ b/TalerWallet1/Controllers/Controller.swift
@@ -33,7 +33,7 @@ class Controller: ObservableObject {
private let symLog = SymLogC()
@Published var backendState: BackendState = .none // only used for
launch animation
- @Published var currencyInfos: [CurrencyInfo]
+ @Published var currencyTicker: Int = 0
@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("talerFont") var talerFont: Int = 0 // extension
mustn't define this, so it must be here
@@ -41,6 +41,7 @@ class Controller: ObservableObject {
let logger = Logger(subsystem: "net.taler.gnu", category: "Controller")
let player = AVQueuePlayer()
let semaphore = AsyncSemaphore(value: 1)
+ var currencyInfos: [CurrencyInfo]
var messageForSheet: String? = nil
@@ -52,36 +53,52 @@ class Controller: ObservableObject {
// }
// }
backendState = .instantiated
+ currencyTicker = 0
currencyInfos = []
}
+// MARK: -
+ func hasInfo(for currency: String) -> Bool {
+ for info in currencyInfos {
+ if info.scope.currency == currency {
+ return true
+ }
+ }
+ return false
+ }
- func info(for currency: String) -> CurrencyInfo? {
- for currencyInfo in currencyInfos {
- if currencyInfo.scope.currency == currency {
- return currencyInfo
+ 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 nil
+ return CurrencyInfo.zero(currency)
}
@MainActor
- func setInfo(_ info: CurrencyInfo) async {
+ func setInfo(_ newInfo: CurrencyInfo) async {
await semaphore.wait()
defer { semaphore.signal() }
- var existing: ScopeInfo? = nil
- for currencyInfo in currencyInfos {
- let scope = currencyInfo.scope
- if scope.currency == info.scope.currency {
- existing = scope
- break
+
+ var replaced = false
+ var newInfos = currencyInfos.map { currencyInfo in
+ if currencyInfo.scope.currency == newInfo.scope.currency {
+ replaced = true
+ return newInfo
+ } else {
+ return currencyInfo
}
}
- if existing != nil {
- currencyInfos.removeAll(where: { $0.scope == existing })
+ if !replaced {
+ newInfos.append(newInfo)
}
- currencyInfos.append(info)
+ currencyInfos = newInfos
+ currencyTicker += 1 // triggers published view update
}
-
+// MARK: -
@MainActor
func initWalletCore(_ model: WalletModel, sqlite3: Bool)
async throws {
diff --git a/TalerWallet1/Controllers/DebugViewC.swift
b/TalerWallet1/Controllers/DebugViewC.swift
index 50cbdca..2c18cce 100644
--- a/TalerWallet1/Controllers/DebugViewC.swift
+++ b/TalerWallet1/Controllers/DebugViewC.swift
@@ -152,7 +152,7 @@ class DebugViewC: ObservableObject {
func announce(this: String) {
if UIAccessibility.isVoiceOverRunning {
- UIAccessibility.post(notification: .screenChanged, argument: this)
+ UIAccessibility.post(notification: .announcement, argument: this)
}
}
diff --git a/TalerWallet1/Controllers/TalerWallet1App.swift
b/TalerWallet1/Controllers/TalerWallet1App.swift
index 9efb5be..043d646 100644
--- a/TalerWallet1/Controllers/TalerWallet1App.swift
+++ b/TalerWallet1/Controllers/TalerWallet1App.swift
@@ -22,7 +22,6 @@ struct TalerWallet1App: App {
@State private var soundPlayed = false
private let walletCore = WalletCore.shared
- // our main controller
private let controller = Controller.shared
private let model = WalletModel.shared
private let debugViewC = DebugViewC.shared
diff --git a/TalerWallet1/Helper/CurrencySpecification.swift
b/TalerWallet1/Helper/CurrencySpecification.swift
index 63988e3..1cac315 100644
--- a/TalerWallet1/Helper/CurrencySpecification.swift
+++ b/TalerWallet1/Helper/CurrencySpecification.swift
@@ -8,11 +8,31 @@ import taler_swift
extension Amount {
func string(_ currencyInfo: CurrencyInfo?) -> String {
if let currencyInfo {
- return currencyInfo.string(for: valueAsTuple)
+ return currencyInfo.string(for: valueAsFloatTuple)
} else {
return valueStr
}
}
+
+ func inputDigits(_ currencyInfo: CurrencyInfo) -> UInt {
+ let inputDigits = currencyInfo.specs.fractionalInputDigits
+ if inputDigits < 0 { return 0 }
+ if inputDigits > 8 { return 8}
+ return UInt(inputDigits)
+ }
+
+ func addDigit(_ digit: UInt8, currencyInfo: CurrencyInfo) {
+ shiftLeft(add: digit, inputDigits(currencyInfo))
+ }
+
+ func removeDigit(_ currencyInfo: CurrencyInfo) {
+ shiftRight() // divide by 10
+ mask(inputDigits(currencyInfo)) // replace all digits after
#inputDigit with 0
+ }
+
+ func plainString(_ currencyInfo: CurrencyInfo) -> String {
+ return plainString(inputDigits: inputDigits(currencyInfo))
+ }
}
public struct CurrencyInfo {
@@ -20,6 +40,18 @@ public struct CurrencyInfo {
let specs: CurrencySpecification
let formatter: CurrencyFormatter
+ public static func zero(_ currency: String) -> CurrencyInfo {
+ let scope = ScopeInfo(type: .global, currency: currency)
+ let specs = CurrencySpecification(name: currency,
+ fractionalInputDigits: 0,
+ fractionalNormalDigits: 0,
+ fractionalTrailingZeroDigits: 0,
+ altUnitNames: [0 : "テ"])
+ return CurrencyInfo(scope: scope, specs: specs,
+ formatter: CurrencyFormatter.formatter(scope: scope,
+ specs: specs))
+ }
+
/// returns all characters left from the decimalSeparator
func integerPartStr(_ integerStr: String, decimalSeparator: String) ->
String {
if let integerIndex = integerStr.endIndex(of: decimalSeparator) {
@@ -119,18 +151,12 @@ public struct CurrencySpecification: Codable, Sendable {
}
/// some name for this CurrencySpecification
let name: String
- /// e.g. “.” for $, and “,” for €
-// let decimalSeparator: String taken from Locale.current
- /// e.g. “,” for $, and “.” or “ ” for € (France uses a narrow space
character, Hungaria a normal one)
-// let groupSeparator: String? taken from Locale.current
/// how much digits the user may enter after the decimal separator
let fractionalInputDigits: Int
/// €,$,£: 2; some arabic currencies: 3, ¥: 0
let fractionalNormalDigits: Int
/// usually same as numFractionalNormalDigits, but e.g. might be 2 for ¥
let fractionalTrailingZeroDigits: Int
- /// true for “$ 3.50”; false for “3,50 €”
-// let isCurrencyNameLeading: Bool
/// map of powers of 10 to alternative currency names / symbols
/// must always have an entry under "0" that defines the base name
/// e.g. "0 => €" or "3 => k€". For BTC, would be "0 => BTC, -3 => mBTC".
diff --git a/TalerWallet1/Model/Model+Exchange.swift
b/TalerWallet1/Model/Model+Exchange.swift
index 524f462..032ab24 100644
--- a/TalerWallet1/Model/Model+Exchange.swift
+++ b/TalerWallet1/Model/Model+Exchange.swift
@@ -32,18 +32,21 @@ struct Exchange: Codable, Hashable, Identifiable {
static func == (lhs: Exchange, rhs: Exchange) -> Bool {
return lhs.exchangeBaseUrl == rhs.exchangeBaseUrl
&& lhs.tosStatus == rhs.tosStatus
- && lhs.exchangeStatus == rhs.exchangeStatus
// deprecated
+// && lhs.exchangeStatus == rhs.exchangeStatus
// deprecated
&& lhs.exchangeEntryStatus == rhs.exchangeEntryStatus
&& lhs.exchangeUpdateStatus == rhs.exchangeUpdateStatus
}
var exchangeBaseUrl: String
- var currency: String?
+ // deprecated, use scopeInfo
+ var currency: String? // TODO: remove this
+ var scopeInfo: ScopeInfo?
var paytoUris: [String]
var tosStatus: ExchangeTosStatus
- var exchangeStatus: String?
// deprecated
- var exchangeEntryStatus: ExchangeEntryStatus? // new,
but not yet deployed in demo.taler.net
- var exchangeUpdateStatus: ExchangeUpdateStatus? // new,
but not yet deployed in demo.taler.net
+ // deprecated, use EntryStatus + UpdateStatus
+// var exchangeStatus: String?
+ var exchangeEntryStatus: ExchangeEntryStatus
+ var exchangeUpdateStatus: ExchangeUpdateStatus
var ageRestrictionOptions: [Int]
var lastUpdateErrorInfo: ExchangeError?
@@ -188,10 +191,10 @@ extension WalletModel {
_ = try await sendRequest(request)
}
- func getCurrencyInfo(scope: ScopeInfo)
+ func getCurrencyInfo(scope: ScopeInfo, delay: UInt = 0)
async throws -> CurrencyInfo {
let request = GetCurrencySpecification(scope: scope)
- let response = try await sendRequest(request, ASYNCDELAY)
+ let response = try await sendRequest(request, ASYNCDELAY + delay)
return CurrencyInfo(scope: scope, specs:
response.currencySpecification,
formatter: CurrencyFormatter.formatter(scope: scope,
specs:
response.currencySpecification))
diff --git a/TalerWallet1/Model/Transaction.swift
b/TalerWallet1/Model/Transaction.swift
index ea1e599..307e548 100644
--- a/TalerWallet1/Model/Transaction.swift
+++ b/TalerWallet1/Model/Transaction.swift
@@ -23,7 +23,7 @@ enum TransactionMinorState: String, Codable {
case aml // AmlRequired
case mergeKycRequired = "merge-kyc"
case track
- case pay
+ case submitPayment = "submit-payment"
case rebindSession = "rebind-session"
case refresh
case pickup
@@ -40,17 +40,16 @@ enum TransactionMinorState: String, Codable {
case repurchase
case bankRegisterReserve = "bank-register-reserve"
case bankConfirmTransfer = "bank-confirm-transfer"
- case exchangeWaitReserve = "exchange-wait-reserve"
case withdrawCoins = "withdraw-coins"
+ case exchangeWaitReserve = "exchange-wait-reserve"
case abortingBank = "aborting-bank"
+ case aborting
case refused
case withdraw
case merchantOrderProposed = "merchant-order-proposed"
case proposed
case refundAvailable = "refund-available"
case acceptRefund = "accept-refund"
-
- case submitPayment = "submit-payment"
}
enum TransactionMajorState: String, Codable {
@@ -67,23 +66,20 @@ enum TransactionMajorState: String, Codable {
case expired
// Only used for the notification, never in the transaction history
case deleted
- // Placeholder until D37 is fully implemented
- case unknown
var localizedState: String {
switch self {
case .none: return "none"
- case .pending: return String(localized: "MajorState.Pending",
defaultValue: "Pending", comment: "TransactionMajorState")
- case .done: return String(localized: "MajorState.Done",
defaultValue: "Done", comment: "TransactionMajorState")
- case .aborting: return String(localized: "MajorState.Aborting",
defaultValue: "Aborting", comment: "TransactionMajorState")
- case .aborted: return String(localized: "MajorState.Aborted",
defaultValue: "Aborted", comment: "TransactionMajorState")
+ case .pending: return String(localized: "MajorState.Pending",
defaultValue: "Pending", comment: "TxMajorState heading")
+ case .done: return String(localized: "MajorState.Done",
defaultValue: "Done", comment: "TxMajorState heading")
+ case .aborting: return String(localized: "MajorState.Aborting",
defaultValue: "Aborting", comment: "TxMajorState heading")
+ case .aborted: return String(localized: "MajorState.Aborted",
defaultValue: "Aborted", comment: "TxMajorState heading")
case .suspended: return "Suspended"
- case .dialog: return String(localized: "MajorState.Dialog",
defaultValue: "Dialog", comment: "TransactionMajorState")
+ case .dialog: return String(localized: "MajorState.Dialog",
defaultValue: "Dialog", comment: "TxMajorState heading")
case .suspendedAborting: return "AbortingSuspended"
- case .failed: return String(localized: "MajorState.Failed",
defaultValue: "Failed", comment: "TransactionMajorState")
- case .expired: return String(localized: "MajorState.Expired",
defaultValue: "Expired", comment: "TransactionMajorState")
- case .deleted: return String(localized: "MajorState.Deleted",
defaultValue: "Deleted", comment: "TransactionMajorState")
- case .unknown: return String(localized: "MajorState.Unknown",
defaultValue: "Unknown", comment: "TransactionMajorState")
+ case .failed: return String(localized: "MajorState.Failed",
defaultValue: "Failed", comment: "TxMajorState heading")
+ case .expired: return String(localized: "MajorState.Expired",
defaultValue: "Expired", comment: "TxMajorState heading")
+ case .deleted: return String(localized: "MajorState.Deleted",
defaultValue: "Deleted", comment: "TxMajorState heading")
}
}
}
@@ -104,12 +100,52 @@ struct TransactionTransition: Codable { //
Notification
}
enum TxAction: String, Codable {
- case abort // pending,dialog,suspended -> aborting
-// case revive // aborting -> pending ?? maybe post 1.0
- case fail // aborting -> failed
case delete // dialog,done,expired,aborted,failed -> ()
case suspend // pending -> suspended; aborting -> ab_suspended
case resume // suspended -> pending; ab_suspended -> aborting
+ case abort // pending,dialog,suspended -> aborting
+// case revive // aborting -> pending ?? maybe post 1.0
+ case fail // aborting -> failed
+ case retry //
+
+ var localizedActionTitle: String {
+ return switch self {
+ case .delete: String(localized: "TxAction.Delete", defaultValue:
"Delete from list", comment: "TxAction button")
+ case .suspend: String("Suspend")
+ case .resume: String("Resume")
+ case .abort: String(localized: "TxAction.Abort", defaultValue:
"Abort", comment: "TxAction button")
+// case .revive: String(localized: "TxAction.Revive",
defaultValue: "Revive", comment: "TxAction button")
+ case .fail: String(localized: "TxAction.Fail", defaultValue:
"Fail", comment: "TxAction button")
+ case .retry: String(localized: "TxAction.Retry", defaultValue:
"Retry", comment: "TxAction button")
+ }
+ }
+ var localizedActionImage: String? {
+ return switch self {
+ case .delete: "trash" //
+ case .suspend:
+ if #available(iOS 16.0, *) {
+ "clock.badge.xmark" //
+ } else {
+ "clock.badge.exclamationmark" //
+ }
+ case .resume: "clock.arrow.circlepath" //
+ case .abort: "x.circle" //
+// case .revive: "clock.arrow.circlepath" //
+ case .fail: "play.slash" //
+ case .retry: "arrow.circlepath" //
+ }
+ }
+ var localizedActionExecuted: String {
+ switch self {
+ case .delete: return String(localized: "TxActionDone.Delete",
defaultValue: "Deleted from list", comment: "TxAction button")
+ case .suspend: return String("Suspending...")
+ case .resume: return String("Resuming...")
+ case .abort: return String(localized: "TxActionDone.Abort",
defaultValue: "Abort pending...", comment: "TxAction button")
+// case .revive: return String(localized: "TxActionDone.Revive",
defaultValue: "Revive", comment: "TxAction button")
+ case .fail: return String(localized: "TxActionDone.Fail",
defaultValue: "Failing...", comment: "TxAction button")
+ case .retry: return String(localized: "TxActionDone.Retry",
defaultValue: "Retrying...", comment: "TxAction button")
+ }
+ }
}
enum TransactionType: String, Codable {
diff --git a/TalerWallet1/Views/Balances/BalanceRowView.swift
b/TalerWallet1/Views/Balances/BalanceRowView.swift
index 2fb15ae..1c78853 100644
--- a/TalerWallet1/Views/Balances/BalanceRowView.swift
+++ b/TalerWallet1/Views/Balances/BalanceRowView.swift
@@ -48,11 +48,11 @@ struct BalanceButton: View {
/// [Send Money] [Request Payment]
struct BalanceRowView: View {
let amount: Amount
- let currencyInfo: CurrencyInfo?
let sendAction: () -> Void
let recvAction: () -> Void
let rowAction: () -> Void
@Environment(\.sizeCategory) var sizeCategory
+ @EnvironmentObject private var controller: Controller
@AppStorage("iconOnly") var iconOnly: Bool = false
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
@@ -72,6 +72,7 @@ struct BalanceRowView: View {
var body: some View {
SingleAxisGeometryReader { width in
VStack (alignment: .trailing) {
+ let currencyInfo = controller.info(for: amount.currencyStr,
controller.currencyTicker)
let amountStr = amount.string(currencyInfo)
BalanceButton(amountStr: amountStr,
sizeCategory: sizeCategory,
@@ -99,28 +100,26 @@ struct BalanceRowView: View {
// MARK: -
#if DEBUG
-struct SomeBalanceRows: View {
+struct BalanceRowView_Previews: PreviewProvider {
+ @MainActor
+ struct StateContainer: View {
var body: some View {
- let testInfo = PreviewCurrencyInfo(TESTCURRENCY, digits: 0)
- let demoInfo = PreviewCurrencyInfo(TESTCURRENCY, digits: 2)
- let test = try! Amount(fromString: TESTCURRENCY + ":1.23")
- let demo = try! Amount(fromString: DEMOCURRENCY + ":123.12")
+ 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, currencyInfo: demoInfo,
sendAction: {}, recvAction: {}, rowAction: {})
+ BalanceRowView(amount: demo, sendAction: {}, recvAction: {},
rowAction: {})
}
- BalanceRowView(amount: test, currencyInfo: testInfo, sendAction:
{}, recvAction: {}, rowAction: {})
+ BalanceRowView(amount: test, sendAction: {}, recvAction: {},
rowAction: {})
}
-
}
-}
+ }
-struct BalanceRowView_Previews: PreviewProvider {
static var previews: some View {
- SomeBalanceRows()
+ StateContainer()
// .environment(\.sizeCategory, .extraExtraLarge) Canvas Device
Settings
}
}
diff --git a/TalerWallet1/Views/Balances/BalancesListView.swift
b/TalerWallet1/Views/Balances/BalancesListView.swift
index d8401e2..f7ee719 100644
--- a/TalerWallet1/Views/Balances/BalancesListView.swift
+++ b/TalerWallet1/Views/Balances/BalancesListView.swift
@@ -14,14 +14,15 @@ struct BalancesListView: View {
let navTitle: String
@Binding var balances: [Balance]
@Binding var shouldReloadBalances: Int
- @State private var lastReloadedBalances = 0
#if TABBAR // Taler Wallet
#else // GNU Taler
let hamburgerAction: () -> Void
#endif
@EnvironmentObject private var model: WalletModel
- @State private var centsToTransfer: UInt64 = 0
+
+ @State private var lastReloadedBalances = 0
+ @State private var amountToTransfer = Amount.zero(currency: "")
// Update currency when used
@State private var summary: String = ""
@State private var showQRScanner: Bool = false
@State private var showCameraAlert: Bool = false
@@ -102,7 +103,7 @@ struct BalancesListView: View {
let hamburger: HamburgerButton = HamburgerButton(action:
hamburgerAction)
#endif
Content(symLog: symLog, stack: stack.push(), balances: $balances,
- centsToTransfer: $centsToTransfer, summary: $summary,
+ amountToTransfer: $amountToTransfer, summary: $summary,
reloadBalances: reloadBalances)
.navigationTitle(navTitle)
.navigationBarItems(leading: hamburger,
@@ -131,7 +132,7 @@ extension BalancesListView {
let stack: CallStack
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
@Binding var balances: [Balance]
- @Binding var centsToTransfer: UInt64
+ @Binding var amountToTransfer: Amount
@Binding var summary: String
var reloadBalances: (_ stack: CallStack, _ invalidateCache: Bool)
async -> Int
@@ -147,9 +148,9 @@ extension BalancesListView {
} else {
List(balances, id: \.self) { balance in
BalancesSectionView(stack:
stack.push("\(balance.scopeInfo.currency)"),
- balance: balance,
+ balance: balance,
// this is the currency to be used
sectionCount: count,
- centsToTransfer: $centsToTransfer,
+ amountToTransfer: $amountToTransfer,
// does still have the wrong currency
summary: $summary)
}
.onAppear() {
diff --git a/TalerWallet1/Views/Balances/BalancesSectionView.swift
b/TalerWallet1/Views/Balances/BalancesSectionView.swift
index d0a3718..5d58670 100644
--- a/TalerWallet1/Views/Balances/BalancesSectionView.swift
+++ b/TalerWallet1/Views/Balances/BalancesSectionView.swift
@@ -17,18 +17,17 @@ import SymLog
struct BalancesSectionView {
private let symLog = SymLogV(0)
let stack: CallStack
- let balance: Balance
+ let balance: Balance // this is the currency to
be used
let sectionCount: Int
- @Binding var centsToTransfer: UInt64
+ @Binding var amountToTransfer: Amount // does still have the
wrong currency
@Binding var summary: String
-// @AppStorage("moreContrast") var moreContrast: Bool = false
- @AppStorage("iconOnly") var iconOnly: Bool = false
@EnvironmentObject private var model: WalletModel
@EnvironmentObject private var controller: Controller
+ @AppStorage("iconOnly") var iconOnly: Bool = false
+// @AppStorage("moreContrast") var moreContrast: Bool = false
@State private var isShowingDetailView = false
-
@State private var transactions: [Transaction] = []
@State private var completedTransactions: [Transaction] = []
@State private var pendingTransactions: [Transaction] = []
@@ -70,7 +69,7 @@ extension BalancesSectionView: View {
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
let currency = balance.scopeInfo.currency
- let currencyInfo = controller.info(for: currency)
+ let currencyInfo = controller.info(for: currency,
controller.currencyTicker)
Section {
if "KUDOS" == currency && !balance.available.isZero {
@@ -81,17 +80,15 @@ extension BalancesSectionView: View {
}
BalancesNavigationLinksView(stack: stack.push(),
balance: balance,
- currencyInfo: currencyInfo,
- centsToTransfer: $centsToTransfer,
+ amountToTransfer: $amountToTransfer, //
does still have the wrong currency
summary: $summary,
-// buttonSelected: $buttonSelected,
completedTransactions: $completedTransactions,
reloadAllAction: reloadCompleted,
reloadOneAction: reloadOneAction)
if pendingTransactions.count > 0 {
BalancesPendingRowView(symLog: symLog,
stack: stack.push(),
- currencyInfo: currencyInfo,
+ currency: currency,
pendingTransactions: $pendingTransactions,
reloadPending: reloadPending,
reloadOneAction: reloadOneAction)
@@ -102,7 +99,7 @@ extension BalancesSectionView: View {
LazyView {
TransactionsListView(stack: stack.push(),
navTitle: String(localized:
"Incomplete", comment: "ViewTitle of TransactionList"),
- currencyInfo: currencyInfo,
+ currency: currency,
transactions: incompleteTransactions,
showUpDown: false,
reloadAllAction: reloadIncomplete,
@@ -135,11 +132,11 @@ extension BalancesSectionView: View {
Section {
let slice = completedTransactions.prefix(3) // already
sorted
let threeTransactions = Array(slice)
- TransactionsRowsView(symLog: symLog,
- stack: stack.push(),
- currencyInfo: currencyInfo,
- transactions: threeTransactions,
- reloadOneAction: reloadOneAction)
+ TransactionsArraySliceV(symLog: symLog,
+ stack: stack.push(),
+ currency: currency,
+ transactions: threeTransactions,
+ reloadOneAction: reloadOneAction)
} header: {
if !iconOnly {
Text("Recent transactions")
@@ -147,22 +144,21 @@ extension BalancesSectionView: View {
// .foregroundColor(moreContrast ? .primary :
.secondary)
}
}
- }
+ } // recent transactions
} // body
-} // extension BalancesSectionView
+} // BalancesSectionView
fileprivate struct BalancesPendingRowView: View {
let symLog: SymLogV?
let stack: CallStack
- let currencyInfo: CurrencyInfo?
-//
+ let currency: String // = currencyInfo.scope.currency
@Binding var pendingTransactions: [Transaction]
let reloadPending: (_ stack: CallStack) async -> ()
let reloadOneAction: ((_ transactionId: String) async throws ->
Transaction)
func computePending(currency: String) -> (Amount, Amount) {
- var incoming = Amount(currency: currency, value: 0)
- var outgoing = Amount(currency: currency, value: 0)
+ var incoming = Amount(currency: currency, cent: 0)
+ var outgoing = Amount(currency: currency, cent: 0)
for transaction in pendingTransactions {
let effective = transaction.common.amountEffective
if currency == effective.currencyStr {
@@ -182,7 +178,6 @@ fileprivate struct BalancesPendingRowView: View {
}
var body: some View {
- let currency: String = currencyInfo?.scope.currency ?? ""
let (pendingIncoming, pendingOutgoing) = computePending(currency:
currency)
NavigationLink {
@@ -190,7 +185,7 @@ fileprivate struct BalancesPendingRowView: View {
LazyView {
TransactionsListView(stack: stack.push(),
navTitle: String(localized: "Pending",
comment: "ViewTitle of TransactionList"),
- currencyInfo: currencyInfo,
+ currency: currency,
transactions: pendingTransactions,
showUpDown: false,
reloadAllAction: reloadPending,
@@ -200,11 +195,11 @@ fileprivate struct BalancesPendingRowView: View {
VStack(spacing: 6) {
var rows = 0
if !pendingIncoming.isZero {
- PendingRowView(amount: pendingIncoming, currencyInfo:
currencyInfo, incoming: true)
+ PendingRowView(amount: pendingIncoming, incoming: true)
let _ = (rows+=1)
}
if !pendingOutgoing.isZero {
- PendingRowView(amount: pendingOutgoing, currencyInfo:
currencyInfo, incoming: false)
+ PendingRowView(amount: pendingOutgoing, incoming: false)
let _ = (rows+=1)
}
if rows == 0 {
@@ -220,9 +215,8 @@ fileprivate struct BalancesPendingRowView: View {
fileprivate struct BalancesNavigationLinksView: View {
let stack: CallStack
let balance: Balance
- let currencyInfo: CurrencyInfo?
// let sectionCount: Int
- @Binding var centsToTransfer: UInt64
+ @Binding var amountToTransfer: Amount // does still have the
wrong currency
@Binding var summary: String
@Binding var completedTransactions: [Transaction]
let reloadAllAction: (_ stack: CallStack) async -> ()
@@ -243,21 +237,26 @@ fileprivate struct BalancesNavigationLinksView: View {
// }
}
- var body: some View {
+ func setCurrency() -> String {
let currency = balance.scopeInfo.currency
+ amountToTransfer.setCurrency(currency)
+ return currency
+ }
+
+ var body: some View {
+ let currency = setCurrency() // update currency
in amountToTransfer
HStack(spacing: 0) {
NavigationLink(destination: LazyView {
SendAmount(stack: stack.push(),
amountAvailable: balance.available,
- centsToTransfer: $centsToTransfer,
+ amountToTransfer: $amountToTransfer, // with correct
currency
summary: $summary)
}, tag: 1, selection: $buttonSelected
) { EmptyView() }.frame(width: 0).opacity(0).hidden() //
SendAmount
NavigationLink(destination: LazyView {
RequestPayment(stack: stack.push(),
- scopeInfo: balance.scopeInfo,
- centsToTransfer: $centsToTransfer,
+ amountToTransfer: $amountToTransfer, // with correct
currency
summary: $summary)
}, tag: 2, selection: $buttonSelected
) { EmptyView() }.frame(width: 0).opacity(0).hidden() //
RequestPayment
@@ -265,7 +264,7 @@ fileprivate struct BalancesNavigationLinksView: View {
NavigationLink(destination: LazyView {
TransactionsListView(stack: stack.push(),
navTitle: String(localized: "Transactions",
comment: "ViewTitle of TransactionList"),
- currencyInfo: currencyInfo,
+ currency: currency,
transactions: completedTransactions,
showUpDown: true,
reloadAllAction: reloadAllAction,
@@ -273,37 +272,37 @@ fileprivate struct BalancesNavigationLinksView: View {
}, tag: 3, selection: $buttonSelected
) { EmptyView() }.frame(width: 0).opacity(0).hidden() //
TransactionsListView
- BalanceRowView(amount: balance.available, currencyInfo:
currencyInfo,
- sendAction: {
- selectAndUpdate(1) // will trigger SendAmount
NavigationLink
- }, recvAction: {
- selectAndUpdate(2) // will trigger RequestPayment
NavigationLink
- }, rowAction: {
- buttonSelected = 3 // will trigger TransactionList
NavigationLink
- })
+ BalanceRowView(amount: balance.available,
+ sendAction: {
+ selectAndUpdate(1) // trigger SendAmount
NavigationLink
+ }, recvAction: {
+ selectAndUpdate(2) // trigger RequestPayment
NavigationLink
+ }, rowAction: {
+ buttonSelected = 3 // trigger TransactionList
NavigationLink
+ })
}
}
}
// MARK: -
#if false // model crashes
+struct BalancesSectionView_Previews: PreviewProvider {
fileprivate struct BindingViewContainer: View {
- @State var centsToTransfer: UInt64 = 333
+ @State var amountToTransfer: UInt64 = 333
@State private var summary: String = "bla-bla"
var body: some View {
let scopeInfo = ScopeInfo(type: ScopeInfo.ScopeInfoType.exchange, url:
DEMOEXCHANGE, currency: LONGCURRENCY)
- let balance = Balance(available: try! Amount(fromString: LONGCURRENCY
+ ":0.1"),
+ let balance = Balance(available: Amount(currency: LONGCURRENCY,
cent:1),
scopeInfo: scopeInfo,
requiresUserInput: false,
hasPendingTransactions: true)
BalancesSectionView(balance: balance,
sectionCount: 2,
- centsToTransfer: $centsToTransfer,
+ amountToTransfer: $amountToTransfer,
summary: $summary)
}
}
-struct BalancesSectionView_Previews: PreviewProvider {
static var previews: some View {
List {
BindingViewContainer()
diff --git a/TalerWallet1/Views/Balances/PendingRowView.swift
b/TalerWallet1/Views/Balances/PendingRowView.swift
index c312006..9549c40 100644
--- a/TalerWallet1/Views/Balances/PendingRowView.swift
+++ b/TalerWallet1/Views/Balances/PendingRowView.swift
@@ -53,10 +53,10 @@ struct PendingRowContentV: View {
/// This view shows a pending transaction row in a currency section
struct PendingRowView: View {
let amount: Amount
- let currencyInfo: CurrencyInfo?
let incoming: Bool
@Environment(\.sizeCategory) var sizeCategory
+ @EnvironmentObject private var controller: Controller
@AppStorage("iconOnly") var iconOnly: Bool = false
let inTitle0 = String(localized: "TitleIncoming_Short", defaultValue:
"Incoming",
@@ -85,6 +85,7 @@ struct PendingRowView: View {
let pendingColor = WalletColors().pendingColor(incoming)
SingleAxisGeometryReader { width in
Group {
+ let currencyInfo = controller.info(for: amount.currencyStr,
controller.currencyTicker)
let amountStr = amount.string(currencyInfo)
let amountWidth = amountStr.width(largeAmountFont: false,
sizeCategory)
let inTitle = iconOnly ? (inTitle0, nil)
@@ -108,31 +109,41 @@ struct PendingRowView: View {
}
// MARK: -
#if DEBUG
-
func PreviewCurrencyInfo(_ currency: String, digits: Int) -> CurrencyInfo {
- let unitName = digits == 0 ? "¥" : "€"
+ let unitName = digits == 0 ? "テ" : "ク" // do not use real currency
symbols like "¥" : "€"
let scope = ScopeInfo(type: .global, currency: currency)
- let specs = CurrencySpecification(name: TESTCURRENCY,
-// decimalSeparator: ".", groupSeparator: "'",
+ let specs = CurrencySpecification(name: currency,
fractionalInputDigits: digits,
fractionalNormalDigits: digits,
fractionalTrailingZeroDigits: digits,
-// isCurrencyNameLeading: true,
altUnitNames: [0 : unitName])
let formatter = CurrencyFormatter.formatter(scope: scope, specs: specs)
return CurrencyInfo(scope: scope, specs: specs, formatter: formatter)
}
-struct PendingRowView_Previews: PreviewProvider {
- static var previews: some View {
- let testInfo = PreviewCurrencyInfo(TESTCURRENCY, digits: 0)
- let demoInfo = PreviewCurrencyInfo(TESTCURRENCY, digits: 2)
- let test = try! Amount(fromString: TESTCURRENCY + ":1.23")
- let demo = try! Amount(fromString: DEMOCURRENCY + ":1234.56")
+@MainActor
+fileprivate struct Preview_Content: View {
+ var body: some View {
+ let test = Amount(currency: TESTCURRENCY, cent: 123)
+ let demo = Amount(currency: DEMOCURRENCY, cent: 123456)
List {
- PendingRowView(amount: test, currencyInfo: testInfo, incoming:
true)
- PendingRowView(amount: demo, currencyInfo: demoInfo, incoming:
false)
+ PendingRowView(amount: test, incoming: true)
+ PendingRowView(amount: demo, incoming: false)
+ }
+ }
+}
+fileprivate struct Previews: PreviewProvider {
+ @MainActor
+ struct StateContainer: View {
+// @StateObject private var controller = Controller.shared
+ var body: some View {
+ Text("Hello")
+// Preview_Content()
+// .environmentObject(controller)
}
}
+ static var previews: some View {
+ StateContainer()
+ }
}
#endif
diff --git a/TalerWallet1/Views/Exchange/ExchangeListView.swift
b/TalerWallet1/Views/Exchange/ExchangeListView.swift
index cc1d987..75b7156 100644
--- a/TalerWallet1/Views/Exchange/ExchangeListView.swift
+++ b/TalerWallet1/Views/Exchange/ExchangeListView.swift
@@ -10,13 +10,14 @@ import SymLog
struct ExchangeListView: View {
private let symLog = SymLogV(0)
let stack: CallStack
-// @Binding var balances: [Balance]
+ @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
@State var newExchange: String = TESTEXCHANGE
@@ -46,7 +47,7 @@ struct ExchangeListView: View {
let addTitleStr = String(localized: "Add Exchange", comment: "title of
the addExchange alert")
let addButtonStr = String(localized: "Add", comment: "button in the
addExchange alert")
if #available(iOS 16.0, *) {
- ExchangeListCommonV(symLog: symLog, stack: stack.push())
+ ExchangeListCommonV(symLog: symLog, stack: stack.push(), balances:
$balances)
.navigationTitle(navTitle)
.navigationBarItems(leading: hamburger, trailing: plusButton)
.alert(addTitleStr, isPresented: $showAlert) {
@@ -60,7 +61,7 @@ struct ExchangeListView: View {
Text("Please enter the exchange URL")
}
} else { // iOS 15 cannot have a textfield in an alert, so we must
- ExchangeListCommonV(symLog: symLog, stack: stack.push())
+ ExchangeListCommonV(symLog: symLog, stack: stack.push(), balances:
$balances)
.navigationTitle(navTitle)
.navigationBarItems(leading: hamburger, trailing: plusButton)
.textFieldAlert(isPresented: $showAlert,
@@ -76,14 +77,14 @@ struct ExchangeListView: View {
struct ExchangeListCommonV: View {
let symLog: SymLogV?
let stack: CallStack
-// @Binding var balances: [Balance]
+ @Binding var balances: [Balance]
@EnvironmentObject private var model: WalletModel
@State private var exchanges: [Exchange] = []
// source of truth for the value the user enters in currencyField for
exchange withdrawals
- @State private var centsToTransfer: UInt64 = 0 // TODO: different
values for different currencies?
+ @State private var amountToTransfer = Amount.zero(currency: "") //
TODO: Hold different values for different currencies?
func reloadExchanges() async -> Void {
exchanges = await model.listExchangesM()
@@ -97,9 +98,9 @@ struct ExchangeListCommonV: View {
//Text("Exchanges...")
Content(symLog: symLog,
stack: stack.push(),
-// balances: $balances,
+ balances: $balances,
exchanges: $exchanges,
- centsToTransfer: $centsToTransfer,
+ amountToTransfer: $amountToTransfer,
reloadExchanges: reloadExchanges)
.overlay {
if exchanges.isEmpty {
@@ -119,9 +120,9 @@ extension ExchangeListCommonV {
let symLog: SymLogV?
let stack: CallStack
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
-// @Binding var balances: [Balance]
+ @Binding var balances: [Balance]
@Binding var exchanges: [Exchange]
- @Binding var centsToTransfer: UInt64
+ @Binding var amountToTransfer: Amount // does still have the
wrong currency
var reloadExchanges: () async -> Void
func currenciesDict(_ exchanges: [Exchange]) -> [String : [Exchange]] {
@@ -142,12 +143,13 @@ extension ExchangeListCommonV {
var body: some View {
let dict = currenciesDict(exchanges)
+ // TODO: Balances for amountAvailable for Deposit
let sortedDict = dict.sorted{ $0.key < $1.key}
Group { // necessary for .backslide transition (bug in SwiftUI)
List(sortedDict, id: \.key) { key, value in
ExchangeSectionView(stack: stack.push(),
currency: key, exchanges: value,
- centsToTransfer: $centsToTransfer)
+ amountToTransfer: $amountToTransfer)
// does still have the wrong currency
}
.refreshable {
symLog?.log("refreshing")
diff --git a/TalerWallet1/Views/Exchange/ExchangeRowView.swift
b/TalerWallet1/Views/Exchange/ExchangeRowView.swift
index 0ed21cb..afea32c 100644
--- a/TalerWallet1/Views/Exchange/ExchangeRowView.swift
+++ b/TalerWallet1/Views/Exchange/ExchangeRowView.swift
@@ -8,12 +8,11 @@ import taler_swift
struct ExchangeRowView: View {
let stack: CallStack
let exchange: Exchange
-// let amount: Amount
let currency: String
- @Binding var centsToTransfer: UInt64
- @AppStorage("iconOnly") var iconOnly: Bool = false
+ @Binding var amountToTransfer: Amount
@Environment(\.sizeCategory) var sizeCategory
+ @AppStorage("iconOnly") var iconOnly: Bool = false
@State private var buttonSelected: Int? = nil
func selectAndUpdate(_ button: Int) {
@@ -69,7 +68,7 @@ struct ExchangeRowView: View {
NavigationLink(destination: LazyView {
ManualWithdraw(stack: stack.push(),
exchange: exchange,
- centsToTransfer: $centsToTransfer)
+ amountToTransfer: $amountToTransfer)
}, tag: 2, selection: $buttonSelected
) { EmptyView() }.frame(width: 0).opacity(0)
}.listRowSeparator(.hidden)
@@ -105,9 +104,9 @@ struct ExchangeRowView: View {
// MARK: -
#if DEBUG
fileprivate struct ExchangeRow_Container : View {
- @State private var centsToTransfer: UInt64 = 100
+ @State private var amountToTransfer = Amount(currency: LONGCURRENCY, cent:
1234)
-// let amount = try! Amount(fromString: LONGCURRENCY + ":1234.56")
+// let amount = Amount(currency: LONGCURRENCY, cent: 123456)
var body: some View {
let exchange1 = Exchange(exchangeBaseUrl: ARS_AGE_EXCHANGE,
currency: LONGCURRENCY,
@@ -126,7 +125,7 @@ fileprivate struct ExchangeRow_Container : View {
ExchangeRowView(stack: CallStack("Preview"),
exchange: exchange1,
currency: LONGCURRENCY,
- centsToTransfer: $centsToTransfer)
+ amountToTransfer: $amountToTransfer)
}
}
diff --git a/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
b/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
index 6e2afab..4664d50 100644
--- a/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
+++ b/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
@@ -10,23 +10,27 @@ import taler_swift
/// [Deposit Coins] [Withdraw Coins]
struct ExchangeSectionView: View {
let stack: CallStack
-// let amount: Amount
- let currency: String // TODO: amount.currencyStr
+ let currency: String // this is the currency to be
used
let exchanges: [Exchange]
- @Binding var centsToTransfer: UInt64
+ @Binding var amountToTransfer: Amount // does still have the wrong
currency
+
+ func setCurrency() -> String {
+ amountToTransfer.setCurrency(currency)
+ return currency
+ }
var body: some View {
#if DEBUG
let _ = Self._printChanges()
// let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
+ let currency2 = setCurrency() // update currency in
amountToTransfer
Section {
ForEach(exchanges) { exchange in
ExchangeRowView(stack: stack.push(),
exchange: exchange,
-// amount: amount,
- currency: currency, // TODO:
(balance.available) amount.isZero to disable Deposit-button
- centsToTransfer: $centsToTransfer)
+ currency: currency2, // TODO:
(balance.available) amount.isZero to disable Deposit-button
+ amountToTransfer: $amountToTransfer)
}
} header: {
BarGraphHeader(stack: stack.push(), currency: currency)
@@ -35,10 +39,11 @@ struct ExchangeSectionView: View {
}
// MARK: -
#if DEBUG
+fileprivate struct ExchangeSection_Previews: PreviewProvider {
fileprivate struct ExchangeRow_Container : View {
- @State private var centsToTransfer: UInt64 = 100
+ @State private var amountToTransfer = Amount(currency: LONGCURRENCY, cent:
1234)
-// let amount = try! Amount(fromString: LONGCURRENCY + ":1234.56")
+// let amount = Amount(currency: LONGCURRENCY, cent: 123456)
var body: some View {
let exchange1 = Exchange(exchangeBaseUrl: ARS_AGE_EXCHANGE,
currency: LONGCURRENCY,
@@ -56,11 +61,10 @@ fileprivate struct ExchangeRow_Container : View {
ageRestrictionOptions: [])
ExchangeSectionView(stack: CallStack("Preview"), currency:
LONGCURRENCY,
exchanges: [exchange1, exchange2],
- centsToTransfer: $centsToTransfer)
+ amountToTransfer: $amountToTransfer)
}
}
-fileprivate struct ExchangeSection_Previews: PreviewProvider {
static var previews: some View {
List {
ExchangeRow_Container()
diff --git a/TalerWallet1/Views/Exchange/ManualWithdraw.swift
b/TalerWallet1/Views/Exchange/ManualWithdraw.swift
index dc98268..505b55d 100644
--- a/TalerWallet1/Views/Exchange/ManualWithdraw.swift
+++ b/TalerWallet1/Views/Exchange/ManualWithdraw.swift
@@ -12,12 +12,12 @@ struct ManualWithdraw: View {
let stack: CallStack
let exchange: Exchange
- @Binding var centsToTransfer: UInt64
+ @Binding var amountToTransfer: Amount
@EnvironmentObject private var model: WalletModel
+ @AppStorage("iconOnly") var iconOnly: Bool = false
@State var withdrawalAmountDetails: WithdrawalAmountDetails? = nil
-
// @State var ageMenuList: [Int] = []
// @State var selectedAge = 0
@@ -28,13 +28,14 @@ struct ManualWithdraw: View {
#endif
let currency = exchange.currency ?? String(localized: "Unknown",
comment: "unknown currency")
let navTitle = String(localized: "NavTitle_Withdraw (currency)",
defaultValue: "Withdraw \(currency)")
- let currencyField = CurrencyField(value: $centsToTransfer, currency:
currency) // becomeFirstResponder
// let agePicker = AgePicker(ageMenuList: $ageMenuList, selectedAge:
$selectedAge)
ScrollView {
VStack {
- CurrencyInputView(currencyField: currencyField,
- title: String(localized: "Amount to
withdraw:"))
+ CurrencyInputView(amount: $amountToTransfer,
+ title: iconOnly ? String(localized: "How
much:")
+ : String(localized: "Amount
to withdraw:"),
+ shortcutLabel: String(localized: "Withdraw",
comment: "VoiceOver: Withdraw $50,$25,$10,$5 shortcut buttons"))
let someCoins = SomeCoins(details: withdrawalAmountDetails)
QuiteSomeCoins(someCoins: someCoins, shouldShowFee: true,
currency: currency, amountEffective:
withdrawalAmountDetails?.amountEffective)
@@ -42,7 +43,7 @@ struct ManualWithdraw: View {
.multilineTextAlignment(.center)
.accessibilityFont(.body)
- let disabled = (centsToTransfer == 0) || someCoins.invalid ||
someCoins.tooMany
+ let disabled = amountToTransfer.isZero || someCoins.invalid ||
someCoins.tooMany
if !disabled {
// agePicker
@@ -54,7 +55,7 @@ struct ManualWithdraw: View {
NavigationLink(destination: LazyView {
ManualWithdrawDone(stack: stack.push(),
exchange: exchange,
- centsToTransfer: centsToTransfer)
+ amountToTransfer: amountToTransfer)
// restrictAge: restrictAge)
}) {
Text("Confirm Withdrawal") //
VIEW_WITHDRAW_ACCEPT
@@ -80,11 +81,11 @@ struct ManualWithdraw: View {
symLog.log("onAppear")
DebugViewC.shared.setViewID(VIEW_WITHDRAWAL, stack: stack.push())
}
- .task(id: centsToTransfer) { // re-run this whenever centsToTransfer
changes
- if centsToTransfer > 0 {
- let amount = Amount.amountFromCents(currency, centsToTransfer)
+ .task(id: amountToTransfer.value) { // re-run this whenever
amountToTransfer changes
+ if !amountToTransfer.isZero {
do {
- withdrawalAmountDetails = try await
model.loadWithdrawalDetailsForAmountM(exchange.exchangeBaseUrl, amount: amount)
+ withdrawalAmountDetails = try await
model.loadWithdrawalDetailsForAmountM(exchange.exchangeBaseUrl,
+
amount: amountToTransfer)
// agePicker.setAges(ages:
withdrawalAmountDetails?.ageRestrictionOptions)
} catch { // TODO: error
symLog.log(error.localizedDescription)
@@ -96,11 +97,12 @@ struct ManualWithdraw: View {
}
// MARK: -
#if DEBUG
-struct ManualWithdraw_Container : View {
- @State private var centsToTransfer: UInt64 = 510
+struct ManualWithdraw_Previews: PreviewProvider {
+ struct StateContainer : View {
+ @State private var amountToTransfer = Amount(currency: LONGCURRENCY, cent:
510)
@State private var details = WithdrawalAmountDetails(tosAccepted: false,
- amountRaw: try!
Amount(fromString: LONGCURRENCY + ":5.1"),
- amountEffective: try!
Amount(fromString: LONGCURRENCY + ":5.0"),
+ amountRaw:
Amount(currency: LONGCURRENCY, cent: 510),
+ amountEffective:
Amount(currency: LONGCURRENCY, cent: 500),
paytoUris: [],
ageRestrictionOptions: [],
numCoins: 6)
@@ -114,14 +116,13 @@ struct ManualWithdraw_Container : View {
ageRestrictionOptions: [])
ManualWithdraw(stack: CallStack("Preview"),
exchange: exchange,
- centsToTransfer: $centsToTransfer,
+ amountToTransfer: $amountToTransfer,
withdrawalAmountDetails: details)
}
-}
+ }
-struct ManualWithdraw_Previews: PreviewProvider {
static var previews: some View {
- ManualWithdraw_Container()
+ StateContainer()
}
}
#endif
diff --git a/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
b/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
index 1a182fa..039f6e4 100644
--- a/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
+++ b/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
@@ -12,7 +12,7 @@ struct ManualWithdrawDone: View {
let navTitle = String(localized: "Wire Transfer")
let exchange: Exchange
- let centsToTransfer: UInt64
+ let amountToTransfer: Amount
// let restrictAge: Int?
@EnvironmentObject private var model: WalletModel
@@ -53,9 +53,8 @@ struct ManualWithdrawDone: View {
DebugViewC.shared.setViewID(VIEW_WITHDRAW_ACCEPT, stack:
stack.push())
}.task {
do {
- let amount = Amount.amountFromCents(exchange.currency!,
centsToTransfer)
let result = try await
model.sendAcceptManualWithdrawalM(exchange.exchangeBaseUrl,
-
amount: amount, restrictAge: 0)
+
amount: amountToTransfer, restrictAge: 0)
transactionId = result!.transactionId
} catch { // TODO: error
symLog.log(error.localizedDescription)
@@ -67,7 +66,7 @@ struct ManualWithdrawDone: View {
// MARK: -
#if DEBUG
struct ManualWithdrawDone_Container : View {
- @State private var centsToTransfer: UInt64 = 510
+ @State private var amountToTransfer = Amount(currency: LONGCURRENCY, cent:
510)
var body: some View {
let exchange = Exchange(exchangeBaseUrl: DEMOEXCHANGE,
@@ -79,7 +78,7 @@ struct ManualWithdrawDone_Container : View {
ageRestrictionOptions: [])
ManualWithdrawDone(stack: CallStack("Preview"),
exchange: exchange,
- centsToTransfer: centsToTransfer)
+ amountToTransfer: amountToTransfer)
}
}
diff --git a/TalerWallet1/Views/HelperViews/AmountRowV.swift
b/TalerWallet1/Views/HelperViews/AmountRowV.swift
index ac741f9..eda77f1 100644
--- a/TalerWallet1/Views/HelperViews/AmountRowV.swift
+++ b/TalerWallet1/Views/HelperViews/AmountRowV.swift
@@ -54,8 +54,8 @@ struct SectionWithAmountRow: View {
var body: some View {
let testInfo = PreviewCurrencyInfo(TESTCURRENCY, digits: 0)
let demoInfo = PreviewCurrencyInfo(DEMOCURRENCY, digits: 2)
- let test = try! Amount(fromString: TESTCURRENCY + ":1.23")
- let demo = try! Amount(fromString: DEMOCURRENCY + ":1234.12")
+ 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 {
diff --git a/TalerWallet1/Views/HelperViews/BarGraph.swift
b/TalerWallet1/Views/HelperViews/BarGraph.swift
index 9eb61dc..673f6ad 100644
--- a/TalerWallet1/Views/HelperViews/BarGraph.swift
+++ b/TalerWallet1/Views/HelperViews/BarGraph.swift
@@ -12,8 +12,9 @@ struct BarGraphHeader: View {
let stack: CallStack
let currency: String
-// @AppStorage("moreContrast") var moreContrast: Bool = false
@EnvironmentObject private var model: WalletModel
+// @AppStorage("moreContrast") var moreContrast: Bool = false
+
@State private var completedTransactions: [Transaction] = []
var body: some View {
diff --git a/TalerWallet1/Views/HelperViews/CurrencyField.swift
b/TalerWallet1/Views/HelperViews/CurrencyField.swift
index 2072b02..784c9dd 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyField.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyField.swift
@@ -21,12 +21,15 @@
*/
import SwiftUI
import UIKit
+import taler_swift
+import SymLog
@MainActor
-public struct CurrencyField: View {
- @Binding var value: UInt64
- var currency: String
- var formatter: NumberFormatter
+struct CurrencyField: View {
+ private let symLog = SymLogV(0)
+ @Binding var amount: Amount // the `value´
+ let currencyInfo: CurrencyInfo
+
private var currencyInputField: CurrencyInputField! = nil
public func becomeFirstResponder() -> Void {
@@ -37,38 +40,31 @@ public struct CurrencyField: View {
currencyInputField.resignFirstResponder()
}
- private var label: String {
- let mag = pow(10, formatter.maximumFractionDigits)
- return formatter.string(for: Decimal(value) / mag) ?? ""
+ func updateText(amount: Amount) {
+ currencyInputField.updateText(amount: amount)
}
- public init(value: Binding<UInt64>, currency: String, formatter:
NumberFormatter) {
- self._value = value
- self.currency = currency
- self.formatter = formatter
- self.currencyInputField = CurrencyInputField(value: $value, formatter:
formatter)
+ public init(amount: Binding<Amount>, currencyInfo: CurrencyInfo) {
+ self._amount = amount
+ self.currencyInfo = currencyInfo
+ self.currencyInputField = CurrencyInputField(amount: self.$amount,
+ currencyInfo: currencyInfo)
}
- public init(value: Binding<UInt64>, currency: String) {
- let formatter = NumberFormatter()
- formatter.locale = .current
- formatter.numberStyle = .currency
- formatter.currencySymbol = currency
- formatter.minimumFractionDigits = 2
- formatter.maximumFractionDigits = 2
-
- self.init(value: value, currency: currency, formatter: formatter)
- }
-
- public var body: some View {
+ var body: some View {
+#if DEBUG
+ let _ = Self._printChanges()
+ let _ = symLog.vlog(amount.description) // just to get the # to
compare it with .onAppear & onDisappear
+#endif
ZStack {
// Text view to display the formatted currency
// Set as priority so CurrencyInputField size doesn't affect parent
- Text(label)
+ Text(amount.string(currencyInfo))
.layoutPriority(1)
// Input text field to handle UI
currencyInputField
+ .accessibilityHidden(true)
// .textFieldStyle(.roundedBorder)
}
}
@@ -91,8 +87,9 @@ class NoCaretTextField: UITextField {
@MainActor
struct CurrencyInputField: UIViewRepresentable {
- @Binding var value: UInt64
- var formatter: NumberFormatter
+ @Binding var amount: Amount
+ let currencyInfo: CurrencyInfo
+
private let textField = NoCaretTextField(frame: .zero)
func makeCoordinator() -> Coordinator {
@@ -107,6 +104,14 @@ struct CurrencyInputField: UIViewRepresentable {
textField.resignFirstResponder()
}
+ func updateText(amount: Amount) {
+ let plain = amount.plainString(currencyInfo)
+ print("Setting textfield to: \(plain)")
+ textField.text = plain
+ let endPosition = textField.endOfDocument
+ textField.selectedTextRange = textField.textRange(from: endPosition,
to: endPosition)
+ }
+
func makeUIView(context: Context) -> NoCaretTextField {
// Assign delegate
textField.delegate = context.coordinator
@@ -127,7 +132,7 @@ struct CurrencyInputField: UIViewRepresentable {
)
// Set initial textfield text
- context.coordinator.updateText(value, textField: textField)
+ context.coordinator.updateText(amount, textField: textField)
return textField
}
@@ -145,30 +150,35 @@ struct CurrencyInputField: UIViewRepresentable {
self.input = currencyTextField
}
- func setValue(_ value: UInt64, textField: UITextField) {
+ func setValue(_ amount: Amount, textField: UITextField) {
+ // Update hidden textfield text
+ updateText(amount, textField: textField)
// Update input value
- input.value = value
-
- // Update textfield text
- updateText(value, textField: textField)
+// print(input.amount.description, " := ", amount.description)
+ input.amount = amount
}
- func updateText(_ value: UInt64, textField: UITextField) {
+ func updateText(_ amount: Amount, textField: UITextField) {
// Update field text and last valid input text
- textField.text = String(value)
- lastValidInput = String(value)
+ lastValidInput = amount.plainString(input.currencyInfo)
+// print("lastValidInput: `\(lastValidInput)´")
+ textField.text = lastValidInput
+ let endPosition = textField.endOfDocument
+ textField.selectedTextRange = textField.textRange(from:
endPosition, to: endPosition)
}
func textField(_ textField: UITextField, shouldChangeCharactersIn
range: NSRange, replacementString string: String) -> Bool {
// If replacement string is empty, we can assume the backspace key
was hit
if string.isEmpty {
// Resign first responder when delete is hit when value is 0
- if input.value == 0 {
+ if input.amount.isZero {
textField.resignFirstResponder()
+ } else {
+ // Remove trailing digit: divide value by 10
+ let amount = input.amount.copy()
+ amount.removeDigit(input.currencyInfo)
+ setValue(amount, textField: textField)
}
-
- // Remove trailing digit
- setValue(UInt64(input.value / 10), textField: textField)
}
return true
}
@@ -190,7 +200,7 @@ struct CurrencyInputField: UIViewRepresentable {
}
// Find new character and try to get an Int value from it
- guard let char = char, let digit = Int(String(char)) else {
+ guard let char, let digit = UInt8(String(char)), digit <= 9 else {
// New character could not be converted to Int
// Revert to last valid text
textField.text = lastValidInput
@@ -198,27 +208,18 @@ struct CurrencyInputField: UIViewRepresentable {
}
// Multiply by 10 to shift numbers one position to the left,
revert if an overflow occurs
- let (multValue, multOverflow) =
input.value.multipliedReportingOverflow(by: 10)
- if multOverflow {
- textField.text = lastValidInput
- return
- }
-
// Add the new trailing digit, revert if an overflow occurs
- let (addValue, addOverflow) =
multValue.addingReportingOverflow(UInt64(digit))
- if addOverflow {
- textField.text = lastValidInput
- return
- }
+ let amount = input.amount.copy()
+ amount.addDigit(digit, currencyInfo: input.currencyInfo)
// If new value has more digits than allowed by formatter, revert
- if input.formatter.maximumFractionDigits +
input.formatter.maximumIntegerDigits < String(addValue).count {
- textField.text = lastValidInput
- return
- }
+// if input.formatter.maximumFractionDigits +
input.formatter.maximumIntegerDigits < String(addValue).count {
+// textField.text = lastValidInput
+// return
+// }
// Update new value
- setValue(addValue, textField: textField)
+ setValue(amount, textField: textField)
}
}
}
diff --git a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
index f89a702..7d6c045 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
@@ -1,33 +1,55 @@
-//
-// CurrencyInputView.swift
-// TalerWalletT
-//
-// Created by Marc Stibane on 2023-06-04.
-// Copyright © 2023 Taler. All rights reserved.
-//
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
import SwiftUI
+import taler_swift
struct CurrencyInputView: View {
- let currencyField: CurrencyField
+ @Binding var amount: Amount // the `value´
let title: String
+ let shortcutLabel: String
+ @EnvironmentObject private var controller: Controller
+
@State var hasBeenShown = false
+
var body: some View {
- VStack (alignment: .leading) {
- Text(title)
-// .padding(.top)
- .accessibilityFont(.title3)
+ let shortcuts = [50,25,10,5]
+ let currency = amount.currencyStr
+ let currencyInfo = controller.info(for: currency,
controller.currencyTicker)
+ let currencyField = CurrencyField(amount: $amount, currencyInfo:
currencyInfo)
+ VStack (alignment: .center) {
+ HStack {
+ Text(title)
+// .padding(.top)
+ .accessibilityFont(.title3)
+ .accessibilityAddTraits(.isHeader)
+ .accessibilityRemoveTraits(.isStaticText)
+ Spacer()
+ }
currencyField
.frame(maxWidth: .infinity, alignment: .trailing)
.foregroundColor(WalletColors().fieldForeground) // text
color
.background(WalletColors().fieldBackground)
.accessibilityFont(.title2)
.textFieldStyle(.roundedBorder)
+ HStack {
+ ForEach(shortcuts, id: \.self) { shortcut in
+ let shortie = Amount(currency: currency, integer:
UInt64(shortcut), fraction: 0)
+ let title = shortie.string(currencyInfo)
+ Button(title) {
+ currencyField.updateText(amount: shortie)
+ amount = shortie
+ }.buttonStyle(.bordered)
+ .accessibilityLabel("\(shortcutLabel) \(title)")
+ }
+ }
}.onAppear { // make CurrencyField show the keyboard after 0.4
seconds
if hasBeenShown {
- print("❗️Yikes: CurrencyInputView hasBeenShown")
+// print("❗️Yikes: CurrencyInputView hasBeenShown")
} else {
- print("❗️Yikes: First CurrencyInputView❗️")
+// print("❗️Yikes: First CurrencyInputView❗️")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
hasBeenShown = true
currencyField.becomeFirstResponder()
@@ -40,21 +62,21 @@ struct CurrencyInputView: View {
}
// MARK: -
#if DEBUG
-fileprivate struct BindingViewContainer : View {
- @State var centsToTransfer: UInt64 = 0
-
- var body: some View {
- let currencyField = CurrencyField(value: $centsToTransfer, currency:
LONGCURRENCY)
- CurrencyInputView(currencyField: currencyField,
- title: "Amount to withdraw:")
+fileprivate struct Previews: PreviewProvider {
+ @MainActor
+ struct StateContainer: View {
+ @StateObject private var controller = Controller.shared
+ @State var amountToTransfer = Amount(currency: LONGCURRENCY, cent: 0)
+ var body: some View {
+// Preview_Content()
+ CurrencyInputView(amount: $amountToTransfer,
+ title: "Amount to withdraw:",
+ shortcutLabel: "Withdraw")
+ .environmentObject(controller)
+ }
}
-}
-
-struct CurrencyInputView_Previews: PreviewProvider {
static var previews: some View {
- List {
- BindingViewContainer()
- }
+ StateContainer()
}
}
#endif
diff --git a/TalerWallet1/Views/HelperViews/TransactionButton.swift
b/TalerWallet1/Views/HelperViews/TransactionButton.swift
index 0ac465e..40b0c5e 100644
--- a/TalerWallet1/Views/HelperViews/TransactionButton.swift
+++ b/TalerWallet1/Views/HelperViews/TransactionButton.swift
@@ -15,58 +15,29 @@ struct TransactionButton: View {
@State var executed: Bool = false
var body: some View {
let isDestructive = (command == .delete) || (command == .fail)
- let role: ButtonRole? = (command == .abort) ? .cancel
- : isDestructive ? .destructive
- : nil
+ let isCancel = (command == .abort)
+ let role: ButtonRole? = isDestructive ? .destructive
+ : isCancel ? .cancel
+ : nil
Button(role: role, action: {
- Task { // runs on MainActor
+ if !disabled {
disabled = true // don't try this more than once
- do {
- try await action(transactionId)
+ Task { // runs on MainActor
+ do {
+ try await action(transactionId)
// symLog.log("\(executed) \(transactionId)")
- executed = true
- } catch { // TODO: error
+ executed = true
+ } catch { // TODO: error
// symLog.log(error.localizedDescription)
+ }
}
}
}, label: {
- HStack {
- if executed {
- switch command {
- case .delete:
- Text("Deleted from list")
- case .abort:
- Text("Abort pending...")
- case .fail:
- Text("Failing...")
- case .suspend:
- Text("Suspending...")
- case .resume:
- Text("Resuming...")
- }
- } else {
- let spaces = " "
- switch command {
- case .delete:
- Text("Delete from list" + spaces)
- Image(systemName: "trash")
//
- case .abort:
- Text("Abort" + spaces)
- Image(systemName: "x.circle")
//
- case .fail:
- Text("Fail" + spaces)
- Image(systemName: "play.slash")
//
- case .suspend:
- Text("Suspend" + spaces)
- if #available(iOS 16.0, *) {
- Image(systemName: "clock.badge.xmark")
//
- } else {
- Image(systemName:
"clock.badge.exclamationmark")//
- }
- case .resume:
- Text("Resume" + spaces)
- Image(systemName: "clock.arrow.circlepath")
//
- }
+ HStack(spacing: 50) {
+ Text(executed ? command.localizedActionExecuted
+ : command.localizedActionTitle)
+ if let imageName = command.localizedActionImage {
+ Image(systemName: imageName)
}
}
.accessibilityFont(.title2)
@@ -76,16 +47,18 @@ struct TransactionButton: View {
.controlSize(.large)
.disabled(disabled)
}
-
}
-
-
+// MARK: -
#if DEBUG
struct TransactionButton_Previews: PreviewProvider {
+ static func action(_ transactionId: String) async throws {
+ print(transactionId)
+ }
+
static var previews: some View {
List {
- TransactionButton(transactionId: "String", command: .abort,
action: {transactionId in })
+ TransactionButton(transactionId: "Button pressed", command:
.abort, action: action)
}
}
}
diff --git a/TalerWallet1/Views/HelperViews/View+fitsSideBySide.swift
b/TalerWallet1/Views/HelperViews/View+fitsSideBySide.swift
index fa4ca83..baecf5a 100644
--- a/TalerWallet1/Views/HelperViews/View+fitsSideBySide.swift
+++ b/TalerWallet1/Views/HelperViews/View+fitsSideBySide.swift
@@ -9,7 +9,7 @@ extension View {
@MainActor
public func announce(this: String) {
if UIAccessibility.isVoiceOverRunning {
- UIAccessibility.post(notification: .screenChanged, argument: this)
+ UIAccessibility.post(notification: .announcement, argument: this)
}
}
}
diff --git a/TalerWallet1/Views/Main/MainView.swift
b/TalerWallet1/Views/Main/MainView.swift
index 128f214..a1639ed 100644
--- a/TalerWallet1/Views/Main/MainView.swift
+++ b/TalerWallet1/Views/Main/MainView.swift
@@ -18,15 +18,18 @@ struct MainView: View {
private let symLog = SymLogV(0)
let logger: Logger
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
+
@State private var sheetPresented = false
@State private var urlToOpen: URL? = nil
- @Binding var soundPlayed: Bool
- @AppStorage("talerFont") var talerFont: Int = 0 // extension
mustn't define this, so it must be here
func sheetDismissed() -> Void {
- symLog.log("sheet dismiss")
+ logger.info("sheet dismiss")
}
var body: some View {
#if DEBUG
@@ -35,11 +38,13 @@ struct MainView: View {
#endif
Group {
if controller.backendState == .ready {
- Content(symLog: symLog, logger: logger, stack:
stack.push("Content"), talerFont: $talerFont)
+ Content(logger: logger, stack: stack.push("Content"),
talerFont: $talerFont)
// any change to rootViewId triggers popToRootView behaviour
.id(viewState.rootViewId)
.onAppear() {
- controller.playSound(1008) // Startup chime
+ if playSounds != 0 && !soundPlayed {
+ controller.playSound(1008) // Startup chime
+ }
soundPlayed = true
}
} else if controller.backendState == .error {
@@ -75,7 +80,6 @@ enum Tab {
// MARK: - Content
extension MainView {
struct Content: View {
- let symLog: SymLogV?
let logger: Logger
let stack: CallStack
@State private var shouldReloadBalances = 0
@@ -164,7 +168,9 @@ extension MainView {
// } else {
let _ = Self._printChanges()
// }
- let _ = symLog?.vlog() // just to get the identity # to
compare with .onAppear & .onDisappear
+ let delay: UInt = 5
+#else
+ let delay: UInt = 0
#endif
Group {
#if TABBAR // Taler Wallet
@@ -186,7 +192,7 @@ extension MainView {
NavigationView {
ExchangeListView(stack: stack.push(exchangesTitle),
-// balances: $balances,
+ balances: $balances,
navTitle: exchangesTitle)
}.navigationViewStyle(.stack)
.tabItem {
@@ -248,8 +254,7 @@ extension MainView {
}
}
} else { // should never happen
- logger.error("Yikes! TransactionStateTransition without
transition")
-// symLog.log(notification.userInfo as Any)
+ logger.error("Yikes❗️ TransactionStateTransition without
transition")
}
}
.alert("You need to pass a KYC procedure",
@@ -265,15 +270,16 @@ extension MainView {
.onChange(of: balances) { newArray in
for balance in newArray {
let scope = balance.scopeInfo
- if controller.info(for: scope.currency) == nil {
+ logger.info("balance changed: \(scope.currency, privacy:
.public)")
+ if !controller.hasInfo(for: scope.currency) {
Task { // runs on MainActor
- symLog?.log("get info for: \(scope.currency)")
+ logger.info("Task to get info for:
\(scope.currency, privacy: .public)")
do {
- let info = try await
model.getCurrencyInfo(scope: scope)
- symLog?.log("added: \(scope.currency)")
+ let info = try await
model.getCurrencyInfo(scope: scope, delay: delay)
+ logger.info("got info: \(scope.currency,
privacy: .public)")
await controller.setInfo(info)
} catch { // TODO: error handling - couldn't
get CurrencyInfo
- symLog?.log("error: \(error)")
+ logger.error("Couldn't get info for:
\(scope.currency, privacy: .public)\n\(error)")
}
}
}
diff --git a/TalerWallet1/Views/Peer2peer/RequestPayment.swift
b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
index 10cb313..a41b72e 100644
--- a/TalerWallet1/Views/Peer2peer/RequestPayment.swift
+++ b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
@@ -11,11 +11,11 @@ struct RequestPayment: View {
private let symLog = SymLogV(0)
let stack: CallStack
- var scopeInfo: ScopeInfo
- @Binding var centsToTransfer: UInt64
+ @Binding var amountToTransfer: Amount
@Binding var summary: String
@EnvironmentObject private var model: WalletModel
+ @AppStorage("iconOnly") var iconOnly: Bool = false
@State private var peerPullCheck: CheckPeerPullCreditResponse? = nil
@State private var expireDays: UInt = 0
@@ -25,32 +25,32 @@ struct RequestPayment: View {
let _ = Self._printChanges()
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
- let currency = scopeInfo.currency
+ let currency = amountToTransfer.currencyStr
let navTitle = String(localized: "Request Money", comment: "Dialog
Title")
- let currencyField = CurrencyField(value: $centsToTransfer, currency:
currency)
ScrollView { VStack {
- CurrencyInputView(currencyField: currencyField,
- title: String(localized: "Amount to request:"))
+ CurrencyInputView(amount: $amountToTransfer,
+ title: iconOnly ? String(localized: "How much:")
+ : String(localized: "Amount to
request:"),
+ shortcutLabel: String(localized: "Request", comment:
"VoiceOver: Request $50,$25,$10,$5 shortcut buttons"))
let someCoins = SomeCoins(details: peerPullCheck)
QuiteSomeCoins(someCoins: someCoins, shouldShowFee: true,
- currency: currency, amountEffective:
peerPullCheck?.amountEffective)
+ currency: currency,
+ amountEffective: peerPullCheck?.amountEffective)
HStack {
- let disabled = (centsToTransfer == 0) || someCoins.invalid ||
someCoins.tooMany
+ let disabled = amountToTransfer.isZero || someCoins.invalid ||
someCoins.tooMany
NavigationLink(destination: LazyView {
- PaymentPurpose(stack: stack.push(),
- scopeInfo: scopeInfo,
- centsToTransfer: centsToTransfer,
+ RequestPurpose(stack: stack.push(),
+ amountToTransfer: amountToTransfer,
fee: someCoins.fee,
summary: $summary,
expireDays: $expireDays)
// { deactivateAction() }
}) {
- let amount = Amount.amountFromCents(currency,
centsToTransfer)
- Text("Request \(amount.readableDescription)")
+ Text("Request \(amountToTransfer.readableDescription)")
// TODO: formatter
}
.buttonStyle(TalerButtonStyle(type: .prominent))
.disabled(disabled)
@@ -68,16 +68,17 @@ struct RequestPayment: View {
.onDisappear {
symLog.log("❗️Yikes \(navTitle) onDisappear")
}
- .task(id: centsToTransfer) {
- let amount = Amount.amountFromCents(currency, centsToTransfer)
- do {
- let ppCheck = try await model.checkPeerPullCreditM(amount,
exchangeBaseUrl: nil)
- peerPullCheck = ppCheck
- // TODO: set from exchange
-// agePicker.setAges(ages: peerPushCheck?.ageRestrictionOptions)
- } catch { // TODO: error
- symLog.log(error.localizedDescription)
- peerPullCheck = nil
+ .task(id: amountToTransfer.value) {
+ if !amountToTransfer.isZero {
+ do {
+ let ppCheck = try await
model.checkPeerPullCreditM(amountToTransfer, exchangeBaseUrl: nil)
+ peerPullCheck = ppCheck
+ // TODO: set from exchange
+// agePicker.setAges(ages:
peerPushCheck?.ageRestrictionOptions)
+ } catch { // TODO: error
+ symLog.log(error.localizedDescription)
+ peerPullCheck = nil
+ }
}
}
}
diff --git a/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift
b/TalerWallet1/Views/Peer2peer/RequestPurpose.swift
similarity index 75%
rename from TalerWallet1/Views/Peer2peer/PaymentPurpose.swift
rename to TalerWallet1/Views/Peer2peer/RequestPurpose.swift
index 8b04c12..8562c52 100644
--- a/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift
+++ b/TalerWallet1/Views/Peer2peer/RequestPurpose.swift
@@ -6,32 +6,27 @@ import SwiftUI
import taler_swift
import SymLog
-struct PaymentPurpose: View {
+struct RequestPurpose: View {
private let symLog = SymLogV(0)
let stack: CallStack
- let scopeInfo: ScopeInfo
- let centsToTransfer: UInt64
+ let amountToTransfer: Amount
let fee: String
@Binding var summary: String
@Binding var expireDays: UInt
+
+ @EnvironmentObject private var controller: Controller
@AppStorage("iconOnly") var iconOnly: Bool = false
- let navTitle = String(localized: "NavTitle_Request_Subject", defaultValue:
"Request", comment: "NavTitle for entering the subject for Request-Payment")
+ let navTitle = String(localized: "NavTitle_Request_Subject",
+ defaultValue: "Request", comment: "NavTitle for
entering the subject for Request-Payment")
@State private var transactionStarted: Bool = false
@FocusState private var isFocused: Bool
- private var label: String {
-// let mag = pow(10, formatter.maximumFractionDigits)
-// return formatter.string(for: Decimal(centsToTransfer) / mag) ?? ""
- return String(centsToTransfer / 100) // TODO: based on currency
- }
-
var body: some View {
- let amount = Amount.amountFromCents(scopeInfo.currency,
centsToTransfer)
-
+ let currencyInfo = controller.info(for: amountToTransfer.currencyStr,
controller.currencyTicker)
VStack (spacing: 6) {
- Text(amount.readableDescription)
+ Text(amountToTransfer.string(currencyInfo))
Text("+ \(fee) payment fee")
.foregroundColor(.red)
VStack(alignment: .leading, spacing: 6) {
@@ -65,12 +60,12 @@ struct PaymentPurpose: View {
NavigationLink(destination: LazyView {
SendDoneV(stack: stack.push(),
amountToSend: nil,
- amountToReceive: amount,
+ amountToReceive: amountToTransfer,
summary: summary,
expireDays: expireDays,
transactionStarted: $transactionStarted)
}) {
- Text("Request \(label) \(scopeInfo.currency)")
+ Text("Request \(amountToTransfer.readableDescription)")
// TODO: formatter
// .accessibilityFont(buttonFont)
}
.buttonStyle(TalerButtonStyle(type: .prominent))
@@ -86,23 +81,23 @@ struct PaymentPurpose: View {
.background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
.onAppear {
DebugViewC.shared.setViewID(VIEW_REQUEST_PURPOSE, stack:
stack.push())
-// print("❗️ PaymentPurpose onAppear")
+// print("❗️ RequestPurpose onAppear")
}
.onDisappear {
-// print("❗️ PaymentPurpose onDisappear")
+// print("❗️ RequestPurpose onDisappear")
}
}
}
// MARK: -
#if DEBUG
-//struct PaymentPurpose_Previews: PreviewProvider {
+//struct RequestPurpose_Previews: PreviewProvider {
// static var previews: some View {
-// let scopeInfo = ScopeInfo(type: ScopeInfo.ScopeInfoType.exchange,
exchangeBaseUrl: DEMOEXCHANGE, currency: LONGCURRENCY)
+// let scopeInfo = ScopeInfo(type: .exchange, exchangeBaseUrl:
DEMOEXCHANGE, currency: LONGCURRENCY)
// @State var summary: String = "pUrPoSe"
// @State var expireDays: UInt = 0
-// PaymentPurpose(scopeInfo: scopeInfo,
-// centsToTransfer: 5,
+// RequestPurpose(scopeInfo: scopeInfo,
+// amountToReceive: 5,
// fee: "fee",
// summary: $summary,
// expireDays: $expireDays)
diff --git a/TalerWallet1/Views/Peer2peer/SendAmount.swift
b/TalerWallet1/Views/Peer2peer/SendAmount.swift
index 5fa6693..7437004 100644
--- a/TalerWallet1/Views/Peer2peer/SendAmount.swift
+++ b/TalerWallet1/Views/Peer2peer/SendAmount.swift
@@ -12,24 +12,30 @@ struct SendAmount: View {
let stack: CallStack
let amountAvailable: Amount // TODO: GetMaxPeerPushAmount
- @Binding var centsToTransfer: UInt64
+ @Binding var amountToTransfer: Amount
@Binding var summary: String
+ @EnvironmentObject private var controller: Controller
@EnvironmentObject private var model: WalletModel
+ @AppStorage("iconOnly") var iconOnly: Bool = false
@State var peerPushCheck: CheckPeerPushDebitResponse? = nil
@State private var expireDays: UInt = SEVENDAYS
+ @State private var insufficient: Bool = false
+ @State private var fee: String = ""
- private func fee(ppCheck: CheckPeerPushDebitResponse?) -> String {
+ private func fee(ppCheck: CheckPeerPushDebitResponse?) -> Amount? {
do {
if let ppCheck {
// Outgoing: fee = effective - raw
let fee = try ppCheck.amountEffective - ppCheck.amountRaw
- return fee.readableDescription
+ return fee
}
} catch {}
- return ""
+ return nil
}
+
+ var feeLabel: String { String(localized: " + \(fee) payment fee") }
var body: some View {
#if DEBUG
@@ -37,29 +43,33 @@ struct SendAmount: View {
let _ = symLog.vlog() // just to get the # to compare it with
.onAppear & onDisappear
#endif
let currency = amountAvailable.currencyStr
+ let currencyInfo = controller.info(for: currency,
controller.currencyTicker)
let navTitle = String(localized: "Send \(currency)", comment: "Send
currency, Dialog Title")
- let currencyField = CurrencyField(value: $centsToTransfer, currency:
currency)
-
- let fee = fee(ppCheck: peerPushCheck)
+ let available = amountAvailable.string(currencyInfo)
+ let current = amountToTransfer.string(currencyInfo)
+ let insufficientLabel = String(localized: "You don't have enough
\(currency).")
+ let insufficientLabel2 = String(localized: "but you only have
\(available) to send.")
ScrollView {
VStack(alignment: .trailing) {
- let available = amountAvailable.readableDescription
- Text("Available: \(available)")
+// let _ = print("available: \(available)")
+ Text("Available:\t\(available)")
.accessibilityFont(.title3)
.padding(.bottom, 2)
- CurrencyInputView(currencyField: currencyField,
- title: String(localized: "Amount to send:"))
- Text("+ \(fee) payment fee")
+ CurrencyInputView(amount: $amountToTransfer,
+ title: iconOnly ? String(localized: "How
much:")
+ : String(localized: "Amount
to send:"),
+ shortcutLabel: String(localized: "Send", comment:
"VoiceOver: Send $50,$25,$10,$5 shortcut buttons"))
+ let disabled = insufficient || amountToTransfer.isZero
+ Text(insufficient ? insufficientLabel
+ : feeLabel)
.accessibilityFont(.body)
.foregroundColor(.red)
.padding(4)
- let disabled = centsToTransfer == 0 // TODO: check
amountAvailable
-
NavigationLink(destination: LazyView {
SendPurpose(stack: stack.push(),
amountAvailable: amountAvailable,
- centsToTransfer: centsToTransfer,
+ amountToTransfer: amountToTransfer,
fee: fee,
summary: $summary,
expireDays: $expireDays)
@@ -81,14 +91,27 @@ struct SendAmount: View {
.onDisappear {
symLog.log("❗️Yikes SendAmount onDisappear")
}
- .task(id: centsToTransfer) {
- if centsToTransfer > 0 {
- let amount = Amount.amountFromCents(currency, centsToTransfer)
+ .task(id: amountToTransfer.value) {
+ do {
+ insufficient = try amountToTransfer > amountAvailable
+ print("current: \(current)")
+ } catch {
+ print("Yikes❗️ insufficient failed❗️")
+ insufficient = true
+ }
+
+ if insufficient {
+ announce(this: "\(current), \(insufficientLabel2)")
+ } else if !amountToTransfer.isZero {
do {
- let ppCheck = try await model.checkPeerPushDebitM(amount)
+ let ppCheck = try await
model.checkPeerPushDebitM(amountToTransfer)
peerPushCheck = ppCheck
// TODO: set from exchange
// agePicker.setAges(ages: peerPushCheck?.ageRestrictionOptions)
+ if let feeAmount = fee(ppCheck: peerPushCheck) {
+ fee = feeAmount.string(currencyInfo)
+ } else { fee = "" }
+ announce(this: "\(current), \(feeLabel)")
} catch { // TODO: error
symLog.log(error.localizedDescription)
peerPushCheck = nil
@@ -99,22 +122,30 @@ struct SendAmount: View {
}
// MARK: -
#if DEBUG
-struct SendAmount_Container : View {
- @State private var centsToTransfer: UInt64 = 510
+fileprivate struct Preview_Content: View {
+ @State private var amountToTransfer = Amount(currency: TESTCURRENCY, cent:
510)
@State private var summary: String = ""
var body: some View {
- let amount = Amount(currency: LONGCURRENCY, integer: 10, fraction: 0)
+ let amount = Amount(currency: TESTCURRENCY, integer: 10, fraction: 0)
SendAmount(stack: CallStack("Preview"),
amountAvailable: amount,
- centsToTransfer: $centsToTransfer,
+ amountToTransfer: $amountToTransfer,
summary: $summary)
}
}
-//struct SendAmount_Previews: PreviewProvider {
-// static var previews: some View {
-// SendAmount_Container()
-// }
-//}
+fileprivate struct Previews: PreviewProvider {
+ @MainActor
+ struct StateContainer: View {
+ @StateObject private var controller = Controller.shared
+ var body: some View {
+ Preview_Content()
+ .environmentObject(controller)
+ }
+ }
+ static var previews: some View {
+ StateContainer()
+ }
+}
#endif
diff --git a/TalerWallet1/Views/Peer2peer/SendDoneV.swift
b/TalerWallet1/Views/Peer2peer/SendDoneV.swift
index 84b31ee..d7fdd45 100644
--- a/TalerWallet1/Views/Peer2peer/SendDoneV.swift
+++ b/TalerWallet1/Views/Peer2peer/SendDoneV.swift
@@ -11,6 +11,7 @@ struct SendDoneV: View {
private let symLog = SymLogV()
let stack: CallStack
let navTitle = String(localized: "P2P Ready")
+ @EnvironmentObject private var model: WalletModel
#if DEBUG
@AppStorage("developerMode") var developerMode: Bool = true
#else
@@ -24,8 +25,6 @@ struct SendDoneV: View {
let expireDays: UInt
@Binding var transactionStarted: Bool
- @EnvironmentObject private var model: WalletModel
-
@State private var transactionId: String? = nil
func reloadOneAction(_ transactionId: String) async throws -> Transaction {
@@ -97,7 +96,7 @@ struct SendDoneV: View {
// static var previews: some View {
// Group {
// SendDoneV(stack: CallStack("Preview"),
-// amountToSend: try! Amount(fromString: LONGCURRENCY + ":4.8"),
+// amountToSend: Amount(currency: LONGCURRENCY, cent: 480),
// amountToReceive: nil,
// summary: "some subject/purpose",
// expireDays: 0)
diff --git a/TalerWallet1/Views/Peer2peer/SendPurpose.swift
b/TalerWallet1/Views/Peer2peer/SendPurpose.swift
index 06ba0a4..a06f25b 100644
--- a/TalerWallet1/Views/Peer2peer/SendPurpose.swift
+++ b/TalerWallet1/Views/Peer2peer/SendPurpose.swift
@@ -9,10 +9,9 @@ import SymLog
struct SendPurpose: View {
private let symLog = SymLogV(0)
let stack: CallStack
- @FocusState private var isFocused: Bool
let amountAvailable: Amount
- let centsToTransfer: UInt64
+ let amountToTransfer: Amount
let fee: String
@Binding var summary: String
@Binding var expireDays: UInt
@@ -20,18 +19,11 @@ struct SendPurpose: View {
let navTitle = String(localized: "NavTitle_Send_Subject", defaultValue:
"Subject", comment: "NavTitle for entering the subject for Send-Money")
@State private var transactionStarted: Bool = false
-
- private var value: String {
-// let mag = pow(10, formatter.maximumFractionDigits)
-// return formatter.string(for: Decimal(centsToTransfer) / mag) ?? ""
- return String(centsToTransfer / 100) // TODO: based on currency
- }
+ @FocusState private var isFocused: Bool
var body: some View {
- let amount = Amount.amountFromCents(amountAvailable.currencyStr,
centsToTransfer)
-
VStack (spacing: 6) {
- Text(amount.readableDescription)
+ Text(amountToTransfer.readableDescription) // TODO:
curreny formatter
Text("+ \(fee) payment fee")
.accessibilityFont(.body)
.foregroundColor(.red)
@@ -84,13 +76,13 @@ struct SendPurpose: View {
let disabled = (expireDays == 0) || (summary.count < 1) //
TODO: check amountAvailable
NavigationLink(destination: LazyView {
SendDoneV(stack: stack.push(),
- amountToSend: amount,
+ amountToSend: amountToTransfer,
amountToReceive: nil,
summary: summary,
expireDays: expireDays,
transactionStarted: $transactionStarted)
}) {
- Text("Send \(value) \(amountAvailable.currencyStr) now",
comment: "first is value, second currencyString") // TODO: currency formatter
+ Text("Send \(amountToTransfer.readableDescription) now",
comment: "amountToTransfer") // TODO: currency formatter
}
.buttonStyle(TalerButtonStyle(type: .prominent))
.disabled(disabled)
@@ -130,7 +122,7 @@ struct SendPurpose: View {
// @State var expireDays: UInt = 0
// let amount = Amount(currency: LONGCURRENCY, integer: 10, fraction: 0)
// SendPurpose(amountAvailable: amount,
-// centsToTransfer: 543,
+// amountToTransfer: 543,
// fee: "0,43",
// summary: $summary,
// expireDays: $expireDays)
diff --git a/TalerWallet1/Views/Settings/AboutView.swift
b/TalerWallet1/Views/Settings/AboutView.swift
index 2e460bf..adf768d 100644
--- a/TalerWallet1/Views/Settings/AboutView.swift
+++ b/TalerWallet1/Views/Settings/AboutView.swift
@@ -18,9 +18,9 @@ struct AboutView: View {
@AppStorage("developerMode") var developerMode: Bool = false
#endif
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
+ @AppStorage("iconOnly") var iconOnly: Bool = false
@State private var rotationEnabled = false
-
@State private var listID = UUID()
var body: some View {
@@ -31,38 +31,45 @@ struct AboutView: View {
let walletCore = WalletCore.shared
Group {
List {
-// VStack {
- HStack {
- Spacer()
- RotatingTaler(size: 100, rotationEnabled:
$rotationEnabled)
- .accessibilityHint("Will go to the taler.net
website when long-pressed.")
- .onTapGesture(count: 2) {
- rotationEnabled.toggle()
- }
- .onLongPressGesture(minimumDuration: 0.3) {
-
UIApplication.shared.open(URL(string:"https://taler.net")!, options: [:])
- }
- Spacer()
- }
- SettingsItem(name: "App Version", id1: "app") {
- Text(verbatim:
"\(Bundle.main.releaseVersionNumberPretty)")
- }
- SettingsItem(name: "Wallet Core Version", id1:
"wallet-core") {
- Text(verbatim: "\(walletCore.versionInfo!.version)")
+ HStack {
+ Spacer()
+ RotatingTaler(size: 100, rotationEnabled: $rotationEnabled)
+ .onTapGesture(count: 2) {
+ rotationEnabled.toggle()
+ }
+ Spacer()
+ }
+ SettingsItem(name: "Visit the taler.net website", id1: "web",
+ description: iconOnly ? nil : String(localized:
"More info about Gnu Taler in general...")) { }
+ .accessibilityAddTraits(.isLink)
+ .accessibilityRemoveTraits(.isStaticText)
+ .onTapGesture() {
+
UIApplication.shared.open(URL(string:"https://taler.net")!, options: [:])
}
+
+ SettingsItem(name: "App Version", id1: "app") {
+ Text(verbatim: "\(Bundle.main.releaseVersionNumberPretty)")
+ }
+ SettingsItem(name: "Wallet Core Version", id1: "wallet-core") {
+ Text(verbatim: "\(walletCore.versionInfo?.version ??
"unknown")")
+ }
+ if developerMode {
SettingsItem(name: "Wallet Core DevMode", id1: "devMode") {
- Text(verbatim: "\(walletCore.versionInfo!.devMode ?
"YES" : "NO")")
+ let modeStr = if let devMode =
walletCore.versionInfo?.devMode {
+ devMode ? "YES" : "NO"
+ } else { "unknown" }
+ Text(modeStr)
}
- SettingsItem(name: "Supported Exchange Versions", id1:
"exchange") {
- Text(verbatim: "\(walletCore.versionInfo!.exchange)")
- }
- SettingsItem(name: "Supported Merchant Versions", id1:
"merchant") {
- Text(verbatim: "\(walletCore.versionInfo!.merchant)")
- }
- SettingsItem(name: "Used Bank", id1: "bank") {
- Text(verbatim: "\(walletCore.versionInfo!.bank)")
- }
-// } // App version info
+ }
+ 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
@@ -94,11 +101,7 @@ extension Bundle {
#if DEBUG
struct AboutView_Previews: PreviewProvider {
static var previews: some View {
-#if TABBAR // Taler Wallet
AboutView(stack: CallStack("Preview"), navTitle: "About")
-#else // GNU Taler
- SettingsView(stack: CallStack("Preview"), navTitle: "About") { }
-#endif
}
}
#endif
diff --git a/TalerWallet1/Views/Settings/SettingsView.swift
b/TalerWallet1/Views/Settings/SettingsView.swift
index 40abcc2..ae34f61 100644
--- a/TalerWallet1/Views/Settings/SettingsView.swift
+++ b/TalerWallet1/Views/Settings/SettingsView.swift
@@ -13,8 +13,6 @@ import SymLog
* Debug log
* View/send internal log
*
- * Reset Wallet (dangerous!)
- * Throws away your money
*/
struct SettingsView: View {
@@ -23,6 +21,7 @@ struct SettingsView: View {
let navTitle: String
@EnvironmentObject private var controller: Controller
+ @EnvironmentObject private var model: WalletModel
#if DEBUG
@AppStorage("developerMode") var developerMode: Bool = true
#else
@@ -41,8 +40,6 @@ struct SettingsView: View {
var hamburgerAction: () -> Void
#endif
- @EnvironmentObject private var model: WalletModel
-
@State private var checkDisabled = false
@State private var withDrawDisabled = false
#if DEBUG
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
index b26a6d0..0088fdc 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
@@ -14,8 +14,8 @@ struct P2pAcceptDone: View {
let transactionId: String
let incoming: Bool
- @EnvironmentObject private var model: WalletModel
@EnvironmentObject private var controller: Controller
+ @EnvironmentObject private var model: WalletModel
@State private var finished: Bool = false
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
index 9e9ccc6..7d4b384 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
@@ -51,7 +51,7 @@ struct P2pPayURIView: View {
.buttonStyle(TalerButtonStyle(type: .prominent))
.padding(.horizontal)
} else {
- WithdrawProgressView(message: url.host ?? "Yikes - no valid
URL")
+ WithdrawProgressView(message: url.host ?? "Yikes❗️ no valid
URL")
.navigationTitle("Contacting Exchange")
}
}
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
index d856947..4a39eca 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
@@ -12,16 +12,17 @@ struct P2pReceiveURIView: View {
private let symLog = SymLogV(0)
let stack: CallStack
let navTitle = String(localized: "P2P Receive")
- @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
// the scanned URL
let url: URL
@EnvironmentObject private var model: WalletModel
+ @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
@State private var peerPushCreditResponse: PreparePeerPushCreditResponse?
var body: some View {
+ let badURL = "Error in URL: \(url)"
VStack {
if let peerPushCreditResponse {
List {
@@ -59,8 +60,8 @@ struct P2pReceiveURIView: View {
}
} else {
// Yikes no details or no baseURL
-// WithdrawProgressView(message: url.host ?? badURL)
-// .navigationTitle("Contacting Exchange")
+ WithdrawProgressView(message: url.host ?? badURL)
+ .navigationTitle("Contacting Exchange")
}
}
.onAppear() {
diff --git a/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
b/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
index 7dda920..5a66006 100644
--- a/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
+++ b/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
@@ -13,13 +13,12 @@ struct PayTemplateView: View {
let stack: CallStack
let navTitle = String(localized: "Confirm Payment", comment:"pay merchant")
- @EnvironmentObject private var controller: Controller
- @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
-
// the scanned URL
let url: URL
+ @EnvironmentObject private var controller: Controller
@EnvironmentObject private var model: WalletModel
+ @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
func acceptAction(preparePayResult: PreparePayResult) {
Task { // runs on MainActor
diff --git a/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
b/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
index 27557b7..534a7d4 100644
--- a/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
+++ b/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
@@ -13,13 +13,12 @@ struct PaymentView: View {
let stack: CallStack
let navTitle = String(localized: "Confirm Payment", comment:"pay merchant")
- @EnvironmentObject private var controller: Controller
- @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
-
// the scanned URL
let url: URL
+ @EnvironmentObject private var controller: Controller
@EnvironmentObject private var model: WalletModel
+ @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
func acceptAction(preparePayResult: PreparePayResult) {
Task { // runs on MainActor
@@ -119,44 +118,43 @@ struct PaymentURIView_Previews: PreviewProvider {
let extra = Extra(articleName: "articleName")
let product = Product(description: "description")
let terms = MerchantContractTerms(hWire: "hWire",
- wireMethod: "wireMethod",
- summary: "summary",
- summaryI18n: nil,
+ wireMethod: "wireMethod",
+ summary: "summary",
+ summaryI18n: nil,
nonce: "nonce",
- amount: try! Amount(fromString:
LONGCURRENCY + ":2.2"),
- payDeadline: Timestamp.tomorrow(),
- maxFee: try! Amount(fromString:
LONGCURRENCY + ":0.2"),
- merchant: merchant,
- merchantPub: "merchantPub",
- deliveryDate: nil,
- deliveryLocation: nil,
- exchanges: [],
- products: [product],
- refundDeadline: Timestamp.tomorrow(),
- wireTransferDeadline:
Timestamp.tomorrow(),
- timestamp: Timestamp.now(),
- orderID: "orderID",
- merchantBaseURL: "merchantBaseURL",
- fulfillmentURL: "fulfillmentURL",
- publicReorderURL:
"publicReorderURL",
- fulfillmentMessage: nil,
- fulfillmentMessageI18n: nil,
- wireFeeAmortization: 0,
- maxWireFee: try! Amount(fromString:
LONGCURRENCY + ":0.2"),
- minimumAge: nil
-// extra: extra,
-// auditors: [],
+ amount: Amount(currency:
LONGCURRENCY, cent: 220),
+ payDeadline: Timestamp.tomorrow(),
+ maxFee: Amount(currency:
LONGCURRENCY, cent: 20),
+ merchant: merchant,
+ merchantPub: "merchantPub",
+ deliveryDate: nil,
+ deliveryLocation: nil,
+ exchanges: [],
+ products: [product],
+ refundDeadline: Timestamp.tomorrow(),
+ wireTransferDeadline: Timestamp.tomorrow(),
+ timestamp: Timestamp.now(),
+ orderID: "orderID",
+ merchantBaseURL: "merchantBaseURL",
+ fulfillmentURL: "fulfillmentURL",
+ publicReorderURL: "publicReorderURL",
+ fulfillmentMessage: nil,
+ fulfillmentMessageI18n: nil,
+ wireFeeAmortization: 0,
+ maxWireFee: Amount(currency:
LONGCURRENCY, cent: 20),
+ minimumAge: nil
+// extra: extra,
+// auditors: []
)
- let details = PreparePayResult(
- status: PreparePayResultType.paymentPossible,
- transactionId: "txn:payment:012345",
- contractTerms: terms,
- contractTermsHash: "termsHash",
- amountRaw: try! Amount(fromString: LONGCURRENCY + ":2.2"),
- amountEffective: try! Amount(fromString: LONGCURRENCY + ":2.4"),
- balanceDetails: nil,
- paid: nil
-// , talerUri: "talerURI"
+ let details = PreparePayResult(status:
PreparePayResultType.paymentPossible,
+ transactionId: "txn:payment:012345",
+ contractTerms: terms,
+ contractTermsHash: "termsHash",
+ amountRaw: Amount(currency: LONGCURRENCY,
cent: 220),
+ amountEffective: Amount(currency: LONGCURRENCY,
cent: 240),
+ balanceDetails: nil,
+ paid: nil
+// , talerUri: "talerURI"
)
let url = URL(string: "taler://pay/some_amount")!
diff --git a/TalerWallet1/Views/Sheets/URLSheet.swift
b/TalerWallet1/Views/Sheets/URLSheet.swift
index 4bb7a10..4c9f99d 100644
--- a/TalerWallet1/Views/Sheets/URLSheet.swift
+++ b/TalerWallet1/Views/Sheets/URLSheet.swift
@@ -10,6 +10,7 @@ struct URLSheet: View {
let stack: CallStack
let navTitle = String(localized: "Checking Link")
var urlToOpen: URL
+
@EnvironmentObject private var controller: Controller
var body: some View {
diff --git
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift
index 0799c6c..048351d 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift
@@ -14,8 +14,8 @@ struct WithdrawAcceptDone: View {
let exchangeBaseUrl: String?
let url: URL
- @EnvironmentObject private var model: WalletModel
@EnvironmentObject private var controller: Controller
+ @EnvironmentObject private var model: WalletModel
@State private var transactionId: String? = nil
diff --git
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
index bf584a5..0142d42 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
@@ -8,19 +8,18 @@ import SymLog
struct WithdrawTOSView: View {
private let symLog = SymLogV(0)
let stack: CallStack
- @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
let navTitle = String(localized: "Terms of Service")
let exchangeBaseUrl: String?
+ let viewID: Int // either VIEW_WITHDRAW_TOS or SHEET_WITHDRAW_TOS
+ let acceptAction: (() -> Void)?
+ @Environment(\.presentationMode) var presentationMode
@EnvironmentObject private var model: WalletModel
+ @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
@State var exchangeTOS: ExchangeTermsOfService?
- let viewID: Int // either VIEW_WITHDRAW_TOS or SHEET_WITHDRAW_TOS
-
- let acceptAction: (() -> Void)?
- @Environment(\.presentationMode) var presentationMode
var body: some View {
VStack {
diff --git
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
index 2fb147e..a8e5b2d 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
@@ -71,8 +71,8 @@ struct WithdrawURIView: View {
}
} else {
// Yikes no details or no baseURL
-// WithdrawProgressView(message: url.host ?? badURL)
-// .navigationTitle("Contacting Exchange")
+ WithdrawProgressView(message: url.host ?? badURL)
+ .navigationTitle("Contacting Exchange")
}
}
.onAppear() {
diff --git a/TalerWallet1/Views/Transactions/ManualDetailsV.swift
b/TalerWallet1/Views/Transactions/ManualDetailsV.swift
index 3adceaf..44fc7cf 100644
--- a/TalerWallet1/Views/Transactions/ManualDetailsV.swift
+++ b/TalerWallet1/Views/Transactions/ManualDetailsV.swift
@@ -85,8 +85,8 @@ struct ManualDetails_Previews: PreviewProvider {
static var previews: some View {
let common = TransactionCommon(type: .withdrawal,
txState: TransactionState(major: .done),
- amountEffective: try! Amount(fromString:
LONGCURRENCY + ":1.1"),
- amountRaw: try! Amount(fromString:
LONGCURRENCY + ":2.2"),
+ amountEffective: Amount(currency: LONGCURRENCY,
cent: 110),
+ amountRaw: Amount(currency: LONGCURRENCY,
cent: 220),
transactionId: "someTxID",
timestamp: Timestamp(from:
1_666_666_000_000),
txActions: [])
diff --git a/TalerWallet1/Views/Transactions/ThreeAmountsV.swift
b/TalerWallet1/Views/Transactions/ThreeAmountsV.swift
index 2579b94..0ff8f06 100644
--- a/TalerWallet1/Views/Transactions/ThreeAmountsV.swift
+++ b/TalerWallet1/Views/Transactions/ThreeAmountsV.swift
@@ -114,8 +114,8 @@ struct ThreeAmounts_Previews: PreviewProvider {
static var previews: some View {
let common = TransactionCommon(type: .withdrawal,
txState: TransactionState(major: .done),
- amountEffective: try! Amount(fromString:
LONGCURRENCY + ":0.1"),
- amountRaw: try! Amount(fromString:
LONGCURRENCY + ":0.2"),
+ amountEffective: Amount(currency: LONGCURRENCY,
cent: 10),
+ amountRaw: Amount(currency: LONGCURRENCY,
cent: 20),
transactionId: "someTxID",
timestamp: Timestamp(from:
1_666_666_000_000),
txActions: [])
diff --git a/TalerWallet1/Views/Transactions/TransactionDetailView.swift
b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
index aca9ae8..31e8780 100644
--- a/TalerWallet1/Views/Transactions/TransactionDetailView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
@@ -8,7 +8,7 @@ import SymLog
extension Transaction { // for Dummys
init(dummyCurrency: String) {
- let amount = try! Amount(fromString: "\(dummyCurrency):0")
+ let amount = Amount.zero(currency: dummyCurrency)
let now = Timestamp.now()
let common = TransactionCommon(type: .dummy,
txState: TransactionState(major:
.pending),
diff --git a/TalerWallet1/Views/Transactions/TransactionRowView.swift
b/TalerWallet1/Views/Transactions/TransactionRowView.swift
index f51e1c6..e3d71ab 100644
--- a/TalerWallet1/Views/Transactions/TransactionRowView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionRowView.swift
@@ -59,9 +59,10 @@ struct TransactionRowContentV: View {
struct TransactionRowView: View {
let transaction : Transaction
- let currencyInfo: CurrencyInfo?
+ let currency: String
@Environment(\.sizeCategory) var sizeCategory
+ @EnvironmentObject private var controller: Controller
func needVStack(available: CGFloat, contentWidth: CGFloat, valueWidth:
CGFloat) -> Bool {
available < (contentWidth + valueWidth + 40)
@@ -80,7 +81,7 @@ struct TransactionRowView: View {
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)
@@ -114,30 +115,21 @@ struct TransactionRow_Previews: PreviewProvider {
id: "some payment ID",
time: Timestamp(from:
1_666_666_000_000))
static var previews: some View {
- let testInfo = PreviewCurrencyInfo(TESTCURRENCY, digits: 0)
- let demoInfo = PreviewCurrencyInfo(TESTCURRENCY, digits: 2)
- let test = try! Amount(fromString: TESTCURRENCY + ":1.23")
- let demo = try! Amount(fromString: DEMOCURRENCY + ":1234.56")
List {
- TransactionRowView(transaction: withdrawal, currencyInfo: testInfo)
- TransactionRowView(transaction: payment, currencyInfo: demoInfo)
+ TransactionRowView(transaction: withdrawal, currency: TESTCURRENCY)
+ TransactionRowView(transaction: payment, currency: DEMOCURRENCY)
}
}
}
// MARK: -
extension Transaction { // for PreViews
init(incoming: Bool, pending: Bool, id: String, time: Timestamp) {
- let currency = LONGCURRENCY
- let raw = currency + ":5"
- let effective = currency + (incoming ? ":4.8"
- : ":5.2")
- let refRaw = currency + ":3"
- let refEff = currency + ":2.8"
+ let effective = incoming ? 480 : 520
let common = TransactionCommon(type: incoming ? .withdrawal : .payment,
txState: TransactionState(major: pending ?
TransactionMajorState.pending
:
TransactionMajorState.done),
- amountEffective: try! Amount(fromString:
effective),
- amountRaw: try! Amount(fromString: raw),
+ amountEffective: Amount(currency: LONGCURRENCY,
cent: UInt64(effective)),
+ amountRaw: Amount(currency: LONGCURRENCY,
cent: 5),
transactionId: id,
timestamp: time,
txActions: [.abort])
@@ -159,8 +151,8 @@ extension Transaction { // for PreViews
summary: "some product summary",
products: [])
let pDetails = PaymentTransactionDetails(proposalId: "some
proposal ID",
- totalRefundRaw: try!
Amount(fromString: refRaw),
- totalRefundEffective: try!
Amount(fromString: refEff),
+ totalRefundRaw:
Amount(currency: LONGCURRENCY, cent: 300),
+ totalRefundEffective:
Amount(currency: LONGCURRENCY, cent: 280),
info: info)
self = .payment(PaymentTransaction(common: common, details:
pDetails))
}
diff --git a/TalerWallet1/Views/Transactions/TransactionsListView.swift
b/TalerWallet1/Views/Transactions/TransactionsListView.swift
index 7160969..cce00e6 100644
--- a/TalerWallet1/Views/Transactions/TransactionsListView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionsListView.swift
@@ -11,7 +11,7 @@ struct TransactionsListView: View {
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
let navTitle: String
- let currencyInfo: CurrencyInfo?
+ let currency: String
let transactions: [Transaction]
let showUpDown: Bool
let reloadAllAction: (_ stack: CallStack) async -> ()
@@ -28,12 +28,11 @@ struct TransactionsListView: View {
let count = transactions.count
ScrollViewReader { scrollView in
List {
- TransactionsRowsView(symLog: symLog,
- stack: stack.push(),
- currencyInfo: currencyInfo,
- transactions: transactions,
-// reloadAllAction: reloadAllAction,
- reloadOneAction: reloadOneAction)
+ TransactionsArraySliceV(symLog: symLog,
+ stack: stack.push(),
+ currency: currency,
+ transactions: transactions,
+ reloadOneAction: reloadOneAction)
}
.id(viewId)
.listStyle(myListStyle.style).anyView
@@ -69,8 +68,7 @@ struct TransactionsListView: View {
}
.overlay {
if transactions.isEmpty {
- let unknown = String(localized: "Unknown currency")
- TransactionsEmptyView(stack: stack.push(), currency:
currencyInfo?.scope.currency ?? unknown)
+ TransactionsEmptyView(stack: stack.push(), currency: currency)
}
}
.onAppear {
@@ -79,13 +77,12 @@ struct TransactionsListView: View {
}
}
// MARK: -
-// used by TransactionsListView, and by Balances to show the last 3
transactions
-struct TransactionsRowsView: View {
+// used by TransactionsListView, and by BalancesSectionView to show the last 3
transactions
+struct TransactionsArraySliceV: View {
let symLog: SymLogV?
let stack: CallStack
- let currencyInfo: CurrencyInfo?
+ let currency: String
let transactions: [Transaction]
-// let reloadAllAction: (_ stack: CallStack) async -> ()
let reloadOneAction: ((_ transactionId: String) async throws ->
Transaction)
@EnvironmentObject private var model: WalletModel
@@ -114,7 +111,8 @@ struct TransactionsRowsView: View {
resumeAction: resumeAction)
}
} label: {
- TransactionRowView(transaction: transaction, currencyInfo:
currencyInfo)
+ TransactionRowView(transaction: transaction,
+ currency: currency)
}
.id(Int(index))
}
diff --git a/TestFlight/WhatToTest.en-US.txt b/TestFlight/WhatToTest.en-US.txt
index 4de97d4..81f22ce 100644
--- a/TestFlight/WhatToTest.en-US.txt
+++ b/TestFlight/WhatToTest.en-US.txt
@@ -1,4 +1,15 @@
+Version 0.9.3 (25)
+
+• New feature: Shortcuts Buttons (50,25,10,5) for P2P + Withdrawal
+
+- CurrencyFormatter for P2P + Withdrawal
+- BugFix: Accessibility announcements always did reset the focus to start of
view
+
+A/B test result: Nobody liked GNU Taler (with SideView & Hamburger-button)
better,
+thus we concentrate on Taler Wallet (with TabBar)
+
+
Version 0.9.3 (24)
• Sound & Haptics ON by default
diff --git a/taler-swift/Sources/taler-swift/Amount.swift
b/taler-swift/Sources/taler-swift/Amount.swift
index 25706cf..1751b20 100644
--- a/taler-swift/Sources/taler-swift/Amount.swift
+++ b/taler-swift/Sources/taler-swift/Amount.swift
@@ -47,14 +47,32 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
/// The largest possible value that can be represented.
private static let maxValue: UInt64 = 1 << 52
- /// The size of `integer` in relation to `fraction`.
- private static let fractionalBase: UInt32 = 100000000
-
/// The greatest number of fractional digits that can be represented.
private static let fractionalBaseDigits: UInt = 8
-
- /// The currency of the amount. Cannot be changed later
- private let currency: String
+
+ /// The size of `integer` in relation to `fraction`.
+ static func fractionalBase(_ power: UInt = Amount.fractionalBaseDigits) ->
UInt32 {
+ var exponent = power < Amount.fractionalBaseDigits
+ ? power : Amount.fractionalBaseDigits
+ var base: UInt32 = 1
+ for _ in 0..<exponent { base *= 10 }
+ return base
+ }
+
+ /// Convenience re-definition
+ func fractionalBase(_ power: UInt = Amount.fractionalBaseDigits) -> UInt32
{
+ Self.fractionalBase(power)
+ }
+
+ public static let decimalSeparator = "."
+
+ /// The currency of the amount. Cannot be changed later, except...
+ private var currency: String
+
+ /// ... with this function
+ public func setCurrency(_ newCurrency: String) {
+ currency = newCurrency
+ }
/// The integer value of the amount (number to the left of the decimal
point).
var integer: UInt64
@@ -81,7 +99,7 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
/// The floating point representation of the fraction.
public var fracValue: Double {
let oneThousand = 1000.0
- let base = Double(Amount.fractionalBase) / oneThousand
+ let base = Double(fractionalBase()) / oneThousand
let thousandths = Double(fraction) / base
return thousandths / oneThousand
}
@@ -95,43 +113,76 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
}
/// The tuple representation of the value.
- public var valueAsTuple: (Double, Double) {
+ public var valueAsDecimalTuple: (UInt64, UInt32) {
+ (integer, fraction)
+ }
+ /// The tuple representation of the value.
+ public var valueAsFloatTuple: (Double, Double) {
(intValue, fracValue)
}
/// The string representation of the value, formatted as
"`integer`.`fraction`",
/// no trailing zeroes, no group separator.
public var valueStr: String {
- var decimalSeparator = "."
-// if let currencySpecification { // TODO: use locale
-// decimalSeparator = currencySpecification.decimalSeparator
-// }
if fraction == 0 {
- return "\(integer)"
+ return String(integer)
} else {
var frac = fraction
var fracStr = ""
while (frac > 0) {
- fracStr += "\(frac / (Amount.fractionalBase / 10))"
- frac = (frac * 10) % Amount.fractionalBase
+ fracStr += String(frac / (fractionalBase() / 10))
+ frac = (frac * 10) % fractionalBase()
}
- return "\(integer)\(decimalSeparator)\(fracStr)"
+ return "\(integer)\(Self.decimalSeparator)\(fracStr)"
+ }
+ }
+
+ /// The string representation of the value, formatted as
"`integer``fraction`",
+ /// no group separator, no decimalSeparator, #inputDigits digits from
fraction, padded with trailing zeroes
+ public func plainString(inputDigits: UInt) -> String {
+ let base = fractionalBase()
+ var frac = fraction
+ var fracStr = ""
+ var i = inputDigits
+
+ var nextchar: UInt32 {
+ let fracChar = frac / (base / 10)
+ frac = (frac * 10) % base
+ return fracChar
+ }
+
+ if integer > 0 {
+ while (i > 0) {
+ fracStr += String(nextchar)
+ i -= 1
+ }
+ return "\(integer)\(fracStr)"
+ } else {
+ while (i > 0) {
+ let fracChar = nextchar
+ // skip leading zeroes
+ if fracStr.count > 0 || fracChar > 0 {
+ fracStr += String(fracChar)
+ }
+ i -= 1
+ }
+ return fracStr.count > 0 ? fracStr : "0"
}
}
/// read-only getter
public var currencyStr: String {
- return currency
+ currency
}
- /// The string representation of the amount, formatted as
"`currency`:`integer`.`fraction`".
+ /// The string representation of the amount, formatted as
"`currency`:`integer`.`fraction`" (without space).
public var description: String {
- return "\(currency):\(valueStr)"
+ "\(currency):\(valueStr)"
}
- /// The string representation of the amount, formatted as
"`integer`.`fraction` `currency`".
+ /// The string representation of the amount, formatted as
"`integer`.`fraction` `currency`" (with space).
public var readableDescription: String {
- return "\(valueStr) \(currency)"
+ "\(valueStr) \(currency)"
}
/// Whether the value is valid. An amount is valid if and only if the
currency is not empty and the value is less than the maximum allowed value.
@@ -144,7 +195,7 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
/// Whether this amount is zero or not.
public var isZero: Bool {
- return integer == 0 && fraction == 0
+ integer == 0 && fraction == 0
}
/// Initializes an amount by parsing a string representing the amount. The
string should be formatted as "`currency`:`integer`.`fraction`".
@@ -160,13 +211,13 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
if let dotIndex = amountStr.firstIndex(of: ".") {
let integerStr = String(amountStr[..<dotIndex])
let fractionStr = String(amountStr[string.index(dotIndex,
offsetBy: 1)...])
- if (fractionStr.count > Amount.fractionalBaseDigits) {
+ if (fractionStr.count > Self.fractionalBaseDigits) {
throw AmountError.invalidStringRepresentation
}
guard let intValue = UInt64(integerStr) else { throw
AmountError.invalidStringRepresentation }
self.integer = intValue
self.fraction = 0
- var digitValue = Amount.fractionalBase / 10
+ var digitValue = fractionalBase() / 10
for char in fractionStr {
guard let digit = char.wholeNumberValue else { throw
AmountError.invalidStringRepresentation }
self.fraction += digitValue * UInt32(digit)
@@ -195,10 +246,10 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
self.integer = integer
self.fraction = fraction
}
- public init(currency: String, value: UInt64) {
+ public init(currency: String, cent: UInt64) {
self.currency = currency
- self.integer = value / 100 // TODO: fractional digits can be
0, 2 or 3
- self.fraction = UInt32(value - (self.integer * 100))
+ self.integer = cent / 100 // For existing currencies, fractional
digits could be 0, 2 or 3
+ self.fraction = UInt32(cent - (self.integer * 100))
}
/// Initializes an amount from a decoder.
@@ -215,8 +266,8 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
/// Copies an amount.
/// - Returns: A copy of the amount.
- func copy() -> Amount {
- return Amount(currency: currency, integer: integer, fraction: fraction)
+ public func copy() -> Amount {
+ Amount(currency: currency, integer: integer, fraction: fraction)
}
/// Creates a normalized copy of an amount (the fractional part is
strictly less than one unit of currency).
@@ -235,20 +286,67 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
try container.encode(description)
}
- /// Normalizes an amount by reducing `fraction` until it is less than
`Amount.fractionalBase`, increasing `integer` appropriately.
+ /// Normalizes an amount by reducing `fraction` until it is less than
`fractionalBase()`, increasing `integer` appropriately.
/// - Throws:
/// - `AmountError.invalidAmount` if the amount is invalid either before
or after normalization.
func normalize() throws {
if !valid {
throw AmountError.invalidAmount
}
- integer += UInt64(fraction / Amount.fractionalBase)
- fraction = fraction % Amount.fractionalBase
+ integer += UInt64(fraction / fractionalBase())
+ fraction = fraction % fractionalBase()
if !valid {
throw AmountError.invalidAmount
}
}
+ /// Divides by ten
+ public func shiftRight() {
+ var remainder = UInt32(integer % 10)
+ self.integer = integer / 10
+
+ remainder = remainder * fractionalBase() + fraction
+ self.fraction = remainder / 10
+ }
+
+ /// Multiplies by ten, then adds digit
+ public func shiftLeft(add digit: UInt8, _ inputDigits: UInt) {
+ // how many digits to shift right (e.g. inputD=2 ==> shift:=6)
+ let shift = Self.fractionalBaseDigits - inputDigits
+ // mask to zero out fractions smaller than inputDigits
+ let shiftMask = fractionalBase(shift)
+
+ let carryMask = fractionalBase(Self.fractionalBaseDigits - 1)
+ // get biggest fractional digit
+ let carry = fraction / carryMask
+ var remainder = fraction % carryMask
+// print("fraction: \(fraction) = \(carry) + \(remainder)")
+
+ let shiftedInt = integer * 10 + UInt64(carry)
+ if shiftedInt < Self.maxValue {
+ self.integer = shiftedInt
+// print("remainder: \(remainder) / shiftMask \(shiftMask) =
\(remainder / shiftMask)")
+ remainder = (remainder / shiftMask) * 10
+ } else { // will get too big
+ // Just swap the last significant digit for the one the user typed
last
+ if shiftMask >= 10 {
+ remainder = (remainder / (shiftMask / 10)) * 10
+ } else {
+ remainder = (remainder / 10) * 10
+ }
+ }
+ let sum = remainder + UInt32(digit)
+ self.fraction = sum * shiftMask
+// print("(remainder: \(remainder) + \(digit)) * base(shift)
\(shiftMask) = fraction \(fraction)")
+ }
+
+ /// Sets all fractional digits after inputDigits to 0
+ public func mask(_ inputDigits: UInt) {
+ let mask = fractionalBase(Self.fractionalBaseDigits - inputDigits)
+ let remainder = fraction % mask
+ self.fraction -= remainder
+ }
+
/// Adds two amounts together.
/// - Parameters:
/// - left: The amount on the left.
@@ -285,7 +383,7 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
if (leftNormalized.fraction < rightNormalized.fraction) {
guard leftNormalized.integer != 0 else { throw
AmountError.negativeAmount }
leftNormalized.integer -= 1
- leftNormalized.fraction += Amount.fractionalBase
+ leftNormalized.fraction += fractionalBase()
}
guard leftNormalized.integer >= rightNormalized.integer else { throw
AmountError.negativeAmount }
let diff = Amount.zero(currency: left.currency)
@@ -314,8 +412,8 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
var remainder = result.integer % UInt64(divisor)
result.integer = result.integer / UInt64(divisor)
- let fractionalBase = UInt64(Amount.fractionalBase)
- remainder = (remainder * fractionalBase) + UInt64(result.fraction)
+ let fractionalBase64 = UInt64(fractionalBase())
+ remainder = (remainder * fractionalBase64) + UInt64(result.fraction)
result.fraction = UInt32(remainder / UInt64(divisor))
try result.normalize()
return result
@@ -330,8 +428,8 @@ public final class Amount: Codable, Hashable, @unchecked
Sendable, CustomStringC
let result = try amount.normalizedCopy()
result.integer = result.integer * UInt64(factor)
let fraction_tmp = UInt64(result.fraction) * UInt64(factor)
- result.integer += fraction_tmp / UInt64(Amount.fractionalBase)
- result.fraction = UInt32(fraction_tmp % UInt64(Amount.fractionalBase))
+ result.integer += fraction_tmp / UInt64(fractionalBase())
+ result.fraction = UInt32(fraction_tmp % UInt64(fractionalBase()))
return result
}
diff --git a/taler-swift/Tests/taler-swiftTests/AmountTests.swift
b/taler-swift/Tests/taler-swiftTests/AmountTests.swift
index a0b1f27..27e5dae 100644
--- a/taler-swift/Tests/taler-swiftTests/AmountTests.swift
+++ b/taler-swift/Tests/taler-swiftTests/AmountTests.swift
@@ -10,31 +10,31 @@ class AmountTests: XCTestCase {
var str = "TESTKUDOS:23.42"
var amt = try! Amount(fromString: str)
XCTAssert(str == amt.description)
- XCTAssert("TESTKUDOS" == amt.currency)
+ XCTAssert("TESTKUDOS" == amt.currencyStr)
XCTAssert(23 == amt.value)
XCTAssert(UInt64(0.42 * 1e8) == amt.fraction)
str = "EUR:500000000.00000001"
amt = try! Amount(fromString: str)
XCTAssert(str == amt.description)
- XCTAssert("EUR" == amt.currency)
+ XCTAssert("EUR" == amt.currencyStr)
XCTAssert(500000000 == amt.value)
XCTAssert(1 == amt.fraction)
str = "EUR:1500000000.00000003"
amt = try! Amount(fromString: str)
XCTAssert(str == amt.description)
- XCTAssert("EUR" == amt.currency)
+ XCTAssert("EUR" == amt.currencyStr)
XCTAssert(1500000000 == amt.value)
XCTAssert(3 == amt.fraction)
- let maxValue = 4503599627370496
+ let maxValue = 4503599627370496 // 16 significant
digits are 1 too many for double
str = "TESTKUDOS123:\(maxValue).99999999"
amt = try! Amount(fromString: str)
XCTAssert(str == amt.description)
- XCTAssert("TESTKUDOS123" == amt.currency)
- XCTAssert(maxValue == amt.value)
-
+ XCTAssert("TESTKUDOS123" == amt.currencyStr)
+ XCTAssert(Double(maxValue) == amt.value)
+
XCTAssertThrowsError(try Amount(fromString:
"TESTKUDOS1234:\(maxValue).99999999"))
XCTAssertThrowsError(try Amount(fromString: "TESTKUDOS123:\(maxValue +
1).99999999"))
XCTAssertThrowsError(try Amount(fromString:
"TESTKUDOS123:\(maxValue).999999990"))
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [taler-taler-ios] branch master updated (e082595 -> e79881c),
gnunet <=
- [taler-taler-ios] 04/36: DD51 fractional base, gnunet, 2023/11/13
- [taler-taler-ios] 05/36: fix tests, gnunet, 2023/11/13
- [taler-taler-ios] 03/36: Exchange with scopeInfo, gnunet, 2023/11/13
- [taler-taler-ios] 02/36: Transaction, L10N, gnunet, 2023/11/13
- [taler-taler-ios] 19/36: amountToTransfer Pending, gnunet, 2023/11/13
- [taler-taler-ios] 23/36: amountToTransfer Exchange, gnunet, 2023/11/13
- [taler-taler-ios] 14/36: Debugging: Delay currency info, gnunet, 2023/11/13
- [taler-taler-ios] 27/36: Debugging, gnunet, 2023/11/13
- [taler-taler-ios] 30/36: Bugfix, gnunet, 2023/11/13
- [taler-taler-ios] 12/36: DD51 Currency rendering, gnunet, 2023/11/13