commit 4f50ba3a4a431cafe51d2ca091ccc866f7444e6b
parent b974a783707ac51303598e6c34253b1e04f55e16
Author: Marc Stibane <marc@taler.net>
Date: Fri, 13 Sep 2024 09:11:00 +0200
cleanup project
Diffstat:
8 files changed, 682 insertions(+), 658 deletions(-)
diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj
@@ -748,7 +748,6 @@
4EB0952E2989CBFE0043A8A1 /* Transactions */,
4EB095272989CBFE0043A8A1 /* Banking */,
4EB095242989CBFE0043A8A1 /* Settings */,
- 4ECB627E2A0BA4DA004ABBB7 /* Peer2peer */,
4EEC157129F7188B00D46A03 /* Sheets */,
4EB095462989CBFE0043A8A1 /* HelperViews */,
);
@@ -889,6 +888,7 @@
children = (
4E2B337C2C8B1D5500186A3E /* ActionsSheet.swift */,
4EB095362989CBFE0043A8A1 /* SendRequestV.swift */,
+ 4ECB627E2A0BA4DA004ABBB7 /* Peer2peer */,
4EA076B22BC0607400685A10 /* DepositWithdrawV.swift */,
);
path = Actions;
diff --git a/TalerWallet1/Views/Peer2peer/P2PReadyV.swift b/TalerWallet1/Views/Actions/Peer2peer/P2PReadyV.swift
diff --git a/TalerWallet1/Views/Actions/Peer2peer/P2PSubjectV.swift b/TalerWallet1/Views/Actions/Peer2peer/P2PSubjectV.swift
@@ -0,0 +1,170 @@
+/*
+ * 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
+
+func p2pFee(ppCheck: CheckPeerPushDebitResponse) -> Amount? {
+ do {
+ // Outgoing: fee = effective - raw
+ let fee = try ppCheck.amountEffective - ppCheck.amountRaw
+ return fee
+ } catch {}
+ return nil
+}
+
+struct P2PSubjectV: View {
+ private let symLog = SymLogV(0)
+ let stack: CallStack
+ let scope: ScopeInfo
+ @Binding var currencyInfo: CurrencyInfo
+ let feeLabel: String?
+ let feeIsNotZero: Bool? // nil = no fees at all, false = no fee for this tx
+ let outgoing: Bool
+ @Binding var amountToTransfer: Amount
+ @Binding var summary: String
+ @Binding var expireDays: UInt
+
+ @EnvironmentObject private var model: WalletModel
+ @Environment(\.colorScheme) private var colorScheme
+ @Environment(\.colorSchemeContrast) private var colorSchemeContrast
+ @AppStorage("minimalistic") var minimalistic: Bool = false
+
+ @State private var myFeeLabel: String = EMPTYSTRING
+ @State private var transactionStarted: Bool = false
+ @FocusState private var isFocused: Bool
+
+ private func buttonTitle(_ amount: Amount, _ currencyInfo: CurrencyInfo) -> String {
+ let amountWithCurrency = amount.formatted(currencyInfo, isNegative: false, useISO: true)
+ return outgoing ? String(localized: "Send \(amountWithCurrency) now",
+ comment: "amount with currency")
+ : String(localized: "Request \(amountWithCurrency)",
+ comment: "amount with currency")
+ }
+
+ private func subjectTitle(_ amount: Amount, _ currencyInfo: CurrencyInfo) -> String {
+ let amountStr = amount.formatted(currencyInfo, isNegative: false)
+ return outgoing ? String(localized: "NavTitle_Send_AmountStr",
+ defaultValue: "Send \(amountStr)",
+ comment: "NavTitle: Send 'amountStr'")
+ : String(localized: "NavTitle_Request_AmountStr",
+ defaultValue: "Request \(amountStr)",
+ comment: "NavTitle: Request 'amountStr'")
+ }
+
+ var body: some View {
+#if PRINT_CHANGES
+ let _ = Self._printChanges()
+ let _ = symLog.vlog(amountToTransfer.readableDescription) // just to get the # to compare it with .onAppear & onDisappear
+#endif
+ ScrollView { VStack (alignment: .leading, spacing: 6) {
+ if let feeIsNotZero { // don't show fee if nil
+ let label = feeLabel ?? myFeeLabel
+ if label.count > 0 {
+ Text(label)
+ .frame(maxWidth: .infinity, alignment: .trailing)
+ .foregroundColor(feeIsNotZero ? .red : WalletColors().secondary(colorScheme, colorSchemeContrast))
+ .talerFont(.body)
+ }
+ }
+ if !minimalistic {
+ Text("Enter subject:") // Purpose
+ .talerFont(.title3)
+ .accessibilityAddTraits(.isHeader)
+ .accessibilityRemoveTraits(.isStaticText)
+ .padding(.top)
+ }
+ Group { if #available(iOS 16.0, *) {
+ TextField(minimalistic ? "Subject" : EMPTYSTRING, text: $summary, axis: .vertical)
+ .focused($isFocused)
+ .lineLimit(2...)
+ } else {
+ TextField("Subject", text: $summary)
+ .focused($isFocused)
+// .lineLimit(2...5) // lineLimit' is only available in iOS 16.0 or newer
+ } } // Group for iOS16+ & iOS15
+ .talerFont(.title2)
+ .foregroundColor(WalletColors().fieldForeground) // text color
+ .background(WalletColors().fieldBackground)
+ .textFieldStyle(.roundedBorder)
+ .onAppear {
+ if !UIAccessibility.isVoiceOverRunning {
+ symLog.log("dispatching kbd...")
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
+ isFocused = true // make first responder - raise keybord
+ symLog.log("...kbd isFocused")
+ }
+ }
+ }
+ Text(verbatim: "\(summary.count)/100") // maximum 100 characters
+ .frame(maxWidth: .infinity, alignment: .trailing)
+ .talerFont(.body)
+ .accessibilityValue(String(localized: "\(summary.count) characters of 100"))
+
+ // TODO: compute max Expiration day from peerPushCheck to disable 30 (and even 7)
+ SelectDays(selected: $expireDays, maxExpiration: THIRTYDAYS, outgoing: outgoing)
+ .disabled(false)
+ .padding(.bottom)
+
+ let disabled = (expireDays == 0) || (summary.count < 1) // TODO: check amountAvailable
+ NavigationLink(destination: LazyView {
+ P2PReadyV(stack: stack.push(),
+ currencyInfo: $currencyInfo,
+ scope: scope,
+ summary: summary,
+ expireDays: expireDays,
+ outgoing: outgoing,
+ amountToTransfer: amountToTransfer,
+ transactionStarted: $transactionStarted)
+ }) {
+ Text(buttonTitle(amountToTransfer, currencyInfo))
+ }
+ .buttonStyle(TalerButtonStyle(type: .prominent, disabled: disabled))
+ .disabled(disabled)
+ .accessibilityHint(disabled ? String(localized: "enabled when subject and expiration are set") : EMPTYSTRING)
+ }.padding(.horizontal) } // ScrollVStack
+// .scrollBounceBehavior(.basedOnSize) needs iOS 16.4
+ .navigationTitle(subjectTitle(amountToTransfer, currencyInfo))
+ .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
+ .onAppear {
+ DebugViewC.shared.setViewID(VIEW_P2P_SUBJECT, stack: stack.push())
+// print("❗️ P2PSubjectV onAppear")
+ }
+ .onDisappear {
+// print("❗️ P2PSubjectV onDisappear")
+ }
+ .task(id: amountToTransfer.value) {
+ if outgoing && feeLabel == nil {
+ if let ppCheck = try? await model.checkPeerPushDebitM(amountToTransfer, scope: scope) {
+ if let feeAmount = p2pFee(ppCheck: ppCheck) {
+ let feeStr = feeAmount.formatted(currencyInfo, isNegative: false)
+ myFeeLabel = String(localized: "+ \(feeStr) fee")
+ } else { myFeeLabel = EMPTYSTRING }
+ } else {
+ print("❗️ checkPeerPushDebitM failed")
+
+ }
+ }
+ }
+ }
+}
+// MARK: -
+#if DEBUG
+//struct SendPurpose_Previews: PreviewProvider {
+// static var previews: some View {
+// @State var summary: String = ""
+// @State var expireDays: UInt = 0
+// let amount = Amount(currency: LONGCURRENCY, integer: 10, fraction: 0)
+// SendPurpose(amountAvailable: amount,
+// amountToTransfer: 543,
+// fee: "0,43",
+// summary: $summary,
+// expireDays: $expireDays)
+// }
+//}
+#endif
diff --git a/TalerWallet1/Views/Actions/Peer2peer/RequestPayment.swift b/TalerWallet1/Views/Actions/Peer2peer/RequestPayment.swift
@@ -0,0 +1,198 @@
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
+import SwiftUI
+import taler_swift
+import SymLog
+
+// Called when tapping "Request Payment" in the balances list
+struct RequestPayment: View {
+ private let symLog = SymLogV(0)
+ let stack: CallStack
+ let balances: [Balance]
+ @Binding var selectedBalance: Balance?
+
+ @Binding var amountToTransfer: Amount
+ @Binding var summary: String
+ let scopeInfo: ScopeInfo
+ let cameraAction: () -> Void
+
+ @EnvironmentObject private var controller: Controller
+ @EnvironmentObject private var model: WalletModel
+ @AppStorage("minimalistic") var minimalistic: Bool = false
+
+ @State private var peerPullCheck: CheckPeerPullCreditResponse? = nil
+ @State private var expireDays: UInt = 0
+// @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 amountZero = Amount.zero(currency: EMPTYSTRING) // needed for isZero
+ @State private var exchange: Exchange? = nil // wg. noFees
+ @State private var currencyInfo: CurrencyInfo = CurrencyInfo.zero(UNKNOWN)
+ @State private var currencyName: String = UNKNOWN
+ @State private var currencySymbol: String = UNKNOWN
+
+ 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 { // Incoming: fee = raw - effective
+ let fee = try raw - effective
+ return fee
+ } catch {}
+ return nil
+ }
+
+ private func feeIsNotZero() -> Bool? {
+ if let hasNoFees = exchange?.noFees {
+ if hasNoFees {
+ return nil // this exchange never has fees
+ }
+ }
+ return peerPullCheck != nil ? true : false
+ }
+
+ private func computeFeeRequest(_ amount: Amount) async -> ComputeFeeResult? {
+ if exchange == nil {
+ if let url = scopeInfo.url {
+ exchange = try? await model.getExchangeByUrl(url: url)
+ }
+ }
+ if amount.isZero {
+ return ComputeFeeResult.zero()
+ }
+ do {
+ let baseURL = exchange?.exchangeBaseUrl
+ let ppCheck = try await model.checkPeerPullCreditM(baseURL, amount: amount)
+ let raw = ppCheck.amountRaw
+ let effective = ppCheck.amountEffective
+ if let fee = fee(raw: raw, effective: effective) {
+ feeStr = fee.formatted(currencyInfo, isNegative: true)
+ symLog.log("Fee = \(feeStr)")
+
+ peerPullCheck = ppCheck
+ return ComputeFeeResult(insufficient: false,
+ feeAmount: fee,
+ feeStr: feeLabel(feeStr),
+ numCoins: ppCheck.numCoins)
+ } else {
+ peerPullCheck = nil
+ }
+ } catch {
+ // handle cancel, errors
+ symLog.log("❗️ \(error)")
+ }
+ 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
+ let currency = amountToTransfer.currencyStr
+// let currencyInfo = controller.info(for: currency, controller.currencyTicker)
+ let currencySymbol = currencyInfo.altUnitSymbol ?? currency
+ let navTitle = String(localized: "NavTitle_Request_Currency",
+ defaultValue: "Request \(currencySymbol)",
+ comment: "NavTitle: Request 'currencySymbol'")
+ let coinData = CoinData(details: peerPullCheck)
+ let _ = symLog.log("currency: \(currency)")
+ let amountVoiceOver = amountToTransfer.formatted(currencyInfo, isNegative: false)
+
+ let inputDestination = LazyView {
+ P2PSubjectV(stack: stack.push(),
+ scope: scopeInfo,
+ currencyInfo: $currencyInfo,
+ feeLabel: coinData.feeLabel(currencyInfo,
+ feeZero: String(localized: "No payment fee"),
+ isNegative: false),
+ feeIsNotZero: feeIsNotZero(),
+ outgoing: false,
+ amountToTransfer: $amountToTransfer,
+ summary: $summary,
+ expireDays: $expireDays)
+ }
+ let shortcutDestination = LazyView {
+ P2PSubjectV(stack: stack.push(),
+ scope: scopeInfo,
+ currencyInfo: $currencyInfo,
+ feeLabel: nil,
+ feeIsNotZero: feeIsNotZero(),
+ outgoing: false,
+ amountToTransfer: $amountShortcut,
+ summary: $summary,
+ expireDays: $expireDays)
+ }
+
+ ScrollView {
+ let amountLabel = minimalistic ? String(localized: "Amount:")
+ : String(localized: "Amount to request:")
+ AmountInputV(stack: stack.push(),
+ currencyInfo: $currencyInfo,
+ amountAvailable: $amountZero,
+ amountLabel: amountLabel,
+ amountToTransfer: $amountToTransfer,
+ wireFee: nil,
+ summary: $summary,
+ shortcutAction: shortcutAction,
+ buttonAction: buttonAction,
+ feeIsNegative: true,
+ computeFee: computeFeeRequest)
+ .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
+ } // ScrollView
+ .frame(maxWidth: .infinity, alignment: .leading)
+// .scrollBounceBehavior(.basedOnSize) needs iOS 16.4
+ .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
+ .navigationTitle(navTitle)
+ .onAppear {
+ DebugViewC.shared.setViewID(VIEW_P2P_REQUEST, stack: stack.push())
+ // if we set >> controller.frontendState = 1 << here, then becomeFirstResponder won't work!
+ symLog.log("❗️ \(navTitle) onAppear")
+ }
+ .onDisappear {
+ symLog.log("❗️ \(navTitle) onDisappear")
+ }
+ .navigationBarItems(trailing: QRButton(action: cameraAction))
+// .task(id: amountToTransfer.value) {
+// if exchange == nil {
+// if let url = scopeInfo.url {
+// exchange = try? await model.getExchangeByUrl(url: url)
+// }
+// }
+// if amountToTransfer.isZero {
+// // fee = EMPTYSTRING
+// } else {
+// let baseURL = exchange?.exchangeBaseUrl
+// // peerPullCheck = try? await model.checkPeerPullCreditM(amountToTransfer, exchangeBaseUrl: nil)
+// peerPullCheck = try? await model.checkPeerPullCreditM(baseURL, amount: amountToTransfer)
+// }
+// }
+ }
+}
+// MARK: -
+#if DEBUG
+//struct ReceiveAmount_Previews: PreviewProvider {
+// static var scopeInfo = ScopeInfo(type: ScopeInfo.ScopeInfoType.exchange, exchangeBaseUrl: DEMOEXCHANGE, currency: LONGCURRENCY)
+// static var previews: some View {
+// let amount = Amount(currency: LONGCURRENCY, integer: 10, fraction: 0)
+// RequestPayment(exchangeBaseUrl: DEMOEXCHANGE, currency: LONGCURRENCY)
+// }
+//}
+#endif
diff --git a/TalerWallet1/Views/Actions/Peer2peer/SendAmount.swift b/TalerWallet1/Views/Actions/Peer2peer/SendAmount.swift
@@ -0,0 +1,313 @@
+/*
+ * 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 Coins" in the balances list
+struct SendAmount: View {
+ private let symLog = SymLogV(0)
+ let stack: CallStack
+ let balances: [Balance]
+ @Binding var selectedBalance: Balance?
+ @Binding var amountToTransfer: Amount
+ @Binding var summary: String
+ let scopeInfo: ScopeInfo
+ let cameraAction: () -> Void
+
+ // TODO: call getMaxPeerPushDebitAmountM
+
+ @EnvironmentObject private var controller: Controller
+ @EnvironmentObject private var model: WalletModel
+ @Environment(\.colorScheme) private var colorScheme
+ @Environment(\.colorSchemeContrast) private var colorSchemeContrast
+ @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 amountAvailable = Amount.zero(currency: EMPTYSTRING) // GetMaxPeerPushAmount
+ @State private var exchange: Exchange? = nil // wg. noFees
+
+ @State private var balanceIndex = 0
+ @State private var currencyInfo = CurrencyInfo.zero(UNKNOWN)
+ @State private var currencyName = UNKNOWN
+ @State private var currencySymbol = UNKNOWN
+
+ 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 true //PRINT_CHANGES
+ let _ = Self._printChanges()
+ let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear
+#endif
+ let nonZeroBalances = Balance.nonZeroBalances(balances)
+ let count = nonZeroBalances.count
+ let index = balanceIndex < count ? balanceIndex : 0
+ let balance = nonZeroBalances[index]
+ let scopeInfo = balance.scopeInfo
+ let currency = scopeInfo.currency
+ let navTitle = String(localized: "NavTitle_Send",
+ defaultValue: "Send",
+ comment: "NavTitle: Send")
+ let availableStr = amountAvailable.formatted(currencyInfo, isNegative: false)
+ let amountVoiceOver = amountToTransfer.formatted(currencyInfo, isNegative: false)
+ let insufficientLabel2 = String(localized: "but you only have \(availableStr) to send.")
+
+ let inputDestination = LazyView {
+ P2PSubjectV(stack: stack.push(),
+ scope: scopeInfo,
+ currencyInfo: $currencyInfo,
+ feeLabel: feeLabel(feeStr),
+ feeIsNotZero: feeIsNotZero(),
+ outgoing: true,
+ amountToTransfer: $amountToTransfer, // from the textedit
+ summary: $summary,
+ expireDays: $expireDays)
+ }
+ let shortcutDestination = LazyView {
+ P2PSubjectV(stack: stack.push(),
+ scope: scopeInfo,
+ currencyInfo: $currencyInfo,
+ feeLabel: nil,
+ feeIsNotZero: feeIsNotZero(),
+ outgoing: true,
+ amountToTransfer: $amountShortcut, // from the tapped shortcut button
+ summary: $summary,
+ expireDays: $expireDays)
+ }
+
+ let sendAmountView = ScrollView {
+ ScopePicker(stack: stack.push(), balances: nonZeroBalances, selected: $balanceIndex)
+
+ let amountLabel = minimalistic ? String(localized: "Amount:")
+ : String(localized: "Amount to send:")
+ AmountInputV(stack: stack.push(),
+ currencyInfo: $currencyInfo,
+ amountAvailable: $amountAvailable,
+ amountLabel: amountLabel,
+ amountToTransfer: $amountToTransfer,
+ 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
+ } // ScrollView
+ .frame(maxWidth: .infinity, alignment: .leading)
+// .scrollBounceBehavior(.basedOnSize) needs iOS 16.4
+ .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
+ .navigationTitle(navTitle)
+ .task {
+ do {
+ let amount = try await model.getMaxPeerPushDebitAmountM(amountToTransfer.currencyStr,
+ scope: scopeInfo, viewHandles: true)
+ amountAvailable = amount
+ } catch {
+ amountAvailable = Amount.zero(currency: amountToTransfer.currencyStr)
+ }
+ }
+ .onAppear {
+ DebugViewC.shared.setViewID(VIEW_P2P_SEND, stack: stack.push())
+ // if we set >> controller.frontendState = -1 << here, then becomeFirstResponder won't work!
+ symLog.log("❗️ \(navTitle) onAppear")
+ }
+ .onDisappear {
+ symLog.log("❗️ \(navTitle) onDisappear")
+ }
+ .navigationBarItems(trailing: QRButton(action: cameraAction))
+ .task(id: balanceIndex + (1000 * controller.currencyTicker)) {
+ let balance = nonZeroBalances[index]
+ let scopeInfo = balance.scopeInfo
+ let currency = scopeInfo.currency
+ amountAvailable = balance.available
+ amountToTransfer.setCurrency(currency)
+ currencyInfo = controller.info(for: currency, controller.currencyTicker)
+ currencyName = currencyInfo.scope.currency
+ currencySymbol = currencyInfo.altUnitSymbol ?? currencyInfo.specs.name
+ }
+// .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
+// }
+// }
+// }
+
+ if #available(iOS 16.0, *) {
+ sendAmountView.toolbar(.hidden, for: .tabBar)
+ } else {
+ sendAmountView
+ }
+
+ }
+}
+// MARK: -
+#if DEBUG
+fileprivate struct Preview_Content: View {
+ @State private var amountToPreview = Amount(currency: DEMOCURRENCY, cent: 510)
+ @State private var summary: String = ""
+ @State private var currencyInfoL: CurrencyInfo = CurrencyInfo.zero(DEMOCURRENCY)
+ @State private var selectedBalance: Balance? = nil
+
+ private func checkCameraAvailable() -> Void {
+ // Open Camera when QR-Button was tapped
+ }
+ var body: some View {
+ let amount = Amount(currency: DEMOCURRENCY, cent: 1000)
+ let pending = Amount(currency: DEMOCURRENCY, cent: 0)
+ let currencyInfo = CurrencyInfo.zero(DEMOCURRENCY)
+ let exchange2 = Exchange(exchangeBaseUrl: ARS_EXP_EXCHANGE,
+ masterPub: "masterPub",
+ scopeInfo: currencyInfo.scope,
+ paytoUris: [],
+ tosStatus: .proposed,
+ exchangeEntryStatus: .ephemeral,
+ exchangeUpdateStatus: .ready,
+ ageRestrictionOptions: [])
+ let scopeInfo = ScopeInfo(type: .exchange, url: DEMOEXCHANGE, currency: DEMOCURRENCY)
+ let balance = Balance(scopeInfo: scopeInfo,
+ available: amount,
+ pendingIncoming: pending,
+ pendingOutgoing: pending,
+ flags: [])
+ SendAmount(stack: CallStack("Preview"),
+ balances: [balance],
+ selectedBalance: $selectedBalance,
+ amountToTransfer: $amountToPreview,
+ summary: $summary,
+ scopeInfo: currencyInfo.scope,
+ cameraAction: checkCameraAvailable)
+ }
+}
+
+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/P2PSubjectV.swift b/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
@@ -1,167 +0,0 @@
-/*
- * 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
-
-func p2pFee(ppCheck: CheckPeerPushDebitResponse) -> Amount? {
- do {
- // Outgoing: fee = effective - raw
- let fee = try ppCheck.amountEffective - ppCheck.amountRaw
- return fee
- } catch {}
- return nil
-}
-
-struct P2PSubjectV: View {
- private let symLog = SymLogV(0)
- let stack: CallStack
- let scope: ScopeInfo
- @Binding var currencyInfo: CurrencyInfo
- let feeLabel: String?
- let feeIsNotZero: Bool? // nil = no fees at all, false = no fee for this tx
- let outgoing: Bool
- @Binding var amountToTransfer: Amount
- @Binding var summary: String
- @Binding var expireDays: UInt
-
- @EnvironmentObject private var model: WalletModel
- @Environment(\.colorScheme) private var colorScheme
- @Environment(\.colorSchemeContrast) private var colorSchemeContrast
- @AppStorage("minimalistic") var minimalistic: Bool = false
-
- @State private var myFeeLabel: String = EMPTYSTRING
- @State private var transactionStarted: Bool = false
- @FocusState private var isFocused: Bool
-
- private func buttonTitle(_ amount: Amount, _ currencyInfo: CurrencyInfo) -> String {
- let amountWithCurrency = amount.formatted(currencyInfo, isNegative: false, useISO: true)
- return outgoing ? String(localized: "Send \(amountWithCurrency) now",
- comment: "amount with currency")
- : String(localized: "Request \(amountWithCurrency)",
- comment: "amount with currency")
- }
-
- private func subjectTitle(_ amount: Amount, _ currencyInfo: CurrencyInfo) -> String {
- let amountStr = amount.formatted(currencyInfo, isNegative: false)
- return outgoing ? String(localized: "NavTitle_Send_AmountStr",
- defaultValue: "Send \(amountStr)",
- comment: "NavTitle: Send 'amountStr'")
- : String(localized: "NavTitle_Request_AmountStr",
- defaultValue: "Request \(amountStr)",
- comment: "NavTitle: Request 'amountStr'")
- }
-
- var body: some View {
-#if PRINT_CHANGES
- let _ = Self._printChanges()
- let _ = symLog.vlog(amountToTransfer.readableDescription) // just to get the # to compare it with .onAppear & onDisappear
-#endif
- ScrollView { VStack (alignment: .leading, spacing: 6) {
- if let feeIsNotZero { // don't show fee if nil
- let label = feeLabel ?? myFeeLabel
- if label.count > 0 {
- Text(label)
- .frame(maxWidth: .infinity, alignment: .trailing)
- .foregroundColor(feeIsNotZero ? .red : WalletColors().secondary(colorScheme, colorSchemeContrast))
- .talerFont(.body)
- }
- }
- if !minimalistic {
- Text("Enter subject:") // Purpose
- .talerFont(.title3)
- .accessibilityAddTraits(.isHeader)
- .accessibilityRemoveTraits(.isStaticText)
- .padding(.top)
- }
- Group { if #available(iOS 16.0, *) {
- TextField(minimalistic ? "Subject" : EMPTYSTRING, text: $summary, axis: .vertical)
- .focused($isFocused)
- .lineLimit(2...)
- } else {
- TextField("Subject", text: $summary)
- .focused($isFocused)
-// .lineLimit(2...5) // lineLimit' is only available in iOS 16.0 or newer
- } } // Group for iOS16+ & iOS15
- .talerFont(.title2)
- .foregroundColor(WalletColors().fieldForeground) // text color
- .background(WalletColors().fieldBackground)
- .textFieldStyle(.roundedBorder)
- .onAppear {
- if !UIAccessibility.isVoiceOverRunning {
- symLog.log("dispatching kbd...")
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
- isFocused = true // make first responder - raise keybord
- symLog.log("...kbd isFocused")
- }
- }
- }
- Text(verbatim: "\(summary.count)/100") // maximum 100 characters
- .frame(maxWidth: .infinity, alignment: .trailing)
- .talerFont(.body)
- .accessibilityValue(String(localized: "\(summary.count) characters of 100"))
-
- // TODO: compute max Expiration day from peerPushCheck to disable 30 (and even 7)
- SelectDays(selected: $expireDays, maxExpiration: THIRTYDAYS, outgoing: outgoing)
- .disabled(false)
- .padding(.bottom)
-
- let disabled = (expireDays == 0) || (summary.count < 1) // TODO: check amountAvailable
- NavigationLink(destination: LazyView {
- P2PReadyV(stack: stack.push(),
- currencyInfo: $currencyInfo,
- scope: scope,
- summary: summary,
- expireDays: expireDays,
- outgoing: outgoing,
- amountToTransfer: amountToTransfer,
- transactionStarted: $transactionStarted)
- }) {
- Text(buttonTitle(amountToTransfer, currencyInfo))
- }
- .buttonStyle(TalerButtonStyle(type: .prominent, disabled: disabled))
- .disabled(disabled)
- .accessibilityHint(disabled ? String(localized: "enabled when subject and expiration are set") : EMPTYSTRING)
- }.padding(.horizontal) } // ScrollVStack
-// .scrollBounceBehavior(.basedOnSize) needs iOS 16.4
- .navigationTitle(subjectTitle(amountToTransfer, currencyInfo))
- .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
- .onAppear {
- DebugViewC.shared.setViewID(VIEW_P2P_SUBJECT, stack: stack.push())
-// print("❗️ P2PSubjectV onAppear")
- }
- .onDisappear {
-// print("❗️ P2PSubjectV onDisappear")
- }
- .task(id: amountToTransfer.value) {
- if outgoing && feeLabel == nil {
- if let ppCheck = try? await model.checkPeerPushDebitM(amountToTransfer, scope: scope) {
- if let feeAmount = p2pFee(ppCheck: ppCheck) {
- let feeStr = feeAmount.formatted(currencyInfo, isNegative: false)
- myFeeLabel = String(localized: "+ \(feeStr) fee")
- } else { myFeeLabel = EMPTYSTRING }
- }
- }
- }
- }
-}
-// MARK: -
-#if DEBUG
-//struct SendPurpose_Previews: PreviewProvider {
-// static var previews: some View {
-// @State var summary: String = ""
-// @State var expireDays: UInt = 0
-// let amount = Amount(currency: LONGCURRENCY, integer: 10, fraction: 0)
-// SendPurpose(amountAvailable: amount,
-// amountToTransfer: 543,
-// fee: "0,43",
-// summary: $summary,
-// expireDays: $expireDays)
-// }
-//}
-#endif
diff --git a/TalerWallet1/Views/Peer2peer/RequestPayment.swift b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
@@ -1,197 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
- * See LICENSE.md
- */
-import SwiftUI
-import taler_swift
-import SymLog
-
-// Called when tapping "Request Payment" in the balances list
-struct RequestPayment: View {
- private let symLog = SymLogV(0)
- let stack: CallStack
-// @Binding var currencyInfo: CurrencyInfo
-
- @Binding var amountToTransfer: Amount
- @Binding var summary: String
- let scopeInfo: ScopeInfo
- let cameraAction: () -> Void
-
- @EnvironmentObject private var controller: Controller
- @EnvironmentObject private var model: WalletModel
- @AppStorage("minimalistic") var minimalistic: Bool = false
-
- @State private var peerPullCheck: CheckPeerPullCreditResponse? = nil
- @State private var expireDays: UInt = 0
-// @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 amountZero = Amount.zero(currency: EMPTYSTRING) // needed for isZero
- @State private var exchange: Exchange? = nil // wg. noFees
- @State private var currencyInfo: CurrencyInfo = CurrencyInfo.zero(UNKNOWN)
- @State private var currencyName: String = UNKNOWN
- @State private var currencySymbol: String = UNKNOWN
-
- 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 { // Incoming: fee = raw - effective
- let fee = try raw - effective
- return fee
- } catch {}
- return nil
- }
-
- private func feeIsNotZero() -> Bool? {
- if let hasNoFees = exchange?.noFees {
- if hasNoFees {
- return nil // this exchange never has fees
- }
- }
- return peerPullCheck != nil ? true : false
- }
-
- private func computeFeeRequest(_ amount: Amount) async -> ComputeFeeResult? {
- if exchange == nil {
- if let url = scopeInfo.url {
- exchange = try? await model.getExchangeByUrl(url: url)
- }
- }
- if amount.isZero {
- return ComputeFeeResult.zero()
- }
- do {
- let baseURL = exchange?.exchangeBaseUrl
- let ppCheck = try await model.checkPeerPullCreditM(baseURL, amount: amount)
- let raw = ppCheck.amountRaw
- let effective = ppCheck.amountEffective
- if let fee = fee(raw: raw, effective: effective) {
- feeStr = fee.formatted(currencyInfo, isNegative: true)
- symLog.log("Fee = \(feeStr)")
-
- peerPullCheck = ppCheck
- return ComputeFeeResult(insufficient: false,
- feeAmount: fee,
- feeStr: feeLabel(feeStr),
- numCoins: ppCheck.numCoins)
- } else {
- peerPullCheck = nil
- }
- } catch {
- // handle cancel, errors
- symLog.log("❗️ \(error)")
- }
- 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
- let currency = amountToTransfer.currencyStr
-// let currencyInfo = controller.info(for: currency, controller.currencyTicker)
- let currencySymbol = currencyInfo.altUnitSymbol ?? currency
- let navTitle = String(localized: "NavTitle_Request_Currency",
- defaultValue: "Request \(currencySymbol)",
- comment: "NavTitle: Request 'currencySymbol'")
- let coinData = CoinData(details: peerPullCheck)
- let _ = symLog.log("currency: \(currency)")
- let amountVoiceOver = amountToTransfer.formatted(currencyInfo, isNegative: false)
-
- let inputDestination = LazyView {
- P2PSubjectV(stack: stack.push(),
- scope: scopeInfo,
- currencyInfo: $currencyInfo,
- feeLabel: coinData.feeLabel(currencyInfo,
- feeZero: String(localized: "No payment fee"),
- isNegative: false),
- feeIsNotZero: feeIsNotZero(),
- outgoing: false,
- amountToTransfer: $amountToTransfer,
- summary: $summary,
- expireDays: $expireDays)
- }
- let shortcutDestination = LazyView {
- P2PSubjectV(stack: stack.push(),
- scope: scopeInfo,
- currencyInfo: $currencyInfo,
- feeLabel: nil,
- feeIsNotZero: feeIsNotZero(),
- outgoing: false,
- amountToTransfer: $amountShortcut,
- summary: $summary,
- expireDays: $expireDays)
- }
-
- ScrollView {
- let amountLabel = minimalistic ? String(localized: "Amount:")
- : String(localized: "Amount to request:")
- AmountInputV(stack: stack.push(),
- currencyInfo: $currencyInfo,
- amountAvailable: $amountZero,
- amountLabel: amountLabel,
- amountToTransfer: $amountToTransfer,
- wireFee: nil,
- summary: $summary,
- shortcutAction: shortcutAction,
- buttonAction: buttonAction,
- feeIsNegative: true,
- computeFee: computeFeeRequest)
- .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
- } // ScrollView
- .frame(maxWidth: .infinity, alignment: .leading)
-// .scrollBounceBehavior(.basedOnSize) needs iOS 16.4
- .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
- .navigationTitle(navTitle)
- .onAppear {
- DebugViewC.shared.setViewID(VIEW_P2P_REQUEST, stack: stack.push())
- // if we set >> controller.frontendState = 1 << here, then becomeFirstResponder won't work!
- symLog.log("❗️ \(navTitle) onAppear")
- }
- .onDisappear {
- symLog.log("❗️ \(navTitle) onDisappear")
- }
- .navigationBarItems(trailing: QRButton(action: cameraAction))
-// .task(id: amountToTransfer.value) {
-// if exchange == nil {
-// if let url = scopeInfo.url {
-// exchange = try? await model.getExchangeByUrl(url: url)
-// }
-// }
-// if amountToTransfer.isZero {
-// // fee = EMPTYSTRING
-// } else {
-// let baseURL = exchange?.exchangeBaseUrl
-// // peerPullCheck = try? await model.checkPeerPullCreditM(amountToTransfer, exchangeBaseUrl: nil)
-// peerPullCheck = try? await model.checkPeerPullCreditM(baseURL, amount: amountToTransfer)
-// }
-// }
- }
-}
-// MARK: -
-#if DEBUG
-//struct ReceiveAmount_Previews: PreviewProvider {
-// static var scopeInfo = ScopeInfo(type: ScopeInfo.ScopeInfoType.exchange, exchangeBaseUrl: DEMOEXCHANGE, currency: LONGCURRENCY)
-// static var previews: some View {
-// let amount = Amount(currency: LONGCURRENCY, integer: 10, fraction: 0)
-// RequestPayment(exchangeBaseUrl: DEMOEXCHANGE, currency: LONGCURRENCY)
-// }
-//}
-#endif
diff --git a/TalerWallet1/Views/Peer2peer/SendAmount.swift b/TalerWallet1/Views/Peer2peer/SendAmount.swift
@@ -1,293 +0,0 @@
-/*
- * 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 Coins" in the balances list
-struct SendAmount: View {
- private let symLog = SymLogV(0)
- let stack: CallStack
- let balances: [Balance]
- let available: Amount?
- @Binding var amountToTransfer: Amount
- @Binding var summary: String
- let scopeInfo: ScopeInfo
- let cameraAction: () -> Void
-
- @EnvironmentObject private var controller: Controller
- @EnvironmentObject private var model: WalletModel
- @Environment(\.colorScheme) private var colorScheme
- @Environment(\.colorSchemeContrast) private var colorSchemeContrast
- @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 amountAvailable = Amount.zero(currency: EMPTYSTRING) // GetMaxPeerPushAmount
- @State private var exchange: Exchange? = nil // wg. noFees
-
- @State private var balanceIndex = 0
- @State private var currencyInfo = CurrencyInfo.zero(UNKNOWN)
- @State private var currencyName = UNKNOWN
- @State private var currencySymbol = UNKNOWN
-
- 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)")
- }
- return nil
- }
-
- var body: some View {
-#if true //PRINT_CHANGES
- let _ = Self._printChanges()
- let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear
-#endif
- let nonZeroBalances = Balance.nonZeroBalances(balances)
- let count = nonZeroBalances.count
- let index = balanceIndex < count ? balanceIndex : 0
- let balance = nonZeroBalances[index]
- let scopeInfo = balance.scopeInfo
- let currency = scopeInfo.currency
- let navTitle = String(localized: "NavTitle_Send",
- defaultValue: "Send",
- comment: "NavTitle: Send")
- let availableStr = amountAvailable.formatted(currencyInfo, isNegative: false)
- let amountVoiceOver = amountToTransfer.formatted(currencyInfo, isNegative: false)
- let insufficientLabel2 = String(localized: "but you only have \(availableStr) to send.")
-
- let inputDestination = LazyView {
- P2PSubjectV(stack: stack.push(),
- scope: scopeInfo,
- currencyInfo: $currencyInfo,
- feeLabel: feeLabel(feeStr),
- feeIsNotZero: feeIsNotZero(),
- outgoing: true,
- amountToTransfer: $amountToTransfer, // from the textedit
- summary: $summary,
- expireDays: $expireDays)
- }
- let shortcutDestination = LazyView {
- P2PSubjectV(stack: stack.push(),
- scope: scopeInfo,
- currencyInfo: $currencyInfo,
- feeLabel: nil,
- feeIsNotZero: feeIsNotZero(),
- outgoing: true,
- amountToTransfer: $amountShortcut, // from the tapped shortcut button
- summary: $summary,
- expireDays: $expireDays)
- }
-
- ScrollView {
- ScopePicker(stack: stack.push(), balances: nonZeroBalances, selected: $balanceIndex)
-
- let amountLabel = minimalistic ? String(localized: "Amount:")
- : String(localized: "Amount to send:")
- AmountInputV(stack: stack.push(),
- currencyInfo: $currencyInfo,
- amountAvailable: $amountAvailable,
- amountLabel: amountLabel,
- amountToTransfer: $amountToTransfer,
- 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
- } // ScrollView
- .frame(maxWidth: .infinity, alignment: .leading)
-// .scrollBounceBehavior(.basedOnSize) needs iOS 16.4
- .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
- .navigationTitle(navTitle)
- .task {
- do {
- let amount = try await model.getMaxPeerPushDebitAmountM(amountToTransfer.currencyStr,
- scope: scopeInfo, viewHandles: true)
- amountAvailable = amount
- } catch {
- amountAvailable = available ?? Amount.zero(currency: amountToTransfer.currencyStr)
- }
- }
- .onAppear {
- DebugViewC.shared.setViewID(VIEW_P2P_SEND, stack: stack.push())
- // if we set >> controller.frontendState = -1 << here, then becomeFirstResponder won't work!
- symLog.log("❗️ \(navTitle) onAppear")
- }
- .onDisappear {
- symLog.log("❗️ \(navTitle) onDisappear")
- }
- .navigationBarItems(trailing: QRButton(action: cameraAction))
- .task(id: balanceIndex + (10 * controller.currencyTicker)) {
- let balance = nonZeroBalances[index]
- let scopeInfo = balance.scopeInfo
- let currency = scopeInfo.currency
- amountAvailable = balance.available
- amountToTransfer.setCurrency(currency)
- currencyInfo = controller.info(for: currency, controller.currencyTicker)
- currencyName = currencyInfo.scope.currency
- currencySymbol = currencyInfo.altUnitSymbol ?? currencyInfo.specs.name
- }
-// .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
-// }
-// }
-// }
- }
-}
-// MARK: -
-#if DEBUG
-fileprivate struct Preview_Content: View {
- @State private var amountToPreview = Amount(currency: DEMOCURRENCY, cent: 510)
- @State private var summary: String = ""
- @State private var currencyInfoL: CurrencyInfo = CurrencyInfo.zero(DEMOCURRENCY)
-
- private func checkCameraAvailable() -> Void {
- // Open Camera when QR-Button was tapped
- }
- var body: some View {
- let amount = Amount(currency: DEMOCURRENCY, cent: 1000)
- let pending = Amount(currency: DEMOCURRENCY, cent: 0)
- let currencyInfo = CurrencyInfo.zero(DEMOCURRENCY)
- let exchange2 = Exchange(exchangeBaseUrl: ARS_EXP_EXCHANGE,
- masterPub: "masterPub",
- scopeInfo: currencyInfo.scope,
- paytoUris: [],
- tosStatus: .proposed,
- exchangeEntryStatus: .ephemeral,
- exchangeUpdateStatus: .ready,
- ageRestrictionOptions: [])
- let scopeInfo = ScopeInfo(type: .exchange, url: DEMOEXCHANGE, currency: DEMOCURRENCY)
- let balance = Balance(scopeInfo: scopeInfo,
- available: amount,
- pendingIncoming: pending,
- pendingOutgoing: pending,
- flags: [])
- SendAmount(stack: CallStack("Preview"),
- balances: [balance],
-// currencyInfo: $currencyInfoL,
- available: amount,
- amountToTransfer: $amountToPreview,
- summary: $summary,
- scopeInfo: currencyInfo.scope,
- cameraAction: checkCameraAvailable)
- }
-}
-
-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