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