commit c98215fcf19b591d3716a79d53b366d3d03d8d62
parent 73abd7b5382629702ddd4feb1038bb40ef2d9fa6
Author: Marc Stibane <marc@taler.net>
Date: Fri, 31 May 2024 09:08:07 +0200
WithdrawAcceptView - prepare for b-i-withdrawals without amount
Diffstat:
3 files changed, 165 insertions(+), 166 deletions(-)
diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj
@@ -259,6 +259,8 @@
4EE171922B49FE4E00BF9FF5 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = 4EE171912B49FE4E00BF9FF5 /* OrderedCollections */; };
4EE77E7D2C0280E5007C9064 /* GNU_Taler InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 4EE77E7C2C0280E5007C9064 /* GNU_Taler InfoPlist.xcstrings */; };
4EE77E7F2C0280E5007C9064 /* Taler_Wallet InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 4EE77E7E2C0280E5007C9064 /* Taler_Wallet InfoPlist.xcstrings */; };
+ 4EE77E812C06E513007C9064 /* WithdrawAcceptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE77E802C06E513007C9064 /* WithdrawAcceptView.swift */; };
+ 4EE77E822C06E513007C9064 /* WithdrawAcceptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE77E802C06E513007C9064 /* WithdrawAcceptView.swift */; };
4EEC118D2B83DE4800146CFF /* AmountInputV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEC118C2B83DE4700146CFF /* AmountInputV.swift */; };
4EEC118E2B83DE4800146CFF /* AmountInputV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEC118C2B83DE4700146CFF /* AmountInputV.swift */; };
4EEC11932B83FB7A00146CFF /* SubjectInputV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEC11922B83FB7A00146CFF /* SubjectInputV.swift */; };
@@ -452,6 +454,7 @@
4EDBDCD82AB787CB00925C02 /* CallStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallStack.swift; sourceTree = "<group>"; };
4EE77E7C2C0280E5007C9064 /* GNU_Taler InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = "GNU_Taler InfoPlist.xcstrings"; sourceTree = "<group>"; };
4EE77E7E2C0280E5007C9064 /* Taler_Wallet InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = "Taler_Wallet InfoPlist.xcstrings"; sourceTree = "<group>"; };
+ 4EE77E802C06E513007C9064 /* WithdrawAcceptView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WithdrawAcceptView.swift; sourceTree = "<group>"; };
4EEC118C2B83DE4700146CFF /* AmountInputV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmountInputV.swift; sourceTree = "<group>"; };
4EEC11922B83FB7A00146CFF /* SubjectInputV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectInputV.swift; sourceTree = "<group>"; };
4EEC11952B840F1100146CFF /* PayTemplateV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayTemplateV.swift; sourceTree = "<group>"; };
@@ -779,6 +782,7 @@
isa = PBXGroup;
children = (
4EB0953C2989CBFE0043A8A1 /* WithdrawURIView.swift */,
+ 4EE77E802C06E513007C9064 /* WithdrawAcceptView.swift */,
4E5A88F62A3B9E5B00072618 /* WithdrawAcceptDone.swift */,
4EB095402989CBFE0043A8A1 /* WithdrawTOSView.swift */,
);
@@ -1156,6 +1160,7 @@
4E3EAE342A990778009F1BE8 /* SuperScriptDigits.swift in Sources */,
4E3EAE352A990778009F1BE8 /* P2pPayURIView.swift in Sources */,
4E3EAE362A990778009F1BE8 /* Model+Payment.swift in Sources */,
+ 4EE77E812C06E513007C9064 /* WithdrawAcceptView.swift in Sources */,
4E3EAE372A990778009F1BE8 /* SettingsView.swift in Sources */,
4E3EAE382A990778009F1BE8 /* PaymentView.swift in Sources */,
4EEC11962B840F1100146CFF /* PayTemplateV.swift in Sources */,
@@ -1275,6 +1280,7 @@
4EBA563F2A7FD9390084948B /* SuperScriptDigits.swift in Sources */,
4E578E942A4822D500F21F1C /* P2pPayURIView.swift in Sources */,
4EB095542989CBFE0043A8A1 /* Model+Payment.swift in Sources */,
+ 4EE77E822C06E513007C9064 /* WithdrawAcceptView.swift in Sources */,
4EB0954F2989CBFE0043A8A1 /* SettingsView.swift in Sources */,
4EB095552989CBFE0043A8A1 /* PaymentView.swift in Sources */,
4EEC11972B840F1100146CFF /* PayTemplateV.swift in Sources */,
diff --git a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptView.swift b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptView.swift
@@ -1,111 +1,107 @@
/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * 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
struct WithdrawAcceptView: View {
- private let symLog = SymLogV(0)
- let navTitle = String(localized: "Accept Withdrawal")
+ private let symLog = SymLogV()
+ let stack: CallStack
+ let navTitle = String(localized: "Withdrawal")
- let exchangeBaseUrl: String
- let model: WithdrawModel?
- let amount: Amount?
+ // the URL from the bank website
let url: URL
+ @Binding var amountToTransfer: Amount
+ @Binding var exchange: Exchange?
- @State private var buttonSelected: Int? = nil
- @State private var confirmTransferUrl: String? = nil
- @State private var transactionId: String? = nil
- @State var manualWithdrawalDetails: ManualWithdrawalDetails?
+ @EnvironmentObject private var controller: Controller
+ @EnvironmentObject private var model: WalletModel
+ @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
- func acceptAction() -> () {
- Task {
- do {
- if let model {
- if let acceptWithdrawalResponse = try await model.sendAcceptIntWithdrawalM(exchangeBaseUrl, withdrawURL: url.absoluteString) {
- confirmTransferUrl = acceptWithdrawalResponse.confirmTransferUrl
- transactionId = acceptWithdrawalResponse.transactionId
- symLog.log(confirmTransferUrl ?? "❗️Yikes: No confirmTransferUrl")
- buttonSelected = 1 // trigger NavigationLink
- } else {
- // TODO: error sendAcceptIntWithdrawal failed
- }
- }
- } catch { // TODO: error
- symLog.log(error.localizedDescription)
- }
- }
- }
+ @State private var withdrawalAmountDetails: WithdrawalAmountDetails? = nil
var body: some View {
- VStack {
- if let manualWithdrawalDetails {
+ if let exchange, let withdrawalAmountDetails {
+ VStack {
+ let tosAccepted = exchange.tosStatus == .accepted
+ if !tosAccepted {
+ ToSButtonView(stack: stack.push(),
+ exchangeBaseUrl: exchange.exchangeBaseUrl,
+ viewID: SHEET_WITHDRAW_TOS,
+ p2p: false)
+ }
List {
+ let raw = withdrawalAmountDetails.amountRaw
+ let effective = withdrawalAmountDetails.amountEffective
+ let currency = raw.currencyStr
+ let currencyInfo = controller.info(for: currency, controller.currencyTicker)
+ let fee = try! Amount.diff(raw, effective)
+ let outColor = WalletColors().transactionColor(false)
+ let inColor = WalletColors().transactionColor(true)
- HStack(spacing: 0) {
- NavigationLink(destination: LazyView {
- WithdrawAcceptDone(model: model, confirmTransferUrl: confirmTransferUrl, transactionId: transactionId)
- }, tag: 1, selection: $buttonSelected
- ) { EmptyView() }.frame(width: 0).opacity(0).hidden()
-
- ThreeAmountsView(topTitle: String(localized: "Chosen amount to withdraw:"),
- topAmount: raw, fee: fee,
- bottomTitle: String(localized: "Coins to be withdrawn:"),
- bottomAmount: effective,
- large: false, pending: false, incoming: true,
- baseURL: exchangeBaseUrl)
- }
- }
- .safeAreaInset(edge: .bottom) {
- Button("Confirm Withdrawal", action: acceptAction)
- .lineLimit(2)
- .disabled(false)
- .buttonStyle(TalerButtonStyle(type: .prominent, narrow: false, aligned: .center))
- .padding()
+ ThreeAmountsV(stack: stack.push(),
+ topTitle: String(localized: "Chosen amount to withdraw:"),
+ topAbbrev: String(localized: "Chosen:", comment: "mini"),
+ topAmount: raw, fee: fee,
+ bottomTitle: String(localized: "Amount to be withdrawn:"),
+ bottomAbbrev: String(localized: "Effective:", comment: "mini"),
+ bottomAmount: effective,
+ large: false, pending: false, incoming: true,
+ baseURL: exchange.exchangeBaseUrl,
+ noFees: exchange.noFees,
+ txStateLcl: nil, // common.txState.major.localizedState
+ summary: nil,
+ merchant: nil)
+ let someCoins = SomeCoins(details: withdrawalAmountDetails)
+ QuiteSomeCoins(someCoins: someCoins,
+ shouldShowFee: true, // TODO: set to false if we never charge withdrawal fees
+ currency: currency,
+ currencyInfo: currencyInfo,
+ amountEffective: effective)
}
+ .listStyle(myListStyle.style).anyView
.navigationTitle(navTitle)
- } else {
- WithdrawProgressView(message: exchangeBaseUrl.trimURL())
- .navigationTitle("Found Exchange")
- }
- }
-// .overlay {
-// VStack {
-// ErrorView(errortext: "unknown state") // TODO: Error
-// }.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
-// .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
-// }
- .onAppear() {
- DebugViewC.shared.setSheetID(SHEET_WITHDRAW_ACCEPT)
- }
- .task { if let amount, let model {
- do { // TODO: cancelled
- symLog.log(".task")
- if exchangeBaseUrl.hasPrefix(HTTPS) {
- symLog.log("amount: \(amount), baseURL: \(String(describing: exchangeBaseUrl))")
- // TODO: let user choose exchange from list
- manualWithdrawalDetails = try await model.loadWithdrawalDetailsForAmountM(exchangeBaseUrl, amount: amount)
- symLog.log("raw: \(manualWithdrawalDetails!.amountRaw), effective: \(manualWithdrawalDetails!.amountEffective)")
- } else {
- // TODO: error no exchange!
+ if tosAccepted {
+ NavigationLink(destination: LazyView {
+ WithdrawAcceptDone(stack: stack.push(),
+ exchangeBaseUrl: exchange.exchangeBaseUrl,
+ url: url)
+ }) {
+ Text("Confirm Withdrawal") // SHEET_WITHDRAW_ACCEPT
+ }
+ .buttonStyle(TalerButtonStyle(type: .prominent))
+ .padding(.horizontal)
}
- } catch { // TODO: error
- symLog.log(error.localizedDescription)
}
- } else {
- // TODO: error no amount!
- } }
- }
-}
-// MARK: -
-struct WithdrawAcceptView_Previews: PreviewProvider {
- static var previews: some View {
- let amount = try! Amount(fromString: LONGCURRENCY + ":2.4")
- WithdrawAcceptView(exchangeBaseUrl: DEMOEXCHANGE,
- model: nil,
- amount: amount,
- url: URL(string: DEMOSHOP)!)
+ .onAppear() {
+ symLog.log("onAppear")
+ DebugViewC.shared.setSheetID(SHEET_WITHDRAW_ACCEPT)
+ }
+ } else { // no details or no exchange
+#if DEBUG
+ let message = url.host
+#else
+ let message: String? = nil
+#endif
+ LoadingView(scopeInfo: nil, message: message)
+ .task(id: exchange) {
+ symLog.log(".task")
+ if let exchange {
+ if let details = try? await model.getWithdrawalDetailsForAmountM(exchange.exchangeBaseUrl,
+ amount: amountToTransfer) {
+ withdrawalAmountDetails = details
+ }
+// agePicker.setAges(ages: details?.ageRestrictionOptions)
+ } else { // TODO: error
+ symLog.log("no exchangeBaseUrl or no exchange")
+ withdrawalAmountDetails = nil
+ }
+ }
+ }
}
}
diff --git a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
@@ -13,7 +13,7 @@ import SymLog
// We show the user the bank-integrated withdrawal details in a sheet - but first the ToS must be accepted.
// After the user confirmed the withdrawal, we show a button to return to the bank website to confirm there, too
struct WithdrawURIView: View {
- private let symLog = SymLogV(0)
+ private let symLog = SymLogV()
let stack: CallStack
let navTitle = String(localized: "Withdrawal")
@@ -24,94 +24,91 @@ struct WithdrawURIView: View {
@EnvironmentObject private var model: WalletModel
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
- @State private var withdrawalAmountDetails: WithdrawalAmountDetails? = nil
+ @State private var withdrawUriInfo: WithdrawUriInfoResponse? = nil
+ @State private var currencyName = EMPTYSTRING
+ @State private var amountIsEditable = false
+ @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING) // Update currency when used
+ @State private var amountShortcut = Amount.zero(currency: EMPTYSTRING) // Update currency when used
+ @State private var shortcutSelected = false
+
+ @State private var selectedExchange = EMPTYSTRING
@State private var exchange: Exchange? = nil
+ @State private var possibleExchanges: [Exchange] = []
+ @State private var defaultExchangeBaseUrl: String? // if nil then use possibleExchanges
- var body: some View {
- VStack {
- if let withdrawalAmountDetails, let exchange {
- let tosAccepted = exchange.tosStatus == .accepted
- if !tosAccepted {
- ToSButtonView(stack: stack.push(),
- exchangeBaseUrl: exchange.exchangeBaseUrl,
- viewID: SHEET_WITHDRAW_TOS,
- p2p: false)
- }
- List {
- let raw = withdrawalAmountDetails.amountRaw
- let effective = withdrawalAmountDetails.amountEffective
- let currency = raw.currencyStr
- let currencyInfo = controller.info(for: currency, controller.currencyTicker)
- let fee = try! Amount.diff(raw, effective)
- let outColor = WalletColors().transactionColor(false)
- let inColor = WalletColors().transactionColor(true)
+ func loadExchange(_ baseUrl: String) async { // TODO: throws?
+ if let someExchange = try? await model.getExchangeByUrl(url: baseUrl) {
+ exchange = someExchange
+ }
+ }
- ThreeAmountsV(stack: stack.push(),
- topTitle: String(localized: "Chosen amount to withdraw:"),
- topAbbrev: String(localized: "Chosen:", comment: "mini"),
- topAmount: raw, fee: fee,
- bottomTitle: String(localized: "Amount to be withdrawn:"),
- bottomAbbrev: String(localized: "Effective:", comment: "mini"),
- bottomAmount: effective,
- large: false, pending: false, incoming: true,
- baseURL: exchange.exchangeBaseUrl,
- noFees: exchange.noFees,
- txStateLcl: nil, // common.txState.major.localizedState
- summary: nil,
- merchant: nil)
- let someCoins = SomeCoins(details: withdrawalAmountDetails)
- QuiteSomeCoins(someCoins: someCoins,
- shouldShowFee: true, // TODO: set to false if we never charge withdrawal fees
- currency: currency,
- currencyInfo: currencyInfo,
- amountEffective: effective)
+ var body: some View {
+ if possibleExchanges.count > 0 {
+ if let defaultBaseUrl = defaultExchangeBaseUrl ?? possibleExchanges.first?.exchangeBaseUrl {
+ VStack {
+ let title = String(localized: "Exchange")
+ if possibleExchanges.count > 1 {
+ Picker(title, selection: $selectedExchange) {
+ ForEach(possibleExchanges, id: \.self) { exchange in
+ let baseUrl = exchange.exchangeBaseUrl
+ Text(baseUrl)
+ }
+ }
+ .talerFont(.title3)
+ .pickerStyle(.menu)
+ .onAppear() {
+ withAnimation { selectedExchange = defaultBaseUrl }
+ }
+ .onChange(of: selectedExchange) { selected in
+ Task {
+ await loadExchange(selected)
+ }
+ }
+ } else {
+ Text(defaultBaseUrl)
+ .task {
+ await loadExchange(defaultBaseUrl)
+ }
+ }
+ if amountToTransfer.isZero {
+ // TODO: input amount, then
+ WithdrawAcceptView(stack: stack.push(), url: url,
+ amountToTransfer: $amountToTransfer,
+ exchange: $exchange)
+ } else {
+ WithdrawAcceptView(stack: stack.push(), url: url,
+ amountToTransfer: $amountToTransfer,
+ exchange: $exchange)
+ }
}
- .listStyle(myListStyle.style).anyView
.navigationTitle(navTitle)
- if tosAccepted {
- NavigationLink(destination: LazyView {
- WithdrawAcceptDone(stack: stack.push(),
- exchangeBaseUrl: exchange.exchangeBaseUrl,
- url: url)
- }) {
- Text("Confirm Withdrawal") // SHEET_WITHDRAW_ACCEPT
- }
- .buttonStyle(TalerButtonStyle(type: .prominent))
- .padding(.horizontal)
+ .onAppear() {
+ symLog.log("onAppear")
+ DebugViewC.shared.setSheetID(SHEET_WITHDRAWAL)
}
- } else { // no details or no exchange
+ // agePicker.setAges(ages: details?.ageRestrictionOptions)
+ } else { // TODO: error
+// symLog.log("no exchangeBaseUrl or no exchange")
+ }
+
+
+ } else { // no details or no exchange
#if DEBUG
- let message = url.host
+ let message = url.host
#else
- let message: String? = nil
+ let message: String? = nil
#endif
- LoadingView(scopeInfo: nil, message: message)
- }
- }
- .onAppear() {
- symLog.log("onAppear")
- DebugViewC.shared.setSheetID(SHEET_WITHDRAWAL)
- }
- .task {
- symLog.log(".task")
- if let withdrawUriInfo = try? await model.getWithdrawalDetailsForUriM(url.absoluteString) {
- if let amount = withdrawUriInfo.amount {
- let baseUrl = withdrawUriInfo.defaultExchangeBaseUrl
- ?? withdrawUriInfo.possibleExchanges.first?.exchangeBaseUrl
- if let baseUrl {
- exchange = try? await model.getExchangeByUrl(url: baseUrl)
- if let details = try? await model.getWithdrawalDetailsForAmountM(baseUrl, amount: amount) {
- withdrawalAmountDetails = details
- }
- // agePicker.setAges(ages: details?.ageRestrictionOptions)
- } else { // TODO: error
- symLog.log("no exchangeBaseUrl or no exchange")
- withdrawalAmountDetails = nil
+ LoadingView(scopeInfo: nil, message: message)
+ .task {
+ symLog.log(".task")
+ if let someInfo = try? await model.getWithdrawalDetailsForUriM(url.absoluteString) {
+ defaultExchangeBaseUrl = someInfo.defaultExchangeBaseUrl
+ possibleExchanges = someInfo.possibleExchanges
+ amountToTransfer = someInfo.amount ?? Amount.zero(currency: someInfo.currency)
}
- } else {
+
// TODO: amount = nil ==> show amount input
}
- }
}
}
}