taler-ios

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

ExchangeRowView.swift (6128B)


      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 struct ExchangeRowView: View {
     13     private let symLog = SymLogV(0)
     14     let stack: CallStack
     15     let exchange: Exchange
     16     let index: Int
     17     let developerMode: Bool
     18 
     19     @Environment(\.sizeCategory) var sizeCategory
     20     @EnvironmentObject private var controller: Controller
     21     @EnvironmentObject private var model: WalletModel
     22     @EnvironmentObject private var tabBarModel: TabBarModel
     23     
     24     @State private var isUpdated: Bool = false
     25 
     26     var body: some View {
     27 #if PRINT_CHANGES
     28         let _ = Self._printChanges()
     29         let _ = symLog.vlog()       // just to get the # to compare it with .onAppear & onDisappear
     30         let delay: UInt = 0     // set to 5 to test delayed currency information
     31 #else
     32         let delay: UInt = 0
     33 #endif
     34         let baseURL = exchange.exchangeBaseUrl
     35         let chevron = Text(Image(systemName: CHEVRON))
     36                         .talerFont(.table)
     37                         .foregroundColor(.secondary)
     38 
     39         let termsLine = HStack {
     40             Text("Terms of Service")  // VIEW_WITHDRAW_TOS
     41             Spacer()
     42             chevron
     43         }
     44         let rowView = VStack(alignment: .leading) {
     45             Text(baseURL.trimURL)
     46                 .talerFont(.headline)
     47             ExchangeStatus(exchange: exchange)
     48             termsLine
     49                 .talerFont(.body)
     50                 .background {           // only needed for .onTapGesture
     51                     Color.gray.opacity(INVISIBLE)
     52                 }
     53                 .onTapGesture {
     54                     tabBarModel.tosView = index
     55                 }
     56         }
     57         let showToS = WithdrawTOSView(stack: stack.push(),
     58                             exchangeBaseUrl: baseURL,
     59                                      viewID: VIEW_WITHDRAW_TOS,
     60                                acceptAction: nil)         // pop back to here
     61         let details = ExchangeDetailView(stack: stack.push(),
     62                                       exchange: exchange)
     63         let actions = Group {
     64             NavLink(index, $tabBarModel.tosView) { showToS }
     65         }
     66 
     67         Group {
     68             rowView
     69                 .background(actions)
     70 
     71             if developerMode {
     72                 HStack {
     73                     Text(verbatim: "Update exchange")  // VIEW_WITHDRAW_TOS
     74                         .foregroundStyle(isUpdated ? .gray : .primary)
     75                 }
     76                 .background {           // only needed for .onTapGesture
     77                     Color.gray.opacity(INVISIBLE)
     78                 }
     79                 .onTapGesture {
     80                     if !isUpdated {
     81                         isUpdated = true
     82                         Task {
     83                             try await model.updateExchange(exchangeBaseUrl: baseURL, force: true)
     84                         }
     85                     }
     86                 }
     87             }
     88         }.listRowSeparator(.hidden)
     89     }
     90 }
     91 // MARK: -
     92 struct  ExchangeStatus: View {
     93     let exchange: Exchange
     94 
     95     @EnvironmentObject private var controller: Controller
     96     @AppStorage("minimalistic") var minimalistic: Bool = false
     97 
     98     var body: some View {
     99         let status = controller.isConnected ? exchange.exchangeUpdateStatus.localized
    100                                             : ExchangeUpdateStatus.unavailable.localized
    101         VStack(alignment: .leading) {
    102             HStack {
    103                 if !minimalistic {
    104                     Text("Status:")
    105                 }
    106                 Text(status)
    107             }
    108             if let lastUpdateErrorInfo = exchange.lastUpdateErrorInfo {
    109                 Text("Last Error: \(lastUpdateErrorInfo.error.code)")
    110                 if let hint = lastUpdateErrorInfo.error.hint {
    111                     Text(hint)                                                  // TODO: L10N
    112                 }
    113                 if let detail = lastUpdateErrorInfo.error.detail {
    114                     Text(detail)                                                // TODO: L10N
    115                 }
    116             }
    117         }
    118         .padding(.bottom, 4)
    119         .talerFont(.body)
    120     }
    121 }
    122 // MARK: -
    123 #if DEBUG
    124 fileprivate struct ExchangeRow_Previews: PreviewProvider {
    125     @MainActor
    126     struct BindingViewContainer : View {
    127         @State private var amountToPreview = Amount(currency: LONGCURRENCY, cent: 1234)
    128         @State private var depositIBAN = "DE1234567890"
    129         @State private var accountHolder = "Marc Stibane"
    130 //        @State private var previewL: CurrencyInfo = CurrencyInfo.zero(LONGCURRENCY)
    131 
    132 //    let amount = Amount(currency: LONGCURRENCY, cent: 123456)
    133         var body: some View {
    134             let scopeInfo = ScopeInfo(type: .exchange, currency: LONGCURRENCY)
    135             let exchange1 = Exchange(exchangeBaseUrl: ARS_AGE_EXCHANGE,
    136                                            masterPub: "masterPub",
    137                                            scopeInfo: scopeInfo,
    138                                            paytoUris: [],
    139                                            tosStatus: .pending,
    140                                  exchangeEntryStatus: .preset,
    141                                 exchangeUpdateStatus: .initial,
    142                                ageRestrictionOptions: [12,16])
    143             let exchange2 = Exchange(exchangeBaseUrl: ARS_EXP_EXCHANGE,
    144                                            masterPub: "masterPub",
    145                                            scopeInfo: scopeInfo,
    146                                            paytoUris: [],
    147                                            tosStatus: .proposed,
    148                                  exchangeEntryStatus: .ephemeral,
    149                                 exchangeUpdateStatus: .ready,
    150                                ageRestrictionOptions: [])
    151             ExchangeRowView(stack: CallStack("Preview"),
    152                          exchange: exchange1,
    153                             index: 1,
    154                     developerMode: true)
    155         }
    156     }
    157 
    158     @MainActor
    159     static var previews: some View {
    160         List {
    161             BindingViewContainer()
    162         }
    163     }
    164 }
    165 #endif