ManualDetailsWireV.swift (15032B)
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 TransferRestrictionsV: View { 13 let amountStr: (String, String) 14 let obtainStr: (String, String)? 15 let debitIBAN: String? 16 let restrictions: [AccountRestriction]? 17 18 @AppStorage("minimalistic") var minimalistic: Bool = false 19 20 @State private var selectedLanguage = Locale.preferredLanguageCode 21 22 private func transferMini(_ amountS: String) -> String { 23 let amountNBS = amountS.nbs 24 return String(localized: "Transfer \(amountNBS) to the payment service.") 25 } 26 private func transferMaxi(_ amountS: String, _ obtainS: String) -> String { 27 let amountNBS = amountS.nbs 28 let obtainNBS = obtainS.nbs 29 return String(localized: "You need to transfer \(amountNBS) from your regular bank account to the payment service to receive \(obtainNBS) as digital cash in this wallet.") 30 } 31 32 private func authMini(_ amountS: String, _ debitS: String) -> String { 33 let amountNBS = amountS.nbs 34 return String(localized: "Transfer \(amountNBS) from account \(debitS) to verify having control over it.") 35 } 36 private func authMaxi(_ amountS: String, _ debitS: String) -> String { 37 let amountNBS = amountS.nbs 38 return String(localized: "You need to transfer \(amountNBS) to the payment service from your bank account \(debitS) to verify having control over it. Don't use a different bank account, or the verification will fail.") 39 } 40 41 var body: some View { 42 VStack(alignment: .leading) { 43 if let obtainStr { 44 Text(minimalistic ? transferMini(amountStr.0) 45 : transferMaxi(amountStr.0, obtainStr.0)) 46 .accessibilityLabel(minimalistic ? transferMini(amountStr.1) 47 : transferMaxi(amountStr.1, obtainStr.1)) 48 .talerFont(.body) 49 .multilineTextAlignment(.leading) 50 } else if let debitIBAN { 51 Text(minimalistic ? authMini(amountStr.0, debitIBAN) 52 : authMaxi(amountStr.0, debitIBAN)) 53 .accessibilityLabel(minimalistic ? authMini(amountStr.1, debitIBAN) 54 : authMaxi(amountStr.1, debitIBAN)) 55 .talerFont(.body) 56 .multilineTextAlignment(.leading) 57 } 58 if let restrictions { 59 ForEach(restrictions) { restriction in 60 if let hintsI18n = restriction.human_hint_i18n { 61 // let sortedDict = OrderedDictionary(uniqueKeys: hintsI18n.keys, values: hintsI18n.values) 62 // var sorted: OrderedDictionary<String:String> 63 let sortedDict = OrderedDictionary(uncheckedUniqueKeysWithValues: hintsI18n.sorted { $0.key < $1.key }) 64 Picker("Restriction:", selection: $selectedLanguage) { 65 ForEach(sortedDict.keys, id: \.self) { 66 Text(sortedDict[$0] ?? "missing hint") 67 } 68 } 69 } else if let hint = restriction.human_hint { 70 Text(hint) 71 } 72 } 73 } 74 } 75 } 76 } 77 // MARK: - 78 struct ManualDetailsWireV: View { 79 let stack: CallStack 80 let reservePub: String 81 let receiverStr: String 82 let receiverZip: String? 83 let receiverTown: String? 84 let iban: String? 85 let xTaler: String 86 let amountValue: String // string representation of the value, formatted as "`integer`.`fraction`" 87 let amountStr: (String, String) 88 let obtainStr: (String, String)? // only for withdrawal 89 let debitIBAN: String? // only for deposit auth 90 let account: ExchangeAccountDetails 91 92 @AppStorage("minimalistic") var minimalistic: Bool = false 93 let navTitle = String(localized: "Wire transfer", comment: "ViewTitle of wire-transfer instructions") 94 95 private func step3(_ amountS: String) -> String { 96 let amountNBS = amountS.nbs 97 let bePatient = String(localized: "Depending on your bank the transfer can take from minutes to two working days, please be patient.") 98 if let debitIBAN { 99 return minimalistic ? String(localized: "Transfer \(amountNBS) from \(debitIBAN).") 100 : String(localized: "Finish the wire transfer of \(amountNBS) in your banking app or website to verify your bank account \(debitIBAN).") + "\n" + bePatient 101 } 102 return minimalistic ? String(localized: "Transfer \(amountNBS).") 103 : String(localized: "Finish the wire transfer of \(amountNBS) in your banking app or website, then this withdrawal will proceed automatically.") + "\n" + bePatient 104 } 105 106 @ViewBuilder func payeeZip() -> some View { 107 HStack { 108 VStack(alignment: .leading) { 109 Text("Zip code:") 110 .talerFont(.subheadline) 111 Text(receiverZip ?? EMPTYSTRING) 112 .monospacedDigit() 113 .padding(.leading) 114 } .frame(maxWidth: .infinity, alignment: .leading) 115 .accessibilityElement(children: .combine) 116 .accessibilityLabel(Text("Zip code", comment: "a11y")) 117 CopyButton(textToCopy: receiverZip ?? EMPTYSTRING, vertical: true) 118 .accessibilityLabel(Text("Copy the zip code", comment: "a11y")) 119 .disabled(false) 120 } .padding(.top, -8) 121 } 122 123 @ViewBuilder func payeeTown() -> some View { 124 HStack { 125 VStack(alignment: .leading) { 126 Text("City:") 127 .talerFont(.subheadline) 128 Text(receiverTown ?? EMPTYSTRING) 129 .monospacedDigit() 130 .padding(.leading) 131 } .frame(maxWidth: .infinity, alignment: .leading) 132 .accessibilityElement(children: .combine) 133 .accessibilityLabel(Text("City", comment: "a11y")) 134 CopyButton(textToCopy: receiverTown ?? EMPTYSTRING, vertical: true) 135 .accessibilityLabel(Text("Copy the city", comment: "a11y")) 136 .disabled(false) 137 } .padding(.top, -8) 138 } 139 140 var body: some View { 141 List { 142 let cryptoString = debitIBAN == nil ? reservePub : "kyc" + reservePub 143 let cryptocode = HStack { 144 Text(cryptoString) 145 .monospacedDigit() 146 .accessibilityLabel(Text("Cryptocode", comment: "a11y")) 147 .frame(maxWidth: .infinity, alignment: .leading) 148 CopyButton(textToCopy: cryptoString, vertical: true) 149 .accessibilityLabel(Text("Copy the cryptocode", comment: "a11y")) 150 .disabled(false) 151 } .padding(.leading) 152 let payeeCode = HStack { 153 VStack(alignment: .leading) { 154 Text("Recipient:") 155 .talerFont(.subheadline) 156 Text(receiverStr) 157 .monospacedDigit() 158 .padding(.leading) 159 } .frame(maxWidth: .infinity, alignment: .leading) 160 .accessibilityElement(children: .combine) 161 .accessibilityLabel(Text("Recipient", comment: "a11y")) 162 CopyButton(textToCopy: receiverStr, vertical: true) 163 // CopyButton(textToCopy: buildReceiver(), vertical: true) 164 .accessibilityLabel(Text("Copy the recipient", comment: "a11y")) 165 .disabled(false) 166 } .padding(.top, -8) 167 let ibanCode = HStack { 168 VStack(alignment: .leading) { 169 Text("IBAN:") 170 .talerFont(.subheadline) 171 Text(iban ?? EMPTYSTRING) 172 .monospacedDigit() 173 .padding(.leading) 174 } .frame(maxWidth: .infinity, alignment: .leading) 175 .accessibilityElement(children: .combine) 176 .accessibilityLabel(Text("IBAN of the recipient", comment: "a11y")) 177 CopyButton(textToCopy: iban ?? EMPTYSTRING, vertical: true) 178 .accessibilityLabel(Text("Copy the IBAN", comment: "a11y")) 179 .disabled(false) 180 } // .padding(.top, -8) 181 let amountCode = HStack { 182 VStack(alignment: .leading) { 183 Text("Amount:") 184 .talerFont(.subheadline) 185 Text(amountStr.0) 186 .accessibilityLabel(amountStr.1) 187 .monospacedDigit() 188 .padding(.leading) 189 } .frame(maxWidth: .infinity, alignment: .leading) 190 .accessibilityElement(children: .combine) 191 .accessibilityLabel(Text("Amount to transfer", comment: "a11y")) 192 CopyButton(textToCopy: amountValue, vertical: true) // only digits + separator, no currency name or symbol 193 .accessibilityLabel(Text("Copy the amount", comment: "a11y")) 194 .disabled(false) 195 } .padding(.top, -8) 196 let xTalerCode = HStack { 197 VStack(alignment: .leading) { 198 Text("Account:") 199 .talerFont(.subheadline) 200 Text(xTaler) 201 .monospacedDigit() 202 .padding(.leading) 203 } .frame(maxWidth: .infinity, alignment: .leading) 204 .accessibilityElement(children: .combine) 205 .accessibilityLabel(Text("account of the recipient", comment: "a11y")) 206 CopyButton(textToCopy: xTaler, vertical: true) 207 .accessibilityLabel(Text("Copy the account", comment: "a11y")) 208 .disabled(false) 209 } .padding(.top, -8) 210 let step1 = Text(minimalistic ? "**Step 1:** Copy+Paste this subject:" 211 : "**Step 1:** Copy this code and paste it into the subject/purpose field in your banking app or bank website:") 212 .talerFont(.body) 213 .multilineTextAlignment(.leading) 214 let manda1 = String(localized: "This is mandatory, otherwise your money will not arrive in this wallet.") 215 let manda2 = String(localized: "This is mandatory, otherwise the verification will fail.") 216 let mandatory = Text(debitIBAN == nil ? manda1 : manda2) 217 .bold() 218 .talerFont(.body) 219 .multilineTextAlignment(.leading) 220 .listRowSeparator(.hidden) 221 let step2i = Text(minimalistic ? "**Step 2:** Copy+Paste recipient and IBAN:" 222 : "**Step 2:** If you don't already have it in your banking favorites list, then copy and paste recipient and IBAN into the recipient/IBAN fields in your banking app or website (and save it as favorite for the next time):") 223 .talerFont(.body) 224 .multilineTextAlignment(.leading) 225 .padding(.top) 226 let step2x = Text(minimalistic ? "**Step 2:** Copy+Paste recipient and account:" 227 : "**Step 2:** Copy and paste recipient and account into the corresponding fields in your banking app or website:") 228 .talerFont(.body) 229 .multilineTextAlignment(.leading) 230 .padding(.top) 231 let step3A11y = String(localized: "Step 3: \(step3(amountStr.1))", comment: "a11y") 232 let step3Head: LocalizedStringKey = "**Step 3:** \(step3(amountStr.0))" 233 let step3 = Text(step3Head) 234 .accessibilityLabel(step3A11y) 235 .talerFont(.body) 236 .multilineTextAlignment(.leading) 237 238 Group { 239 TransferRestrictionsV(amountStr: amountStr, 240 obtainStr: obtainStr, 241 debitIBAN: debitIBAN, 242 restrictions: account.creditRestrictions) 243 .listRowSeparator(.visible) 244 step1 245 if !minimalistic { 246 mandatory 247 } 248 cryptocode 249 if iban != nil { 250 step2i 251 payeeCode 252 if let receiverZip { 253 if !receiverZip.isEmpty { 254 payeeZip() 255 } 256 } 257 if let receiverTown { 258 if !receiverTown.isEmpty { 259 payeeTown() 260 } 261 } 262 ibanCode 263 } else { 264 step2x 265 payeeCode 266 xTalerCode 267 } 268 amountCode 269 // .padding(.top) 270 step3 // .padding(.top, 6) 271 }.listRowSeparator(.hidden) 272 } 273 .navigationTitle(navTitle) 274 .onAppear() { 275 // symLog.log("onAppear") 276 DebugViewC.shared.setViewID(VIEW_WITHDRAW_INSTRUCTIONS, stack: stack.push()) 277 } 278 } 279 } 280 281 // MARK: - 282 #if DEBUG 283 //struct ManualDetailsWire_Previews: PreviewProvider { 284 // static var previews: some View { 285 // let common = TransactionCommon(type: .withdrawal, 286 // transactionId: "someTxID", 287 // timestamp: Timestamp(from: 1_666_666_000_000), 288 // txState: TransactionState(major: .done), 289 // txActions: []) 290 // amountEffective: Amount(currency: LONGCURRENCY, cent: 110), 291 // amountRaw: Amount(currency: LONGCURRENCY, cent: 220), 292 // let payto = "payto://iban/SANDBOXX/DE159593?receiver-name=Exchange+Company" 293 // let details = WithdrawalDetails(type: .manual, 294 // reservePub: "ReSeRvEpUbLiC_KeY_FoR_WiThDrAwAl", 295 // reserveIsReady: false, 296 // confirmed: false) 297 // List { 298 // ManualDetailsWireV(stack: CallStack("Preview"), 299 // details: details, 300 // receiverStr: <#T##String#>, 301 // iban: <#T##String?#>, 302 // xTaler: <#T##String#>, 303 // amountStr: <#T##String#>, 304 // obtainStr: <#T##String#>, 305 // account: T##ExchangeAccountDetails) 306 // } 307 // } 308 //} 309 #endif