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