taler-ios

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

BalancesListView.swift (5810B)


      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 import AVFoundation
     12 
     13 /// This view shows the list of balances / currencies, each in its own section
     14 struct BalancesListView: View {
     15     private let symLog = SymLogV(0)
     16     let stack: CallStack
     17     let title: String
     18     @Binding var selectedBalance: Balance?                      // set in TransactionsListView
     19     @Binding var reloadTransactions: Int
     20 
     21     @EnvironmentObject private var model: WalletModel
     22     @EnvironmentObject private var controller: Controller
     23     @Environment(\.accessibilityVoiceOverEnabled) private var voiceOverEnabled
     24     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
     25     @AppStorage("demoHints") var demoHints: Bool = true
     26     @AppStorage("preferredColorScheme") var preferredColorScheme: Int = 0
     27     @AppStorage("oimEuro") var oimEuro: Bool = false
     28 
     29     @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING)    // Update currency when used
     30     @State private var summary = EMPTYSTRING
     31     @State private var showRealCashHint = true
     32 
     33     @State private var historyTapped: Int? = nil
     34     @Namespace var namespace
     35 
     36     private static func className() -> String {"\(self)"}
     37 
     38     func refresh() async {
     39         controller.hapticNotification(.success)
     40         symLog.log("refreshing balances")
     41         await controller.loadBalances(stack.push("refreshing balances"), model)
     42     }
     43 
     44     @ViewBuilder
     45     func balancesList() -> some View {
     46         let count = controller.balances.count
     47         let list = List {
     48             if !controller.haveProdBalance && !controller.defaultExchanges.isEmpty && demoHints {
     49                 ProdSectionView(stack: stack.push(),
     50                      showRealCashHint: $showRealCashHint,
     51                               isEmpty: false,
     52                              disabled: false)
     53             }
     54             ForEachWithIndex(array: controller.balances) { index, balance in
     55                 BalancesSectionView(stack: stack.push("\(balance.scopeInfo.currency)"),
     56                                   balance: balance,                     // this is the currency to be used
     57                           selectedBalance: $selectedBalance,            // set in TransactionsListView
     58                              balanceIndex: index,
     59                              sectionCount: count,
     60                          amountToTransfer: $amountToTransfer,           // does still have the wrong currency
     61                                   summary: $summary,
     62                             historyTapped: $historyTapped,
     63                        reloadTransactions: $reloadTransactions)
     64             }
     65 #if DEBUG || TALER_NIGHTLY
     66             if count > 1 {      // otherwise it comes before recentTransactions
     67                 DiscountPassesSection(stack: stack.push())
     68             }
     69 #endif
     70         }
     71             .listStyle(myListStyle.style).anyView
     72             .background(FullBackground())
     73             .onAppear() {
     74                 DebugViewC.shared.setViewID(VIEW_BALANCES, stack: stack.push("onAppear"))
     75                 if !controller.oimModeActive {
     76                     print("🚩BalancesListView.onAppear() reset selectedBalance")
     77 //                  if count > 1 {
     78                         selectedBalance = nil                           // reset
     79 //                  }
     80                 }
     81             }
     82             .refreshable {
     83                 await refresh()
     84             }
     85             .navigationTitle(title)
     86         if voiceOverEnabled {
     87             list
     88         } else if #available(iOS 26.0, *) {
     89             let list26 = list
     90                 .toolbar {
     91                     ToolbarItem(placement: .topBarTrailing) {   //secondaryAction
     92                         SettingsButton26(sortPriority: 0)
     93                     }
     94                 }
     95 #if OIM
     96             if controller.oimModeActive || voiceOverEnabled {
     97                 list    // hide SettingsButton (in Toolbar)
     98             } else {
     99                 list26
    100             }
    101 #else
    102             list26
    103 #endif
    104         } else {
    105             list
    106         }
    107     }
    108 
    109     var body: some View {
    110 #if PRINT_CHANGES
    111         let _ = Self._printChanges()
    112         let _ = symLog.vlog()       // just to get the # to compare it with .onAppear & onDisappear
    113 #endif
    114         /// In standard mode, selectedBalance just sets a "preference" which balance to pre-select for Actions.
    115         ///   However, the user can select another balance (with the picker) in each action
    116         /// In OIM mode, the user selects a balance 'here' (in OIMView) when tapping on a savings box (representing the balance)
    117 
    118         let list = balancesList()
    119             .onChange(of: controller.oimModeActive) { oimModeActive in
    120                 if !oimModeActive {
    121                     print("🚩BalancesListView.onChange(of: oimModeActive) reset selectedBalance")
    122 //                    if count > 1 {
    123                         selectedBalance = nil                               // reset
    124 //                    }
    125                  }
    126             }
    127         ZStack {
    128             if preferredColorScheme == 3, #available(iOS 17.0, *) {
    129                 list.scrollContentBackground(.hidden)
    130             } else {
    131                 list
    132             }
    133         }
    134             .overlay { if #available(iOS 16.4, *) {
    135                 if controller.oimModeActive {
    136 #if OIM
    137                     OIMbalances(stack: stack.push(),
    138                       selectedBalance: $selectedBalance,                    // set to user choice
    139                         historyTapped: $historyTapped,
    140                               oimEuro: oimEuro)
    141                         .environmentObject(NamespaceWrapper(namespace))         // keep OIMviews apart
    142 #endif // OIM
    143                 }
    144             } }
    145     }
    146 }