commit 186b6438d1b1557d8b8a2edde3ea7d343835a617
parent baacfb572f479c863044b443f1959f7518daef84
Author: Marc Stibane <marc@taler.net>
Date: Fri, 8 Nov 2024 13:30:49 +0100
split
Diffstat:
3 files changed, 271 insertions(+), 241 deletions(-)
diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj
@@ -11,6 +11,8 @@
4E0A71152C396D86002485BB /* Error+debugDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0A71132C396D86002485BB /* Error+debugDescription.swift */; };
4E0A71182C3AB099002485BB /* IconBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0A71172C3AB099002485BB /* IconBadge.swift */; };
4E0A71192C3AB099002485BB /* IconBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0A71172C3AB099002485BB /* IconBadge.swift */; };
+ 4E11803D2CD2A6D700023E37 /* SendAmountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E11803C2CD2A6D700023E37 /* SendAmountView.swift */; };
+ 4E11803E2CD2A6D700023E37 /* SendAmountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E11803C2CD2A6D700023E37 /* SendAmountView.swift */; };
4E16E12329F3BB99008B9C86 /* CurrencySpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */; };
4E18539A2BDAE6D40034F3BA /* LocalConsole in Frameworks */ = {isa = PBXBuildFile; productRef = 4E1853992BDAE6D40034F3BA /* LocalConsole */; };
4E18539C2BDAE6E50034F3BA /* LocalConsole in Frameworks */ = {isa = PBXBuildFile; productRef = 4E18539B2BDAE6E50034F3BA /* LocalConsole */; };
@@ -356,6 +358,7 @@
/* Begin PBXFileReference section */
4E0A71132C396D86002485BB /* Error+debugDescription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Error+debugDescription.swift"; sourceTree = "<group>"; };
4E0A71172C3AB099002485BB /* IconBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconBadge.swift; sourceTree = "<group>"; };
+ 4E11803C2CD2A6D700023E37 /* SendAmountView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendAmountView.swift; sourceTree = "<group>"; };
4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrencySpecification.swift; sourceTree = "<group>"; };
4E1A59E02C99C5D700842BBF /* View+Keyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+Keyboard.swift"; sourceTree = "<group>"; };
4E2254952A822B8100E41D29 /* payment_received.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = payment_received.m4a; sourceTree = "<group>"; };
@@ -874,6 +877,7 @@
isa = PBXGroup;
children = (
4E40E0BD29F25ABB00B85369 /* SendAmountV.swift */,
+ 4E11803C2CD2A6D700023E37 /* SendAmountView.swift */,
4E9320442A1645B600A87B0E /* RequestPayment.swift */,
4E7940DD29FC307C00A9AEA1 /* P2PSubjectV.swift */,
4EB3136029FEE79B007D68BC /* P2PReadyV.swift */,
@@ -1304,6 +1308,7 @@
4E3EAE6A2A990778009F1BE8 /* ThreeAmountsSection.swift in Sources */,
4E3EAE6B2A990778009F1BE8 /* Model+Withdraw.swift in Sources */,
4ED80E882B8F5FB8008BD576 /* CStringArray.swift in Sources */,
+ 4E11803D2CD2A6D700023E37 /* SendAmountView.swift in Sources */,
4E3EAE6C2A990778009F1BE8 /* ExchangeSectionView.swift in Sources */,
4E3EAE6D2A990778009F1BE8 /* P2PSubjectV.swift in Sources */,
4E6EF56B2B65A33300AF252A /* PaymentDone.swift in Sources */,
@@ -1437,6 +1442,7 @@
4ED2F94B2A278F5100453B40 /* ThreeAmountsSection.swift in Sources */,
4EB095622989CBFE0043A8A1 /* Model+Withdraw.swift in Sources */,
4ED80E892B8F5FB8008BD576 /* CStringArray.swift in Sources */,
+ 4E11803E2CD2A6D700023E37 /* SendAmountView.swift in Sources */,
4EC90C782A1B528B0071DC58 /* ExchangeSectionView.swift in Sources */,
4E7940DE29FC307C00A9AEA1 /* P2PSubjectV.swift in Sources */,
4E6EF56C2B65A33300AF252A /* PaymentDone.swift in Sources */,
diff --git a/TalerWallet1/Views/Actions/Peer2peer/SendAmountV.swift b/TalerWallet1/Views/Actions/Peer2peer/SendAmountV.swift
@@ -18,19 +18,13 @@ struct SendAmountV: View {
@Binding var summary: String
@EnvironmentObject private var controller: Controller
+ @EnvironmentObject private var model: WalletModel
@State private var balanceIndex = 0
@State private var balance: Balance? = nil // nil only when balances == []
@State private var currencyInfo: CurrencyInfo = CurrencyInfo.zero(UNKNOWN)
-
- func navTitle(_ currency: String, _ condition: Bool = false) -> String {
- condition ? String(localized: "NavTitle_Send_Currency",
- defaultValue: "Send \(currency)",
- comment: "NavTitle: Send 'currency'")
- : String(localized: "NavTitle_Send",
- defaultValue: "Send",
- comment: "NavTitle: Send")
- }
+ @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING) // Update currency when used
+ @State private var amountAvailable = Amount.zero(currency: EMPTYSTRING) // GetMaxPeerPushAmount
private func viewDidLoad() async {
if let selectedBalance {
@@ -54,16 +48,16 @@ struct SendAmountV: View {
var body: some View {
#if PRINT_CHANGES
let _ = Self._printChanges()
+ let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear
#endif
let currencySymbol = currencyInfo.symbol
- let navA11y = navTitle(currencyInfo.name)
- let navTitle = navTitle(currencySymbol, currencyInfo.hasSymbol)
+ let navA11y = SendAmountView.navTitle(currencyInfo.name) // always include currency for a11y
+ let navTitle = SendAmountView.navTitle(currencySymbol, currencyInfo.hasSymbol)
let count = controller.balances.count
let _ = symLog.log("count = \(count)")
let scrollView = ScrollView {
if count > 0 {
- ScopePicker(value: $balanceIndex,
- onlyNonZero: true)
+ ScopePicker(value: $balanceIndex, onlyNonZero: true) // can only send what exists
{ index in
balanceIndex = index
balance = controller.balances[index]
@@ -71,24 +65,43 @@ struct SendAmountV: View {
.padding(.horizontal)
.padding(.bottom, 4)
}
- SendAmountContent(stack: stack.push(),
- currencyInfo: $currencyInfo,
- balance: $balance,
- balanceIndex: $balanceIndex,
- amountLastUsed: $amountLastUsed,
- summary: $summary)
+ if let balance {
+ SendAmountView(stack: stack.push(),
+ balance: balance,
+ amountLastUsed: $amountLastUsed,
+ amountToTransfer: $amountToTransfer,
+ amountAvailable: $amountAvailable,
+ summary: $summary)
+ } else { // no balance - Yikes
+ Text("No balance. There seems to be a problem with the database...")
+ }
} // ScrollView
.navigationTitle(navTitle)
.frame(maxWidth: .infinity, alignment: .leading)
.background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
- .onAppear {
- DebugViewC.shared.setViewID(VIEW_P2P_SEND, stack: stack.push())
- symLog.log("❗️ \(navTitle) onAppear")
- }
- .onDisappear {
- symLog.log("❗️ \(navTitle) onDisappear")
- }
+// .onAppear {
+// DebugViewC.shared.setViewID(VIEW_P2P_SEND, stack: stack.push())
+// symLog.log("❗️ \(navTitle) onAppear")
+// }
+// .onDisappear {
+// symLog.log("❗️ \(navTitle) onDisappear")
+// }
.task { await viewDidLoad() }
+ .task(id: balanceIndex + (1000 * controller.currencyTicker)) {
+ // runs whenever the user changes the exchange via ScopePicker, or on new currencyInfo
+ symLog.log("❗️ task \(balanceIndex)")
+ if let balance {
+ let scope = balance.scopeInfo
+ amountToTransfer.setCurrency(scope.currency)
+ currencyInfo = controller.info(for: scope, controller.currencyTicker)
+ do {
+ amountAvailable = try await model.getMaxPeerPushDebitAmountM(scope)
+ } catch {
+ // TODO: Error
+ amountAvailable = balance.available
+ }
+ }
+ }
if #available(iOS 16.0, *) {
if #available(iOS 16.4, *) {
@@ -103,221 +116,6 @@ struct SendAmountV: View {
}
}
// MARK: -
-struct SendAmountContent: View {
- private let symLog = SymLogV()
- let stack: CallStack
- @Binding var currencyInfo: CurrencyInfo
- @Binding var balance: Balance?
- @Binding var balanceIndex: Int
- @Binding var amountLastUsed: Amount
- @Binding var summary: String
-
- @EnvironmentObject private var controller: Controller
- @EnvironmentObject private var model: WalletModel
- @AppStorage("minimalistic") var minimalistic: Bool = false
-
- @State var peerPushCheck: CheckPeerPushDebitResponse? = nil
- @State private var expireDays = SEVENDAYS
- @State private var insufficient = false
-// @State private var feeAmount: Amount? = nil
- @State private var feeStr: String = EMPTYSTRING
- @State private var buttonSelected = false
- @State private var shortcutSelected = false
- @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING) // Update currency when used
- @State private var amountShortcut = Amount.zero(currency: EMPTYSTRING) // Update currency when used
- @State private var amountAvailable = Amount.zero(currency: EMPTYSTRING) // GetMaxPeerPushAmount
- @State private var exchange: Exchange? = nil // wg. noFees
-
- @State private var scopeInfo: ScopeInfo = ScopeInfo.zero()
-
- private func shortcutAction(_ shortcut: Amount) {
- amountShortcut = shortcut
- shortcutSelected = true
- }
- private func buttonAction() { buttonSelected = true }
-
- private func feeLabel(_ feeString: String) -> String {
- feeString.count > 0 ? String(localized: "+ \(feeString) fee")
- : EMPTYSTRING
- }
-
- private func fee(raw: Amount, effective: Amount) -> Amount? {
- do { // Outgoing: fee = effective - raw
- let fee = try effective - raw
- return fee
- } catch {}
- return nil
- }
-
- private func feeIsNotZero() -> Bool? {
- if let hasNoFees = exchange?.noFees {
- if hasNoFees {
- return nil // this exchange never has fees
- }
- }
- return peerPushCheck == nil ? false
- : true // TODO: !(feeAmount?.isZero ?? false)
- }
-
- private func computeFeeSend(_ amount: Amount) async -> ComputeFeeResult? {
- if amount.isZero {
- return ComputeFeeResult.zero()
- }
- let insufficient = (try? amount > amountAvailable) ?? true
- if insufficient {
- return ComputeFeeResult.insufficient()
- }
- do {
- let ppCheck = try await model.checkPeerPushDebitM(amount, scope: scopeInfo, viewHandles: true)
- let raw = ppCheck.amountRaw
- let effective = ppCheck.amountEffective
- if let fee = fee(raw: raw, effective: effective) {
- feeStr = fee.formatted(currencyInfo, isNegative: false)
- symLog.log("Fee = \(feeStr)")
- let insufficient = (try? effective > amountAvailable) ?? true
-
- peerPushCheck = ppCheck
- let feeLabel = feeLabel(feeStr)
-// announce("\(amountVoiceOver), \(feeLabel)")
- return ComputeFeeResult(insufficient: insufficient,
- feeAmount: fee,
- feeStr: feeLabel,
- numCoins: nil)
- } else {
- peerPushCheck = nil
- }
- } catch {
- // handle cancel, errors
- symLog.log("❗️ \(error), \(error.localizedDescription)")
- switch error {
- case let walletError as WalletBackendError:
- switch walletError {
- case .walletCoreError(let wError):
- if wError?.code == 7027 {
- return ComputeFeeResult.insufficient()
- }
- default: break
- }
- default: break
- }
- }
- return nil
- }
-
- var body: some View {
-#if PRINT_CHANGES
- let _ = Self._printChanges()
- let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear
-#endif
- Group {
- if let balance {
- let scopeInfo = balance.scopeInfo
- let availableStr = amountAvailable.formatted(currencyInfo, isNegative: false)
-// let availableA11y = amountAvailable.formatted(currencyInfo, isNegative: false, useISO: true, a11y: ".")
-// let amountVoiceOver = amountToTransfer.formatted(currencyInfo, isNegative: false)
-// let insufficientLabel2 = String(localized: "but you only have \(availableStr) to send.")
-
- let inputDestination = P2PSubjectV(stack: stack.push(),
- scope: scopeInfo,
- feeLabel: feeLabel(feeStr),
- feeIsNotZero: feeIsNotZero(),
- outgoing: true,
- amountToTransfer: $amountToTransfer, // from the textedit
- summary: $summary,
- expireDays: $expireDays)
- let shortcutDestination = P2PSubjectV(stack: stack.push(),
- scope: scopeInfo,
- feeLabel: nil,
- feeIsNotZero: feeIsNotZero(),
- outgoing: true,
- amountToTransfer: $amountShortcut, // from the tapped shortcut button
- summary: $summary,
- expireDays: $expireDays)
- AmountInputV(stack: stack.push(),
- scope: scopeInfo,
- amountAvailable: $amountAvailable,
- amountLabel: nil, // will use "Available: xxx", trailing
- amountToTransfer: $amountToTransfer,
- amountLastUsed: amountLastUsed,
- wireFee: nil,
- summary: $summary,
- shortcutAction: shortcutAction,
- buttonAction: buttonAction,
- feeIsNegative: false,
- computeFee: computeFeeSend)
- .background(NavigationLink(destination: shortcutDestination, isActive: $shortcutSelected)
- { EmptyView() }.frame(width: 0).opacity(0).hidden()
- ) // shortcutDestination
- .background(NavigationLink(destination: inputDestination, isActive: $buttonSelected)
- { EmptyView() }.frame(width: 0).opacity(0).hidden()
- ) // inputDestination
- } else { // no balance - Yikes
- Text("No balance. There seems to be a problem with the database...")
- }
- } // Group
- .task { // getMaxPeerPushDebit on first appearance
- if scopeInfo.type != .madeUp {
- do {
- symLog.log("❗️ task \(scopeInfo.currency)")
- amountAvailable = try await model.getMaxPeerPushDebitAmountM(scopeInfo, viewHandles: true)
- return
- } catch {}
- }
- amountAvailable = Amount.zero(currency: amountToTransfer.currencyStr)
- }
- .task(id: balanceIndex + (1000 * controller.currencyTicker)) {
- // runs whenever the user changes the exchange via ScopePicker, or on new currencyInfo
- symLog.log("❗️ task \(balanceIndex)")
- if let balance {
- scopeInfo = balance.scopeInfo
- amountToTransfer.setCurrency(scopeInfo.currency)
- currencyInfo = controller.info(for: scopeInfo, controller.currencyTicker)
- do {
- amountAvailable = try await model.getMaxPeerPushDebitAmountM(scopeInfo)
- } catch {
- // TODO: Error
- amountAvailable = balance.available
- }
- }
- }
-// .task(id: amountToTransfer.value) {
-// if exchange == nil {
-// if let url = scopeInfo.url {
-// exchange = try? await model.getExchangeByUrl(url: url)
-// }
-// }
-// do {
-// insufficient = try amountToTransfer > amountAvailable
-// } catch {
-// print("Yikes❗️ insufficient failed❗️")
-// insufficient = true
-// }
-//
-// if insufficient {
-// announce("\(amountVoiceOver), \(insufficientLabel2)")
-// } else if amountToTransfer.isZero {
-// feeStr = EMPTYSTRING
-// } else {
-// if let ppCheck = try? await model.checkPeerPushDebitM(amountToTransfer) {
-// // TODO: set from exchange
-//// agePicker.setAges(ages: peerPushCheck?.ageRestrictionOptions)
-// if let feeAmount = fee(ppCheck: ppCheck) {
-// feeStr = feeAmount.formatted(currencyInfo, isNegative: false)
-// let feeLabel = feeLabel(feeStr)
-// announce("\(amountVoiceOver), \(feeLabel)")
-// } else {
-// feeStr = EMPTYSTRING
-// announce(amountVoiceOver)
-// }
-// peerPushCheck = ppCheck
-// } else {
-// peerPushCheck = nil
-// }
-// }
-// }
- } // body
-}
-// MARK: -
#if DEBUG
fileprivate struct Preview_Content: View {
@State private var amountToPreview = Amount(currency: DEMOCURRENCY, cent: 510)
diff --git a/TalerWallet1/Views/Actions/Peer2peer/SendAmountView.swift b/TalerWallet1/Views/Actions/Peer2peer/SendAmountView.swift
@@ -0,0 +1,226 @@
+/*
+ * This file is part of GNU Taler, ©2022-24 Taler Systems S.A.
+ * See LICENSE.md
+ */
+/**
+ * @author Marc Stibane
+ */
+import SwiftUI
+import taler_swift
+import SymLog
+
+// Called when tapping [Send]
+struct SendAmountView: View {
+ private let symLog = SymLogV(0)
+ let stack: CallStack
+ let balance: Balance
+ @Binding var amountLastUsed: Amount
+ @Binding var amountToTransfer: Amount
+ @Binding var amountAvailable: Amount
+ @Binding var summary: String
+
+ @EnvironmentObject private var controller: Controller
+ @EnvironmentObject private var model: WalletModel
+ @AppStorage("minimalistic") var minimalistic: Bool = false
+
+ @State var peerPushCheck: CheckPeerPushDebitResponse? = nil
+ @State private var expireDays = SEVENDAYS
+ @State private var insufficient = false
+// @State private var feeAmount: Amount? = nil
+ @State private var feeStr: String = EMPTYSTRING
+ @State private var buttonSelected = false
+ @State private var shortcutSelected = false
+ @State private var amountShortcut = Amount.zero(currency: EMPTYSTRING) // Update currency when used
+ @State private var exchange: Exchange? = nil // wg. noFees
+
+ @State private var scopeInfo: ScopeInfo? = nil
+
+ private func shortcutAction(_ shortcut: Amount) {
+ amountShortcut = shortcut
+ shortcutSelected = true
+ }
+ private func buttonAction() { buttonSelected = true }
+
+ public static func navTitle(_ currency: String, _ condition: Bool = false) -> String {
+ condition ? String(localized: "NavTitle_Send_Currency",
+ defaultValue: "Send \(currency)",
+ comment: "NavTitle: Send 'currency'")
+ : String(localized: "NavTitle_Send",
+ defaultValue: "Send",
+ comment: "NavTitle: Send")
+ }
+
+ private func feeLabel(_ feeString: String) -> String {
+ feeString.count > 0 ? String(localized: "+ \(feeString) fee")
+ : EMPTYSTRING
+ }
+
+ private func fee(raw: Amount, effective: Amount) -> Amount? {
+ do { // Outgoing: fee = effective - raw
+ let fee = try effective - raw
+ return fee
+ } catch {}
+ return nil
+ }
+
+ private func feeIsNotZero() -> Bool? {
+ if let hasNoFees = exchange?.noFees {
+ if hasNoFees {
+ return nil // this exchange never has fees
+ }
+ }
+ return peerPushCheck == nil ? false
+ : true // TODO: !(feeAmount?.isZero ?? false)
+ }
+
+ private func computeFee(_ amount: Amount) async -> ComputeFeeResult? {
+ if amount.isZero {
+ return ComputeFeeResult.zero()
+ }
+ let insufficient = (try? amount > amountAvailable) ?? true
+ if insufficient {
+ return ComputeFeeResult.insufficient()
+ }
+ if let scopeInfo {
+ do {
+ let ppCheck = try await model.checkPeerPushDebitM(amount, scope: scopeInfo, viewHandles: true)
+ let raw = ppCheck.amountRaw
+ let effective = ppCheck.amountEffective
+ if let fee = fee(raw: raw, effective: effective) {
+ feeStr = fee.formatted(scopeInfo, isNegative: false)
+ symLog.log("Fee = \(feeStr)")
+ let insufficient = (try? effective > amountAvailable) ?? true
+
+ peerPushCheck = ppCheck
+ let feeLabel = feeLabel(feeStr)
+// announce("\(amountVoiceOver), \(feeLabel)")
+ return ComputeFeeResult(insufficient: insufficient,
+ feeAmount: fee,
+ feeStr: feeLabel,
+ numCoins: nil)
+ } else {
+ peerPushCheck = nil
+ }
+ } catch {
+ // handle cancel, errors
+ symLog.log("❗️ \(error), \(error.localizedDescription)")
+ switch error {
+ case let walletError as WalletBackendError:
+ switch walletError {
+ case .walletCoreError(let wError):
+ if wError?.code == 7027 {
+ return ComputeFeeResult.insufficient()
+ }
+ default: break
+ }
+ default: break
+ }
+ }
+ }
+ return nil
+ }
+
+ var body: some View {
+#if PRINT_CHANGES
+ let _ = Self._printChanges()
+ let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear
+#endif
+ Group {
+ if scopeInfo != nil {
+ let availableStr = amountAvailable.formatted(scopeInfo, isNegative: false)
+// let availableA11y = amountAvailable.formatted(currencyInfo, isNegative: false, useISO: true, a11y: ".")
+// let amountVoiceOver = amountToTransfer.formatted(currencyInfo, isNegative: false)
+// let insufficientLabel2 = String(localized: "but you only have \(availableStr) to send.")
+
+ let inputDestination = P2PSubjectV(stack: stack.push(),
+ scope: scopeInfo!,
+ feeLabel: feeLabel(feeStr),
+ feeIsNotZero: feeIsNotZero(),
+ outgoing: true,
+ amountToTransfer: $amountToTransfer, // from the textedit
+ summary: $summary,
+ expireDays: $expireDays)
+ let shortcutDestination = P2PSubjectV(stack: stack.push(),
+ scope: scopeInfo!,
+ feeLabel: nil,
+ feeIsNotZero: feeIsNotZero(),
+ outgoing: true,
+ amountToTransfer: $amountShortcut, // from the tapped shortcut button
+ summary: $summary,
+ expireDays: $expireDays)
+ let actions = Group {
+ NavLink($buttonSelected) { inputDestination }
+ NavLink($shortcutSelected) { shortcutDestination }
+ }
+ AmountInputV(stack: stack.push(),
+ scope: scopeInfo,
+ amountAvailable: $amountAvailable,
+ amountLabel: nil, // will use "Available: xxx", trailing
+ amountToTransfer: $amountToTransfer,
+ amountLastUsed: amountLastUsed,
+ wireFee: nil,
+ summary: $summary,
+ shortcutAction: shortcutAction,
+ buttonAction: buttonAction,
+ feeIsNegative: false,
+ computeFee: computeFee)
+ .background(actions)
+ } else { // must have $some view, otherwise .task will NOT run
+ Text(" ")
+ }
+ } // Group
+ .onAppear {
+ DebugViewC.shared.setViewID(VIEW_P2P_SEND, stack: stack.push())
+ symLog.log("❗️ onAppear")
+ }
+ .onDisappear {
+ symLog.log("❗️ onDisappear")
+ }
+ .task(id: balance) { // getMaxPeerPushDebit on first appearance
+ let scope = balance.scopeInfo
+ symLog.log("❗️ task \(scope.currency)")
+
+ if let available = try? await model.getMaxPeerPushDebitAmountM(scope, viewHandles: true) {
+ amountAvailable = available
+ } else {
+ amountAvailable = Amount.zero(currency: scope.currency)
+ }
+ scopeInfo = scope
+ }
+// .task(id: amountToTransfer.value) {
+// if exchange == nil {
+// if let url = scopeInfo.url {
+// exchange = try? await model.getExchangeByUrl(url: url)
+// }
+// }
+// do {
+// insufficient = try amountToTransfer > amountAvailable
+// } catch {
+// print("Yikes❗️ insufficient failed❗️")
+// insufficient = true
+// }
+//
+// if insufficient {
+// announce("\(amountVoiceOver), \(insufficientLabel2)")
+// } else if amountToTransfer.isZero {
+// feeStr = EMPTYSTRING
+// } else {
+// if let ppCheck = try? await model.checkPeerPushDebitM(amountToTransfer) {
+// // TODO: set from exchange
+//// agePicker.setAges(ages: peerPushCheck?.ageRestrictionOptions)
+// if let feeAmount = fee(ppCheck: ppCheck) {
+// feeStr = feeAmount.formatted(currencyInfo, isNegative: false)
+// let feeLabel = feeLabel(feeStr)
+// announce("\(amountVoiceOver), \(feeLabel)")
+// } else {
+// feeStr = EMPTYSTRING
+// announce(amountVoiceOver)
+// }
+// peerPushCheck = ppCheck
+// } else {
+// peerPushCheck = nil
+// }
+// }
+// }
+ } // body
+}