taler-ios

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

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 }