taler-ios

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

P2PReadyV.swift (6020B)


      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 taler_swift
     10 import SymLog
     11 
     12 // Called when initiating a P2P transaction: Send or Request(Invoice)
     13 struct P2PReadyV: View {
     14     private let symLog = SymLogV(0)
     15     let stack: CallStack
     16     let scope: ScopeInfo
     17     let summary: String
     18     let iconID: String?
     19     let expireDays: UInt
     20     let outgoing: Bool
     21     let amountToTransfer: Amount
     22     @Binding var transactionStarted: Bool
     23 
     24     @EnvironmentObject private var controller: Controller
     25     @EnvironmentObject private var model: WalletModel
     26 #if DEBUG
     27     @AppStorage("developerMode") var developerMode: Bool = true
     28 #else
     29     @AppStorage("developerMode") var developerMode: Bool = false
     30 #endif
     31     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
     32 
     33     @State private var transactionId: String? = nil
     34     @State private var initiationError: Error? = nil
     35     @State private var noTransaction: TalerTransaction? = nil
     36     @State private var talerTX = TalerTransaction(dummyCurrency: DEMOCURRENCY)
     37     @Namespace var namespace
     38 
     39     @MainActor
     40     private func viewDidLoad() async {
     41         symLog.log("viewDidLoad")
     42         guard transactionStarted == false else {
     43 // TODO:    logger.warning("Trying to start P2P a second time")
     44             symLog.log("Yikes❗️ Trying to start P2P a second time")
     45             return
     46         }
     47         transactionStarted = true
     48         // keep expiration of outgoing P2P in developer mode for debugging
     49         let timestamp = developerMode ? Timestamp.inSomeMinutes(expireDays > 20 ? (24*60)   // 24h
     50                                                               : expireDays > 5  ? 60        //  1h
     51                                                                                 : 3)        //  3m
     52                                       : Timestamp.inSomeDays(expireDays)
     53         let purseExpiration = developerMode ? timestamp
     54                                  : outgoing ? nil : timestamp                   // nil for outgoing (non-dev)
     55         let terms = PeerContractTerms(amount: amountToTransfer,
     56                                      summary: summary,
     57                             purse_expiration: purseExpiration,
     58                                      icon_id: iconID)
     59         do {
     60             if outgoing {
     61                 let response = try await model.initiatePeerPushDebit(scope: scope, terms: terms,
     62                                                                viewHandles: true)
     63                 // will switch from WithdrawProgressView to TransactionSummaryList
     64                 transactionId = response.transactionId
     65             } else {
     66                 // scope is the exchange the user picked in the ScopePicker, and the one the
     67                 // fee was computed for. nil (== wallet-core decides) only for global scope.
     68                 let response = try await model.initiatePeerPullCredit(scope.url, terms: terms,
     69                                                                 viewHandles: true)
     70                 // will switch from WithdrawProgressView to TransactionSummaryList
     71                 transactionId = response.transactionId
     72             }
     73         } catch {
     74             symLog.log("❗️ \(error), \(error.localizedDescription)")
     75             transactionStarted = false      // failed, so let the user try again
     76             initiationError = error         // we show it ourselves (viewHandles: true)
     77         }
     78     }
     79 
     80     func doneAction() {
     81         dismissTop(stack.push())
     82     }
     83 
     84     var body: some View {
     85 #if PRINT_CHANGES
     86         let _ = Self._printChanges()
     87         let _ = symLog.vlog()       // just to get the # to compare it with .onAppear & onDisappear
     88 #endif
     89         let navTitle = String(localized: "Ready")
     90         ZStack {
     91             if let transactionId {
     92                 TransactionSummaryList(stack: stack.push(),
     93 //                                     scope: scope,
     94                                transactionId: transactionId,
     95                                      talerTX: $talerTX,
     96                                     navTitle: navTitle,
     97                                      hasDone: true,             // conclude p2p
     98                                     showDone: .prominent,
     99                                          url: nil,
    100                                  withActions: false)
    101             } else if let initiationError {
    102                 // don't spin forever - the user can go back and start over
    103                 ErrorView(stack.push(), error: initiationError, devMode: developerMode)
    104             } else {
    105 #if DEBUG
    106                 let message = amountToTransfer.currencyStr
    107 #else
    108                 let message: String? = nil
    109 #endif
    110                 LoadingView(stack: stack.push(), scopeInfo: scope, message: message)
    111                     .task { await viewDidLoad() }
    112             }
    113         }.onAppear {
    114             symLog.log("onAppear")
    115             DebugViewC.shared.setViewID(VIEW_P2P_READY, stack: stack.push())
    116         }
    117         .onDisappear {
    118 //            print("❗️ P2PReadyV onDisappear")
    119         }
    120 #if OIM
    121             .overlay { if #available(iOS 16.4, *) {
    122                 if controller.oimModeActive {
    123                     OIMp2pReadyView(stack: stack.push(),
    124                             transactionId: $transactionId,
    125                                   talerTX: talerTX,
    126                                     scope: scope,
    127                                    action: doneAction)
    128                     .environmentObject(NamespaceWrapper(namespace))             // keep OIMviews apart
    129                 }
    130             } }
    131 #endif
    132     }
    133 }
    134 // MARK: -
    135 //struct SendNow_Previews: PreviewProvider {
    136 //    static var previews: some View {
    137 //        Group {
    138 //            SendDoneV(stack: CallStack("Preview"),
    139 //               amountToSend: Amount(currency: LONGCURRENCY, cent: 480),
    140 //           amountToReceive: nil,
    141 //                   summary: "some subject/purpose",
    142 //                expireDays: 0)
    143 //        }
    144 //    }
    145 //}