ExchangeListView.swift (5202B)
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 taler_swift 10 import SymLog 11 12 /// This view shows the list of exchanges 13 struct ExchangeListView: View { 14 private let symLog = SymLogV(0) 15 let stack: CallStack 16 let navTitle: String 17 @EnvironmentObject private var model: WalletModel 18 @EnvironmentObject private var controller: Controller 19 20 @State var showAlert: Bool = false 21 @State var newExchange: String = TESTEXCHANGE 22 23 @MainActor 24 func addExchange(_ exchange: String) -> Void { 25 Task { // runs on MainActor 26 symLog.log("adding: \(exchange)") 27 if let _ = try? await model.addExchange(uri: exchange) { 28 symLog.log("added: \(exchange)") 29 announce("added: \(exchange)") 30 NotificationCenter.default.post(name: .ExchangeAdded, object: nil, userInfo: nil) 31 NotificationCenter.default.post(name: .BalanceChange, object: nil, userInfo: nil) // TODO: ExchangeAdded should suffice 32 } 33 } 34 } 35 36 var body: some View { 37 let a11yLabelStr = String(localized: "Add payment service", comment: "a11y for the + button") 38 let plusButton = PlusButton(accessibilityLabelStr: a11yLabelStr) { 39 showAlert = true 40 } 41 let addTitleStr = String(localized: "Add payment service", comment: "title of the addExchange alert") 42 let addButtonStr = String(localized: "Add", comment: "button in the addExchange alert") 43 let enterURL = String(localized: "Enter the URL") 44 if #available(iOS 16.4, *) { 45 ExchangeListCommonV(symLog: symLog, stack: stack.push()) 46 .navigationTitle(navTitle) 47 .navigationBarItems(trailing: plusButton) 48 .alert(addTitleStr, isPresented: $showAlert) { 49 TextField("Address of the payment service", text: $newExchange) 50 .accessibilityLabel(enterURL) 51 // .textFieldStyle(.roundedBorder) Yikes: when adding style the alert will stop showing the textfield! Don't do this. 52 Button(addButtonStr) { 53 addExchange(newExchange) 54 } 55 Button("Cancel", role: .cancel) { } 56 } message: { 57 Text(enterURL) 58 .accessibilityHidden(true) 59 } 60 } else { // iOS 15 cannot have a textfield in an alert, so we must 61 ExchangeListCommonV(symLog: symLog, stack: stack.push()) 62 .navigationTitle(navTitle) 63 .navigationBarItems(trailing: plusButton) 64 .textFieldAlert(isPresented: $showAlert, 65 title: addTitleStr, 66 doneText: addButtonStr, 67 text: $newExchange) { text in 68 addExchange(text) 69 } 70 } 71 } 72 } 73 // MARK: - 74 struct ExchangeListCommonV: View { 75 let symLog: SymLogV? 76 let stack: CallStack 77 78 @EnvironmentObject private var controller: Controller 79 @AppStorage("minimalistic") var minimalistic: Bool = false 80 @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic 81 82 var body: some View { 83 #if PRINT_CHANGES 84 let _ = Self._printChanges() 85 let _ = symLog?.vlog() // just to get the # to compare it with .onAppear & onDisappear 86 #endif 87 let balanceList = List(Array(controller.balances.enumerated()), id: \.element) { index, balance in 88 ExchangeSectionView(stack: stack.push(), 89 balance: balance, 90 thousand: index * MAXEXCHANGES) // unique ID 91 } 92 93 let emptyList = List { 94 Section { 95 Text("There are no payment services yet.") 96 .talerFont(.title3) 97 } 98 Section { 99 let plus = Image(systemName: "plus") 100 if !minimalistic { 101 Text("Tap the \(plus) button to add a service.") 102 .listRowSeparator(.hidden) 103 Text("You can also scan a withdrawal QR code from your bank in the Action menu to automatically add a payment service.") 104 } else { 105 Text("Tap \(plus) or scan a withdrawal QR code from your bank to add a payment service.") 106 } 107 } 108 .talerFont(.body) 109 } 110 111 Group { 112 if controller.balances.isEmpty { 113 emptyList 114 } else { 115 balanceList // sorted by wallet-core 116 } 117 } 118 .listStyle(myListStyle.style).anyView 119 .refreshable { 120 controller.hapticNotification(.success) 121 symLog?.log("refreshing") 122 // await reloadExchanges() 123 NotificationCenter.default.post(name: .BalanceChange, object: nil, userInfo: nil) 124 } 125 .onAppear() { 126 DebugViewC.shared.setViewID(VIEW_PAYMENT_SERVICES, stack: stack.push()) 127 } 128 } // body 129 }