taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

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