taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

commit 4f50ba3a4a431cafe51d2ca091ccc866f7444e6b
parent b974a783707ac51303598e6c34253b1e04f55e16
Author: Marc Stibane <marc@taler.net>
Date:   Fri, 13 Sep 2024 09:11:00 +0200

cleanup project

Diffstat:
MTalerWallet.xcodeproj/project.pbxproj | 2+-
RTalerWallet1/Views/Peer2peer/P2PReadyV.swift -> TalerWallet1/Views/Actions/Peer2peer/P2PReadyV.swift | 0
ATalerWallet1/Views/Actions/Peer2peer/P2PSubjectV.swift | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ATalerWallet1/Views/Actions/Peer2peer/RequestPayment.swift | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ATalerWallet1/Views/Actions/Peer2peer/SendAmount.swift | 313+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DTalerWallet1/Views/Peer2peer/P2PSubjectV.swift | 167-------------------------------------------------------------------------------
DTalerWallet1/Views/Peer2peer/RequestPayment.swift | 197-------------------------------------------------------------------------------
DTalerWallet1/Views/Peer2peer/SendAmount.swift | 293-------------------------------------------------------------------------------
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