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