aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Stibane <marc@taler.net>2023-11-18 19:24:55 +0100
committerMarc Stibane <marc@taler.net>2023-11-18 19:26:04 +0100
commitfbf878e49ff96b939322323b1fd6b3200c714f69 (patch)
treeec1b5a045990b92d416e88a748c6a8ad67da4579
parent5d877d3c8824d7f769f2b37aa50393cdba967dbd (diff)
downloadtaler-ios-fbf878e49ff96b939322323b1fd6b3200c714f69.tar.gz
taler-ios-fbf878e49ff96b939322323b1fd6b3200c714f69.tar.bz2
taler-ios-fbf878e49ff96b939322323b1fd6b3200c714f69.zip
ShortcutButton
-rw-r--r--TalerWallet1/Views/Exchange/ManualWithdraw.swift3
-rw-r--r--TalerWallet1/Views/HelperViews/CurrencyField.swift70
-rw-r--r--TalerWallet1/Views/HelperViews/CurrencyInputView.swift109
-rw-r--r--TalerWallet1/Views/Peer2peer/RequestPayment.swift30
-rw-r--r--TalerWallet1/Views/Peer2peer/SendAmount.swift4
-rw-r--r--TalerWallet1/Views/Peer2peer/SendPurpose.swift16
6 files changed, 166 insertions, 66 deletions
diff --git a/TalerWallet1/Views/Exchange/ManualWithdraw.swift b/TalerWallet1/Views/Exchange/ManualWithdraw.swift
index e371df4..6b17785 100644
--- a/TalerWallet1/Views/Exchange/ManualWithdraw.swift
+++ b/TalerWallet1/Views/Exchange/ManualWithdraw.swift
@@ -34,8 +34,7 @@ struct ManualWithdraw: View {
VStack {
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"))
+ : String(localized: "Amount to withdraw:"))
let someCoins = SomeCoins(details: withdrawalAmountDetails)
QuiteSomeCoins(someCoins: someCoins,
shouldShowFee: true, // TODO: set to false if we never charge withdrawal fees
diff --git a/TalerWallet1/Views/HelperViews/CurrencyField.swift b/TalerWallet1/Views/HelperViews/CurrencyField.swift
index 784c9dd..7b74114 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyField.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyField.swift
@@ -30,25 +30,25 @@ struct CurrencyField: View {
@Binding var amount: Amount // the `value´
let currencyInfo: CurrencyInfo
- private var currencyInputField: CurrencyInputField! = nil
+ private var currencyFieldRepresentable: CurrencyTextfieldRepresentable! = nil
- public func becomeFirstResponder() -> Void {
- currencyInputField.becomeFirstResponder()
+ public func becomeFirstResponder() -> Bool {
+ currencyFieldRepresentable.becomeFirstResponder()
}
public func resignFirstResponder() -> Void {
- currencyInputField.resignFirstResponder()
+ currencyFieldRepresentable.resignFirstResponder()
}
func updateText(amount: Amount) {
- currencyInputField.updateText(amount: amount)
+ currencyFieldRepresentable.updateText(amount: amount)
}
public init(amount: Binding<Amount>, currencyInfo: CurrencyInfo) {
self._amount = amount
self.currencyInfo = currencyInfo
- self.currencyInputField = CurrencyInputField(amount: self.$amount,
- currencyInfo: currencyInfo)
+ self.currencyFieldRepresentable = CurrencyTextfieldRepresentable(amount: self.$amount,
+ currencyInfo: currencyInfo)
}
var body: some View {
@@ -61,11 +61,13 @@ struct CurrencyField: View {
// Set as priority so CurrencyInputField size doesn't affect parent
Text(amount.string(currencyInfo))
.layoutPriority(1)
+ // make the textfield use the whole width for tapping inside to become active
+ .frame(maxWidth: .infinity, alignment: .trailing)
+// .background(.clear) this is not the problem of the white corners
// Input text field to handle UI
- currencyInputField
- .accessibilityHidden(true)
-// .textFieldStyle(.roundedBorder)
+ currencyFieldRepresentable
+// .accessibilityHidden(true)
}
}
}
@@ -86,7 +88,7 @@ class NoCaretTextField: UITextField {
}
@MainActor
-struct CurrencyInputField: UIViewRepresentable {
+struct CurrencyTextfieldRepresentable: UIViewRepresentable {
@Binding var amount: Amount
let currencyInfo: CurrencyInfo
@@ -96,12 +98,13 @@ struct CurrencyInputField: UIViewRepresentable {
Coordinator(self)
}
- @MainActor public func becomeFirstResponder() -> Void {
+ @MainActor public func becomeFirstResponder() -> Bool {
textField.becomeFirstResponder()
}
@MainActor public func resignFirstResponder() -> Void {
textField.resignFirstResponder()
+ Self.endEditing()
}
func updateText(amount: Amount) {
@@ -113,17 +116,29 @@ struct CurrencyInputField: UIViewRepresentable {
}
func makeUIView(context: Context) -> NoCaretTextField {
+ textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
+
// Assign delegate
textField.delegate = context.coordinator
// Set keyboard type
textField.keyboardType = .numberPad
- // Make visual components invisible
+ // Make visual components invisible...
textField.tintColor = .clear
textField.textColor = .clear
textField.backgroundColor = .clear
+ // ... except for the bezel around the textfield
+ textField.borderStyle = .roundedRect
+// textField.textFieldStyle(.roundedBorder)
+#if DEBUG
+ // Debugging: add a red border around the textfield
+ let myColor = UIColor(red: 0.9, green: 0.1, blue:0, alpha: 1.0)
+ textField.layer.masksToBounds = true
+ textField.layer.borderColor = myColor.cgColor
+ // textField.layer.borderWidth = 2.0 // <- uncomment to show the border
+#endif
// Add editingChanged event handler
textField.addTarget(
context.coordinator,
@@ -141,13 +156,13 @@ struct CurrencyInputField: UIViewRepresentable {
class Coordinator: NSObject, UITextFieldDelegate {
// Reference to currency input field
- private var input: CurrencyInputField
+ private var textfieldRepresentable: CurrencyTextfieldRepresentable
// Last valid text input string to be displayed
private var lastValidInput: String? = ""
- init(_ currencyTextField: CurrencyInputField) {
- self.input = currencyTextField
+ init(_ representable: CurrencyTextfieldRepresentable) {
+ self.textfieldRepresentable = representable
}
func setValue(_ amount: Amount, textField: UITextField) {
@@ -155,12 +170,12 @@ struct CurrencyInputField: UIViewRepresentable {
updateText(amount, textField: textField)
// Update input value
// print(input.amount.description, " := ", amount.description)
- input.amount = amount
+ textfieldRepresentable.amount = amount
}
func updateText(_ amount: Amount, textField: UITextField) {
// Update field text and last valid input text
- lastValidInput = amount.plainString(input.currencyInfo)
+ lastValidInput = amount.plainString(textfieldRepresentable.currencyInfo)
// print("lastValidInput: `\(lastValidInput)´")
textField.text = lastValidInput
let endPosition = textField.endOfDocument
@@ -171,18 +186,27 @@ struct CurrencyInputField: UIViewRepresentable {
// If replacement string is empty, we can assume the backspace key was hit
if string.isEmpty {
// Resign first responder when delete is hit when value is 0
- if input.amount.isZero {
+ if textfieldRepresentable.amount.isZero {
textField.resignFirstResponder()
+// Self.endEditing()
} else {
// Remove trailing digit: divide value by 10
- let amount = input.amount.copy()
- amount.removeDigit(input.currencyInfo)
+ let amount = textfieldRepresentable.amount.copy()
+ amount.removeDigit(textfieldRepresentable.currencyInfo)
setValue(amount, textField: textField)
}
}
return true
}
+ func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
+ return true
+ }
+
+ func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
+ return true
+ }
+
@objc func editingChanged(textField: NoCaretTextField) {
// Get a mutable copy of last text
guard var oldText = lastValidInput else {
@@ -209,8 +233,8 @@ struct CurrencyInputField: UIViewRepresentable {
// Multiply by 10 to shift numbers one position to the left, revert if an overflow occurs
// Add the new trailing digit, revert if an overflow occurs
- let amount = input.amount.copy()
- amount.addDigit(digit, currencyInfo: input.currencyInfo)
+ let amount = textfieldRepresentable.amount.copy()
+ amount.addDigit(digit, currencyInfo: textfieldRepresentable.currencyInfo)
// If new value has more digits than allowed by formatter, revert
// if input.formatter.maximumFractionDigits + input.formatter.maximumIntegerDigits < String(addValue).count {
diff --git a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
index 7d6c045..6155802 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
@@ -5,24 +5,60 @@
import SwiftUI
import taler_swift
+fileprivate let shortcuts = [5000,2500,1000,500] // TODO: adapt for ¥
+
+struct ShortcutButton: View {
+ let shortcut: Int
+ let currency: String
+ let currencyInfo: CurrencyInfo
+ let currencyField: CurrencyField
+ let action: (Int, CurrencyField) -> Void
+
+ func makeButton(with newShortcut: Int) -> ShortcutButton {
+ ShortcutButton(shortcut: newShortcut,
+ currency: currency,
+ currencyInfo: currencyInfo,
+ currencyField: currencyField,
+ action: action)
+ }
+
+ var body: some View {
+ let shortie = Amount(currency: currency, cent: UInt64(shortcut)) // TODO: adapt for ¥
+ let title = shortie.string(currencyInfo)
+ let shortcutLabel = String(localized: "Shortcut", comment: "VoiceOver: $50,$25,$10,$5 shortcut buttons")
+ Button(action: { action(shortcut, currencyField)} ) {
+ Text(title)
+ .accessibilityFont(.callout)
+ }.buttonStyle(.bordered)
+ .accessibilityLabel("\(shortcutLabel) \(title)")
+ }
+}
+// MARK: -
struct CurrencyInputView: View {
@Binding var amount: Amount // the `value´
let title: String
- let shortcutLabel: String
@EnvironmentObject private var controller: Controller
- @State var hasBeenShown = false
+ @State private var hasBeenShown = false
+ @State private var showKeyboard = 0
+ @State private var useShortcut = 0
+
+ func action(shortcut: Int, currencyField: CurrencyField) {
+ useShortcut = shortcut
+ let shortie = Amount(currency: amount.currencyStr, cent: UInt64(shortcut)) // TODO: adapt for ¥
+ currencyField.updateText(amount: shortie)
+ amount = shortie
+ currencyField.resignFirstResponder()
+ }
var body: some View {
- let shortcuts = [50,25,10,5]
let currency = amount.currencyStr
let currencyInfo = controller.info(for: currency, controller.currencyTicker)
let currencyField = CurrencyField(amount: $amount, currencyInfo: currencyInfo)
VStack (alignment: .center) {
HStack {
Text(title)
-// .padding(.top)
.accessibilityFont(.title3)
.accessibilityAddTraits(.isHeader)
.accessibilityRemoveTraits(.isStaticText)
@@ -34,29 +70,67 @@ struct CurrencyInputView: View {
.background(WalletColors().fieldBackground)
.accessibilityFont(.title2)
.textFieldStyle(.roundedBorder)
- HStack {
- ForEach(shortcuts, id: \.self) { shortcut in
- let shortie = Amount(currency: currency, integer: UInt64(shortcut), fraction: 0)
- let title = shortie.string(currencyInfo)
- Button(title) {
- currencyField.updateText(amount: shortie)
- amount = shortie
- }.buttonStyle(.bordered)
- .accessibilityLabel("\(shortcutLabel) \(title)")
+ .onTapGesture {
+ if useShortcut != 0 {
+ amount = Amount.zero(currency: currency)
+ useShortcut = 0
+ }
+ showKeyboard += 1
}
- }
+ if #available(iOS 16.0, *) {
+ let shortcutButton = ShortcutButton(shortcut: 0,
+ currency: currency,
+ currencyInfo: currencyInfo,
+ currencyField: currencyField,
+ action: action)
+ ViewThatFits(in: .horizontal) {
+ HStack {
+ ForEach(shortcuts, id: \.self) {
+ shortcutButton.makeButton(with: $0)
+ .accessibilityAddTraits($0 == useShortcut ? .isSelected : [])
+ }
+ }
+ VStack {
+ let count = shortcuts.count
+ let half = count / 2
+ HStack {
+ ForEach(0..<half, id: \.self) { index in
+ let thisShortcut = shortcuts[index]
+ shortcutButton.makeButton(with: thisShortcut)
+ .accessibilityAddTraits(thisShortcut == useShortcut ? .isSelected : [])
+ }
+ }
+ HStack {
+ ForEach(half..<count, id: \.self) { index in
+ let thisShortcut = shortcuts[index]
+ shortcutButton.makeButton(with: thisShortcut)
+ .accessibilityAddTraits(thisShortcut == useShortcut ? .isSelected : [])
+ }
+ }
+ }
+ VStack {
+ ForEach(shortcuts, id: \.self) {
+ shortcutButton.makeButton(with: $0)
+ .accessibilityAddTraits($0 == useShortcut ? .isSelected : [])
+ }
+ }
+ }
+ } // iOS 16+ only
}.onAppear { // make CurrencyField show the keyboard after 0.4 seconds
if hasBeenShown {
// print("❗️Yikes: CurrencyInputView hasBeenShown")
} else {
-// print("❗️Yikes: First CurrencyInputView❗️")
+ print("❗️CurrencyInputView❗️")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
hasBeenShown = true
- currencyField.becomeFirstResponder()
+ if !currencyField.becomeFirstResponder() {
+ print("❗️Yikes❗️")
+ }
}
}
}.onDisappear {
currencyField.resignFirstResponder()
+ hasBeenShown = false
}
}
}
@@ -70,8 +144,7 @@ fileprivate struct Previews: PreviewProvider {
var body: some View {
// Preview_Content()
CurrencyInputView(amount: $amountToTransfer,
- title: "Amount to withdraw:",
- shortcutLabel: "Withdraw")
+ title: "Amount to withdraw:")
.environmentObject(controller)
}
}
diff --git a/TalerWallet1/Views/Peer2peer/RequestPayment.swift b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
index 618ddee..5192731 100644
--- a/TalerWallet1/Views/Peer2peer/RequestPayment.swift
+++ b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
@@ -31,8 +31,7 @@ struct RequestPayment: View {
ScrollView { VStack {
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"))
+ : String(localized: "Amount to request:"))
let someCoins = SomeCoins(details: peerPullCheck)
QuiteSomeCoins(someCoins: someCoins,
@@ -40,29 +39,24 @@ struct RequestPayment: View {
currency: currency,
amountEffective: peerPullCheck?.amountEffective)
- HStack {
- let disabled = amountToTransfer.isZero || someCoins.invalid || someCoins.tooMany
+ let disabled = amountToTransfer.isZero || someCoins.invalid || someCoins.tooMany
- NavigationLink(destination: LazyView {
- RequestPurpose(stack: stack.push(),
- amountToTransfer: amountToTransfer,
- fee: someCoins.fee,
- summary: $summary,
- expireDays: $expireDays)
-// { deactivateAction() }
- }) {
- Text("Request \(amountToTransfer.readableDescription)") // TODO: formatter
- }
+ NavigationLink(destination: LazyView {
+ RequestPurpose(stack: stack.push(),
+ amountToTransfer: amountToTransfer,
+ fee: someCoins.fee,
+ summary: $summary,
+ expireDays: $expireDays)
+ }) { Text("Next") }
.buttonStyle(TalerButtonStyle(type: .prominent))
.disabled(disabled)
- }
Spacer()
} } // ScrollVStack
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
.background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
.navigationTitle(navTitle)
- .onAppear { // make CurrencyField show the keyboard
+ .onAppear {
DebugViewC.shared.setViewID(VIEW_REQUEST_P2P, stack: stack.push())
symLog.log("❗️Yikes \(navTitle) onAppear")
}
@@ -70,7 +64,9 @@ struct RequestPayment: View {
symLog.log("❗️Yikes \(navTitle) onDisappear")
}
.task(id: amountToTransfer.value) {
- if !amountToTransfer.isZero {
+ if amountToTransfer.isZero {
+// fee = EMPTYSTRING
+ } else {
do {
let ppCheck = try await model.checkPeerPullCreditM(amountToTransfer, exchangeBaseUrl: nil)
peerPullCheck = ppCheck // redraw
diff --git a/TalerWallet1/Views/Peer2peer/SendAmount.swift b/TalerWallet1/Views/Peer2peer/SendAmount.swift
index 7eef7c7..3088ff3 100644
--- a/TalerWallet1/Views/Peer2peer/SendAmount.swift
+++ b/TalerWallet1/Views/Peer2peer/SendAmount.swift
@@ -59,8 +59,7 @@ struct SendAmount: View {
.padding(.bottom, 2)
CurrencyInputView(amount: $amountToTransfer,
title: iconOnly ? String(localized: "How much:")
- : String(localized: "Amount to send:"),
- shortcutLabel: String(localized: "Send", comment: "VoiceOver: Send $50,$25,$10,$5 shortcut buttons"))
+ : String(localized: "Amount to send:"))
let disabled = insufficient || amountToTransfer.isZero
Text(insufficient ? insufficientLabel
: feeLabel)
@@ -73,6 +72,7 @@ struct SendAmount: View {
amountAvailable: amountAvailable,
amountToTransfer: amountToTransfer,
fee: fee,
+ currencyInfo: currencyInfo,
summary: $summary,
expireDays: $expireDays)
}) { Text("Next") }
diff --git a/TalerWallet1/Views/Peer2peer/SendPurpose.swift b/TalerWallet1/Views/Peer2peer/SendPurpose.swift
index 6d7c619..ba17c92 100644
--- a/TalerWallet1/Views/Peer2peer/SendPurpose.swift
+++ b/TalerWallet1/Views/Peer2peer/SendPurpose.swift
@@ -13,6 +13,7 @@ struct SendPurpose: View {
let amountAvailable: Amount
let amountToTransfer: Amount
let fee: String
+ let currencyInfo: CurrencyInfo
@Binding var summary: String
@Binding var expireDays: UInt
@AppStorage("iconOnly") var iconOnly: Bool = false
@@ -22,15 +23,23 @@ struct SendPurpose: View {
@FocusState private var isFocused: Bool
var body: some View {
+#if DEBUG
+ let _ = Self._printChanges()
+ let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear
+#endif
+ let currency = amountAvailable.currencyStr
+ let current = amountToTransfer.string(currencyInfo)
ScrollView { VStack (spacing: 6) {
- Text(amountToTransfer.readableDescription) // TODO: curreny formatter
+ Text(current)
Text("+ \(fee) payment fee")
.accessibilityFont(.body)
.foregroundColor(.red)
VStack(alignment: .leading, spacing: 6) {
if !iconOnly {
- Text("Subject:") // Purpose
+ Text("Enter subject:") // Purpose
.accessibilityFont(.title2)
+ .accessibilityAddTraits(.isHeader)
+ .accessibilityRemoveTraits(.isStaticText)
.padding(.top)
}
Group { if #available(iOS 16.0, *) {
@@ -74,7 +83,7 @@ struct SendPurpose: View {
expireDays: expireDays,
transactionStarted: $transactionStarted)
}) {
- Text("Send \(amountToTransfer.readableDescription) now", comment: "amountToTransfer") // TODO: currency formatter
+ Text("Send \(current) now", comment: "amountToTransfer") // TODO: currency formatter
}
.buttonStyle(TalerButtonStyle(type: .prominent))
.disabled(disabled)
@@ -82,7 +91,6 @@ struct SendPurpose: View {
Spacer()
}
- .textFieldStyle(.roundedBorder)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
} } // ScrollVStack