commit 02e39080f1d77fdfa82cc3ec7c29570d83ec7311
parent 04db105e38c6120fa7445aedc93ae7134974d3d4
Author: Marc Stibane <marc@taler.net>
Date: Sun, 12 Nov 2023 18:22:39 +0100
amountToTransfer Currency
Diffstat:
6 files changed, 87 insertions(+), 87 deletions(-)
diff --git a/TalerWallet1/Views/Exchange/ManualWithdraw.swift b/TalerWallet1/Views/Exchange/ManualWithdraw.swift
@@ -28,13 +28,12 @@ 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(amount: $amountToTransfer) // becomeFirstResponder
// let agePicker = AgePicker(ageMenuList: $ageMenuList, selectedAge: $selectedAge)
ScrollView {
VStack {
- CurrencyInputView(currencyField: currencyField,
- title: String(localized: "Amount to withdraw:"))
+ CurrencyInputView(amount: $amountToTransfer,
+ title: String(localized: "Amount to withdraw:"))
let someCoins = SomeCoins(details: withdrawalAmountDetails)
QuiteSomeCoins(someCoins: someCoins, shouldShowFee: true,
currency: currency, amountEffective: withdrawalAmountDetails?.amountEffective)
diff --git a/TalerWallet1/Views/HelperViews/CurrencyField.swift b/TalerWallet1/Views/HelperViews/CurrencyField.swift
@@ -22,10 +22,14 @@
import SwiftUI
import UIKit
import taler_swift
+import SymLog
@MainActor
-public struct CurrencyField: View {
+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 {
@@ -36,34 +40,22 @@ public struct CurrencyField: View {
currencyInputField.resignFirstResponder()
}
- private var label: String {
- let mag = pow(10, formatter.maximumFractionDigits)
- return formatter.string(for: Decimal(value) / mag) ?? ""
- }
-
- 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
@@ -90,8 +82,8 @@ 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 {
@@ -126,7 +118,7 @@ struct CurrencyInputField: UIViewRepresentable {
)
// Set initial textfield text
- context.coordinator.updateText(value, textField: textField)
+ context.coordinator.updateText(amount, textField: textField)
return textField
}
@@ -144,30 +136,33 @@ 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)
+ textField.text = lastValidInput
}
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
}
@@ -189,7 +184,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
@@ -197,27 +192,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
@@ -3,13 +3,18 @@
* See LICENSE.md
*/
import SwiftUI
+import taler_swift
struct CurrencyInputView: View {
- let currencyField: CurrencyField
+ @Binding var amount: Amount // the `value´
let title: String
+ @EnvironmentObject private var controller: Controller
@State var hasBeenShown = false
+
var body: some View {
+ let currencyInfo = controller.info(for: amount.currencyStr, controller.currencyTicker)
+ let currencyField = CurrencyField(amount: $amount, currencyInfo: currencyInfo)
VStack (alignment: .leading) {
Text(title)
// .padding(.top)
@@ -22,9 +27,9 @@ struct CurrencyInputView: View {
.textFieldStyle(.roundedBorder)
}.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()
@@ -37,20 +42,19 @@ struct CurrencyInputView: View {
}
// MARK: -
#if DEBUG
-fileprivate struct BindingViewContainer : View {
- @State var centsToTransfer: UInt64 = 0
+struct CurrencyInputView_Previews: PreviewProvider {
+ struct StateContainer : View {
+ @State var amountToTransfer = Amount(currency: LONGCURRENCY, cent: 0)
var body: some View {
- let currencyField = CurrencyField(value: $centsToTransfer, currency: LONGCURRENCY)
- CurrencyInputView(currencyField: currencyField,
- title: "Amount to withdraw:")
+ CurrencyInputView(amount: $amountToTransfer,
+ title: "Amount to withdraw:")
}
-}
+ }
-struct CurrencyInputView_Previews: PreviewProvider {
static var previews: some View {
List {
- BindingViewContainer()
+ StateContainer()
}
}
}
diff --git a/TalerWallet1/Views/Peer2peer/RequestPayment.swift b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
@@ -26,10 +26,9 @@ struct RequestPayment: View {
#endif
let currency = amountToTransfer.currencyStr
let navTitle = String(localized: "Request Money", comment: "Dialog Title")
- let currencyField = CurrencyField(amount: $amountToTransfer)
ScrollView { VStack {
- CurrencyInputView(currencyField: currencyField,
+ CurrencyInputView(amount: $amountToTransfer,
title: String(localized: "Amount to request:"))
let someCoins = SomeCoins(details: peerPullCheck)
diff --git a/TalerWallet1/Views/Peer2peer/SendAmount.swift b/TalerWallet1/Views/Peer2peer/SendAmount.swift
@@ -38,7 +38,6 @@ struct SendAmount: View {
#endif
let currency = amountAvailable.currencyStr
let navTitle = String(localized: "Send \(currency)", comment: "Send currency, Dialog Title")
- let currencyField = CurrencyField(amount: $amountToTransfer)
let fee = fee(ppCheck: peerPushCheck)
ScrollView {
VStack(alignment: .trailing) {
@@ -46,7 +45,7 @@ struct SendAmount: View {
Text("Available: \(available)")
.accessibilityFont(.title3)
.padding(.bottom, 2)
- CurrencyInputView(currencyField: currencyField,
+ CurrencyInputView(amount: $amountToTransfer,
title: String(localized: "Amount to send:"))
Text("+ \(fee) payment fee")
.accessibilityFont(.body)
diff --git a/taler-swift/Sources/taler-swift/Amount.swift b/taler-swift/Sources/taler-swift/Amount.swift
@@ -247,7 +247,7 @@ public final class Amount: Codable, Hashable, @unchecked Sendable, CustomStringC
/// Copies an amount.
/// - Returns: A copy of the amount.
- func copy() -> Amount {
+ public func copy() -> Amount {
Amount(currency: currency, integer: integer, fraction: fraction)
}
@@ -283,36 +283,49 @@ public final class Amount: Codable, Hashable, @unchecked Sendable, CustomStringC
/// Divides by ten
public func shiftRight() {
- var remainder = integer % 10
+ var remainder = UInt32(integer % 10)
self.integer = integer / 10
- let fractionalBase64 = UInt64(fractionalBase())
- remainder = (remainder * fractionalBase64) + UInt64(fraction)
- self.fraction = UInt32(remainder / 10)
+ remainder = remainder * fractionalBase() + fraction
+ self.fraction = remainder / 10
}
/// Multiplies by ten, then adds digit
public func shiftLeft(add digit: UInt8, _ inputDigits: UInt) {
- let mask = fractionalBase(Self.fractionalBaseDigits - inputDigits)
- let shiftedInt = integer * 10
- + UInt64(fraction / (fractionalBase() / 10))
+ // 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
- let remainder = (fraction % mask) * 10 + UInt32(digit)
- self.fraction = remainder * fractionalBase(inputDigits)
+// 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
- let remainder = (fraction % (mask / 10)) * 10 + UInt32(digit)
- self.fraction = remainder * fractionalBase(inputDigits)
+ 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 * fractionalBase(inputDigits)
+ self.fraction -= remainder
}
/// Adds two amounts together.