ManualDetailsV.swift (8260B)
1 /* 2 * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. 3 * See LICENSE.md 4 */ 5 /** 6 * @author Marc Stibane 7 */ 8 import SwiftUI 9 import OrderedCollections 10 import taler_swift 11 12 struct ManualDetailsV: View { 13 let stack: CallStack 14 let common: TransactionCommon 15 let details: WithdrawalDetails 16 17 @EnvironmentObject private var model: WalletModel 18 @AppStorage("minimalistic") var minimalistic: Bool = false 19 @State private var accountID = 0 20 @State private var listID = UUID() 21 @State private var qrCodeSpecs: [QrCodeSpec] = [] 22 23 func redraw(_ newAccount: Int) -> Void { 24 if newAccount != accountID { 25 accountID = newAccount 26 withAnimation { listID = UUID() } 27 } 28 } 29 func validDetails(_ details: [ExchangeAccountDetails]) -> [ExchangeAccountDetails] { 30 details.filter { detail in 31 detail.status.lowercased() == "ok" 32 } 33 } 34 35 @MainActor 36 private func viewDidLoad(_ paytoUri: String) async { 37 if let specs = try? await model.getQrCodesForPayto(paytoUri) { 38 qrCodeSpecs = specs 39 return 40 } 41 qrCodeSpecs = [] 42 } 43 44 var body: some View { 45 if let accountDetails = details.exchangeCreditAccountDetails { 46 let validDetails = validDetails(accountDetails) 47 if validDetails.count > 0 { 48 let account = validDetails[accountID] 49 if let amount = account.transferAmount { 50 let specs = account.currencySpecification 51 let amountStr = common.amountRaw.formatted(specs: specs, isNegative: false) 52 let amountValue = common.amountRaw.valueStr 53 let obtainStr = common.amountEffective.formatted(specs: specs, isNegative: false) 54 // let _ = print(amountStr, " | ", obtainStr) 55 if !minimalistic { 56 Text("The payment service is waiting for your wire-transfer.") 57 .bold() 58 .multilineTextAlignment(.leading) 59 .listRowSeparator(.hidden) 60 } 61 if validDetails.count > 1 { 62 if validDetails.count > 3 { // too many for SegmentControl 63 AccountPicker(title: String(localized: "Bank"), 64 value: $accountID, 65 accountDetails: validDetails, 66 action: redraw) 67 .listRowSeparator(.hidden) 68 .pickerStyle(.menu) 69 } else { 70 SegmentControl(value: $accountID, accountDetails: validDetails, action: redraw) 71 .listRowSeparator(.hidden) 72 } 73 } else if let amount = account.transferAmount { 74 if let bankName = account.bankLabel { 75 Text(bankName + ": " + amountStr.0) 76 .accessibilityLabel(bankName + ": " + amountStr.1) 77 // } else { 78 // Text(amountStr) 79 } 80 } 81 let payto = PayTo(account.paytoUri) 82 if let receiverStr = payto.receiver { 83 let wireDetails = ManualDetailsWireV(stack: stack.push(), 84 reservePub: details.reservePub, 85 receiverStr: receiverStr, 86 receiverZip: payto.postalCode, 87 receiverTown: payto.town, 88 iban: payto.iban, 89 xTaler: payto.xTaler ?? EMPTYSTRING, 90 amountValue: amountValue, 91 amountStr: amountStr, 92 obtainStr: obtainStr, 93 debitIBAN: nil, // only for deposit auth 94 account: account) 95 Group { 96 NavigationLink(destination: wireDetails) { 97 Text(minimalistic ? "Instructions" 98 : "Wire transfer instructions") 99 .talerFont(.title3) 100 } 101 102 if qrCodeSpecs.count > 0 { 103 let qrCodesForPayto = QRcodesForPayto(stack: stack.push(), qrCodeSpecs: $qrCodeSpecs) 104 NavigationLink(destination: qrCodesForPayto) { 105 Text(minimalistic ? "QR" 106 : "Wire transfer QR codes") 107 .talerFont(.title3) 108 } 109 .listRowSeparator(.automatic) 110 } 111 #if DEBUG 112 if let iban = payto.iban { 113 Text(minimalistic ? "**Alternative:** Use this PayTo-Link:" 114 : "**Alternative:** If your bank already supports PayTo, you can use this PayTo-Link instead:") 115 .multilineTextAlignment(.leading) 116 .padding(.top) 117 .listRowSeparator(.hidden) 118 let title = String(localized: "Share the PayTo URL", comment: "a11y") 119 let minTitle = String(localized: "Share PayTo", comment: "mini") 120 let textToShare = String("\(payto)\n\nIBAN: \(iban)\nReceiver: \(receiverStr)\nAmount: \(amountStr.1)\nSubject: \(details.reservePub)") 121 let _ = print(textToShare) 122 ShareButton(textToShare: textToShare, title: minimalistic ? minTitle : title) 123 .frame(maxWidth: .infinity, alignment: .center) 124 .accessibilityLabel(Text(title)) 125 .disabled(false) 126 .listRowSeparator(.hidden) 127 } 128 #endif 129 }.id(listID) 130 .talerFont(.body) 131 .task { await viewDidLoad(account.paytoUri) } 132 } else { 133 // TODO: Error No payto URL 134 } 135 } else { 136 // TODO: Error No amount 137 } 138 } else { 139 // TODO: Error none of the details is valid 140 } 141 } else { 142 // TODO: Error No exchangeCreditAccountDetails 143 } 144 } 145 } 146 // MARK: - 147 #if DEBUG 148 struct ManualDetails_Previews: PreviewProvider { 149 static var previews: some View { 150 let common = TransactionCommon(type: .withdrawal, 151 transactionId: "someTxID", 152 timestamp: Timestamp(from: 1_666_666_000_000), 153 scopes: [], 154 txState: TransactionState(major: .done), 155 txActions: [], 156 amountRaw: Amount(currency: LONGCURRENCY, cent: 220), 157 amountEffective: Amount(currency: LONGCURRENCY, cent: 110)) 158 let payto = "payto://iban/SANDBOXX/DE159593?receiver-name=Exchange+Company" 159 let details = WithdrawalDetails(type: .manual, 160 reservePub: "ReSeRvEpUbLiC_KeY_FoR_WiThDrAwAl", 161 reserveIsReady: false, 162 confirmed: false) 163 List { 164 ManualDetailsV(stack: CallStack("Preview"), common: common, details: details) 165 } 166 } 167 } 168 #endif