WalletEmptyView.swift (10026B)
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 SymLog 10 import taler_swift 11 12 struct ProdSectionView: View { 13 let stack: CallStack 14 let isEmpty: Bool 15 let disabled: Bool 16 17 @Environment(\.colorScheme) private var colorScheme 18 @Environment(\.colorSchemeContrast) private var colorSchemeContrast 19 @EnvironmentObject private var controller: Controller 20 @EnvironmentObject private var model: WalletModel 21 #if DEBUG 22 @AppStorage("developerMode") var developerMode: Bool = true 23 #else 24 @AppStorage("developerMode") var developerMode: Bool = false 25 #endif 26 27 @State private var selectedCurrency: Int = 0 28 @State private var showDropdown = true 29 30 func buttonAction() { 31 withAnimation { 32 showDropdown.toggle() 33 } 34 } 35 36 var body: some View { 37 let defaultExchanges = controller.defaultExchanges 38 let count = defaultExchanges.count 39 Section { 40 let chevron = Image(systemName: CHEVRON_UP) // 41 if isEmpty { 42 Text("Welcome to Taler Wallet!") 43 .talerFont(.headline) 44 Text("To make your first payment, withdraw digital cash.") 45 .talerFont(.body) 46 } else { 47 Button(action: buttonAction) { 48 HStack(alignment: .firstTextBaseline) { 49 Text("Currently you have only demo cash.") 50 .talerFont(.body) 51 .accessibilityAddTraits(.isSelected) 52 Spacer() 53 chevron.rotationEffect(.degrees((showDropdown ? -180 : 0))) 54 .accessibilityHidden(true) 55 } 56 } .buttonStyle(.plain) 57 if (showDropdown) { 58 Text("Withdraw real digital cash:") 59 .talerFont(.body) 60 .padding(.top, -30) 61 } 62 } 63 #if false 64 let qrButton = Image(systemName: QRBUTTON) // "qrcode.viewfinder" 65 let settings = Image(systemName: SETTINGS) // "gear" 66 Text("Use «\(qrButton) Scan QR code» in the Actions menu to start a withdrawal if your bank already supports Taler payments.", comment: "« » 'qrcode.viewfinder'") 67 .talerFont(.body) 68 Text("You can also add a payment service manually in the \(settings) Settings tab.", comment: "« » 'gear'") 69 .talerFont(.body) 70 #endif 71 if (showDropdown) { 72 #if !TALER_WALLET 73 if developerMode { 74 if defaultExchanges.count > 1 { 75 CurrencyPicker(stack: stack.push(), value: $selectedCurrency, 76 exchanges: defaultExchanges) { index in 77 selectedCurrency = index 78 } 79 } 80 } 81 #endif 82 let defaultExchange = defaultExchanges[selectedCurrency < count ? selectedCurrency : 0] 83 let currency = defaultExchange.currency 84 let title = String(localized: "LinkTitle_Withdraw", defaultValue: "Withdraw \(currency)") 85 Button(title) { 86 if let talerUri = URL(string: defaultExchange.talerUri) { 87 controller.talerURI = talerUri 88 } 89 } 90 .buttonStyle(TalerButtonStyle(type: isEmpty ? .prominent : .bordered, 91 narrow: false, 92 disabled: disabled, 93 aligned: .center)) 94 .padding(.bottom, 6) 95 } // showDropdown 96 } header: { 97 if isEmpty { 98 let firstHeader = EMPTYSTRING 99 Text(firstHeader) 100 .talerFont(.badge) 101 // .foregroundColor(.primary) 102 /// YIKES! when not specifying this color, the Text in the BarGraphHeader vanishes!!! 103 .foregroundColor(WalletColors().secondary(colorScheme, colorSchemeContrast)) 104 // .listRowInsets(EdgeInsets()) // << here !! 105 } 106 } 107 .listRowSeparator(.hidden) 108 // .listSectionSpacing(.compact) iOS 17 109 } 110 } 111 112 /// This view shows hints if a wallet is empty 113 /// It is the very first thing the user sees after installing the app 114 115 struct WalletEmptyHeader: View { 116 let stack: CallStack 117 118 @EnvironmentObject private var model: WalletModel 119 @EnvironmentObject private var controller: Controller 120 121 func refresh() async { 122 controller.hapticNotification(.success) 123 // symLog.log("refreshing balances") 124 await controller.loadBalances(stack.push("refreshing balances"), model) 125 } 126 127 var body: some View { 128 let talerLogo = HStack(spacing: 2) { 129 Image(TALER_LOGO_FULL) 130 // .border(Color.gray, width: 1) 131 // Text("TALER Wallet") 132 Text("Wallet") 133 // .font(.logo(27, weight: .medium)) 134 .font(.logo("Atkinson Hyperlegible Next", size: 27, weight: .medium)) 135 .kerning(1.0) 136 } 137 .accessibilityElement(children: .combine) 138 .accessibilityAddTraits(.isHeader) 139 .accessibilityLabel(Text("Taler Wallet", comment: "a11y")) 140 141 let emptyView = WalletEmptyView(stack: stack.push("isEmpty")) 142 // .navigationTitle("Taler Wallet") 143 .refreshable { 144 await refresh() 145 } 146 147 let logoItem = ToolbarItem(placement: .principal) { 148 talerLogo 149 .padding(.top, 10) 150 } 151 if #available(iOS 26.0, *) { 152 emptyView 153 .toolbar { 154 logoItem 155 ToolbarItem(placement: .primaryAction) { 156 Button { 157 NotificationCenter.default.post(name: .SettingsAction, object: nil) // will trigger NavigationLink 158 } label: { 159 Label(TalerTab.settings.title, systemImage: TalerTab.settings.sysImg) 160 .labelStyle(.iconOnly) 161 } 162 // .padding() 163 .buttonStyle(.glass) 164 .accessibilitySortPriority(0) 165 166 } 167 } 168 } else { // iOS 15..18 Settings is the 3rd tab 169 emptyView 170 .toolbar { logoItem } 171 } 172 } 173 } 174 175 struct WalletEmptyView: View { 176 private let symLog = SymLogV(0) 177 let stack: CallStack 178 179 @EnvironmentObject private var controller: Controller 180 @EnvironmentObject private var model: WalletModel 181 @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic 182 @State private var withDrawStarted = false 183 @State private var urlToOpen: URL? = nil 184 185 var body: some View { 186 let list = List { 187 if !controller.defaultExchanges.isEmpty { 188 ProdSectionView(stack: stack.push(), isEmpty: true, disabled: withDrawStarted) 189 } 190 #if GNU_TALER 191 Text("If you don't have a Swiss bank account, you can simply try out the demo…") 192 .talerFont(.body) 193 #endif 194 195 #if DEBUG 196 // if developerMode { 197 // Text("Either deposit or send your money to a friend before you delete the app, or it will be lost.") 198 // .talerFont(.body) 199 // } 200 #endif 201 Section { 202 // Text("Demo") 203 // .talerFont(.headline) 204 // Text("Get digital cash to experience how easy you can pay online, and send money to friends & family.") 205 Text("Get demo cash to experience how to pay with digital cash.") 206 // Text("Get digital cash to experience how to pay with the money of the future.") 207 .talerFont(.body) 208 .listRowSeparator(.hidden) 209 let title = String(localized: "LinkTitle_Test_Money", defaultValue: "Get demo cash") 210 Button(title) { 211 withDrawStarted = true // don't run twice 212 Task { // runs on MainActor 213 let amount = Amount(currency: DEMOCURRENCY, cent: 2500) 214 symLog.log("Withdraw KUDOS") 215 try? await model.loadTestKudos(0, amount: amount) 216 DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 217 withDrawStarted = false 218 } 219 } 220 } 221 .buttonStyle(TalerButtonStyle(type: .prominent, narrow: false, disabled: withDrawStarted, aligned: .center)) 222 .disabled(withDrawStarted) 223 // } header: { 224 // let secondHeader = String(localized: "Demo", comment: "section header") 225 // Text(secondHeader) 226 // .talerFont(.title3) 227 // .foregroundColor(WalletColors().secondary(colorScheme, colorSchemeContrast)) 228 } 229 } 230 .listStyle(myListStyle.style).anyView 231 .talerFont(.title2) 232 .background(FullBackground()) 233 ZStack { 234 list 235 if withDrawStarted { 236 RotatingTaler(size: 150, progress: true, once: false, rotationEnabled: $withDrawStarted) 237 } 238 } 239 .onAppear() { 240 DebugViewC.shared.setViewID(VIEW_EMPTY_WALLET, stack: stack.push("onAppear")) // 10 241 } 242 } 243 } 244 245 struct WalletEmptyView_Previews: PreviewProvider { 246 static var previews: some View { 247 WalletEmptyView(stack: CallStack("Preview")) 248 } 249 }