DebugSettingsView.swift (14873B)
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 LocalConsole 12 13 struct DebugSettingsView: View { 14 private let symLog = SymLogV(0) 15 let stack: CallStack 16 let navTitle: String 17 18 @EnvironmentObject private var controller: Controller 19 @EnvironmentObject private var model: WalletModel 20 // @Environment(\.colorSchemeContrast) private var colorSchemeContrast 21 #if DEBUG 22 @AppStorage("developerMode") var developerMode: Bool = true 23 #else 24 @AppStorage("developerMode") var developerMode: Bool = false 25 #endif 26 @AppStorage("logTransactions") var logTransactions: Bool = false 27 @AppStorage("useHaptics") var useHaptics: Bool = true 28 @AppStorage("developDelay") var developDelay: Bool = false 29 @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic 30 @AppStorage("minimalistic") var minimalistic: Bool = false 31 @AppStorage("localConsoleL") var localConsoleL: Bool = false // for Logs 32 @AppStorage("localConsoleO") var localConsoleO: Int = 0 // for Observability 33 @AppStorage("debugViews") var debugViews: Bool = false 34 35 @State private var checkDisabled = false 36 @State private var withDrawDisabled = false 37 @State private var showDevelopItems = false 38 @State private var showResetAlert: Bool = false 39 @State private var didReset: Bool = false 40 41 private var dismissAlertButton: some View { 42 Button("Cancel", role: .cancel) { 43 showResetAlert = false 44 } 45 } 46 private var resetButton: some View { 47 Button("Reset", role: .destructive) { // TODO: WalletColors().errorColor 48 didReset = true 49 showResetAlert = false 50 Task { // runs on MainActor 51 symLog.log("❗️Reset wallet-core❗️") 52 try? await model.resetWalletCore() 53 controller.balances.removeAll() 54 } 55 } 56 } 57 @State private var listID = UUID() 58 59 var body: some View { 60 #if PRINT_CHANGES 61 let _ = Self._printChanges() 62 let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear 63 #endif 64 let walletCore = WalletCore.shared 65 let list = List { 66 SettingsToggle(name: String("Developer Mode"), value: $developerMode, 67 id1: "devMode", 68 description: String("More information intended for debugging")) { newVal in 69 withAnimation(Animation.linear.delay(0.8)) { showDevelopItems = developerMode } 70 } 71 if showDevelopItems { 72 #if DEBUG 73 SettingsToggle(name: String("Log Transactions"), value: $logTransactions.onChange({ isLogging in 74 walletCore.logTransactions = isLogging}), 75 id1: "logTransactions", 76 description: String("full log with all tx")) 77 #endif 78 if #available (iOS 16.0, *) { 79 let localConsStr = String("on LocalConsole") 80 let observability = String("Observe walletCore") 81 SettingsTriState(name: observability, value: $localConsoleO.onChange({ isObserving in 82 walletCore.isObserving = isObserving}), 83 id1: "observe", 84 description: localConsStr) { isObserving in 85 let consoleManager = LCManager.shared 86 consoleManager.isVisible = localConsoleO != 0 || localConsoleL 87 consoleManager.clear() 88 } 89 let showLogs = String("Show logs") 90 SettingsToggle(name: showLogs, value: $localConsoleL.onChange({ isLogging in 91 walletCore.isLogging = isLogging}), 92 id1: "localConsoleL", 93 description: localConsStr) { _ in 94 let consoleManager = LCManager.shared 95 consoleManager.isVisible = localConsoleO != 0 || localConsoleL 96 consoleManager.clear() 97 } 98 } 99 SettingsToggle(name: String(localized: "Show view sizes"), value: $debugViews, 100 id1: "debugViews", 101 description: String(localized: "Debug view layout and flickering")) 102 #if DEBUG 103 let banks = [//"stage.taler-ops.ch", <= never has a bank! 104 // "glstest.taler.net", 105 "taler.fdold.eu", "regio-taler.fdold.eu", 106 "taler.grothoff.org", "taler.ar", 107 "head.taler.net", "test.taler.net", "demo.taler.net", "kyctest.taler.net"] 108 ForEach(banks, id: \.self) { bank in 109 let urlStr = "https://bank." + bank 110 Link(bank, destination: URL(string: urlStr)!) 111 } 112 #endif 113 } // showDevelopItems 114 SettingsItem(name: String("DEMO"), id1: "demo1with", 115 description: String("Get money for testing")) { 116 let title = "Withdraw" 117 Button(title) { 118 withDrawDisabled = true // don't run twice 119 Task { // runs on MainActor 120 symLog.log("Withdraw DEMO KUDOS") 121 let amount = Amount(currency: DEMOCURRENCY, cent: 11100) 122 try? await model.loadTestKudos(0, amount: amount) 123 } 124 } 125 .buttonStyle(.bordered) 126 .disabled(withDrawDisabled) 127 }.id("demo1withdraw") 128 SettingsItem(name: String("TEST"), id1: "test1with", 129 description: String("Get money for testing")) { 130 let title = "Withdraw" 131 Button(title) { 132 withDrawDisabled = true // don't run twice 133 Task { // runs on MainActor 134 symLog.log("Withdraw TESTKUDOS") 135 let cent = UInt64.random(in: 110...195) * 100 136 let amount = Amount(currency: TESTCURRENCY, cent: cent) 137 try? await model.loadTestKudos(1, amount: amount) 138 } 139 } 140 .buttonStyle(.bordered) 141 .disabled(withDrawDisabled) 142 }.id("test1withdraw") 143 if showDevelopItems { 144 SettingsItem(name: String("HEAD"), id1: "head1with", 145 description: String("Get money for testing")) { 146 let title = "Withdraw" 147 Button(title) { 148 withDrawDisabled = true // don't run twice 149 Task { // runs on MainActor 150 symLog.log("Withdraw HEAD KUDOS") 151 let amount = Amount(currency: DEMOCURRENCY, cent: 1100) 152 try? await model.loadTestKudos(2, amount: amount) 153 } 154 } 155 .buttonStyle(.bordered) 156 .disabled(withDrawDisabled) 157 }.id("head1withdraw") 158 SettingsToggle(name: String("Set 2 seconds delay"), 159 value: $developDelay.onChange({ delay in 160 walletCore.developDelay = delay}), 161 id1: "delay", 162 description: String("After each wallet-core action")) 163 .id("delay") 164 #if DEBUG 165 SettingsItem(name: String("Run Dev Experiment Refresh"), 166 id1: "applyDevExperiment", 167 description: "dev-experiment/insert-pending-refresh") { 168 let title = "Refresh" 169 Button(title) { 170 Task { // runs on MainActor 171 symLog.log("running applyDevExperiment Refresh") 172 try await model.setConfig(setTesting: true) 173 try await model.devExperimentT(talerUri: "taler://dev-experiment/start-block-refresh") 174 try await model.devExperimentT(talerUri: "taler://dev-experiment/insert-pending-refresh") 175 } 176 } 177 .buttonStyle(.bordered) 178 }.id("Refresh") 179 #endif 180 SettingsItem(name: String("Run Integration Test"), 181 id1: "demo1test", 182 description: String("Perform basic test transactions")) { 183 let title = "Demo 1" 184 Button(title) { 185 checkDisabled = true // don't run twice 186 Task { // runs on MainActor 187 symLog.log("running integration test on demo") 188 try? await model.runIntegrationTest(newVersion: false, test: false) 189 } 190 } 191 .buttonStyle(.bordered) 192 .disabled(checkDisabled) 193 }.id("demo1runTest") 194 SettingsItem(name: String("Run Integration Test"), 195 id1: "test1test", 196 description: "Perform basic test transactions") { 197 let title = "Test 1" 198 Button(title) { 199 checkDisabled = true // don't run twice 200 Task { // runs on MainActor 201 symLog.log("running integration test on test") 202 try? await model.runIntegrationTest(newVersion: false, test: true) 203 } 204 } 205 .buttonStyle(.bordered) 206 .disabled(checkDisabled) 207 }.id("test1runTest") 208 SettingsItem(name: String("Run Integration Test V2"), 209 id1: "demo2test", 210 description: String("Perform more test transactions")) { 211 let title = "Demo 2" 212 Button(title) { 213 checkDisabled = true // don't run twice 214 Task { // runs on MainActor 215 symLog.log("running integration test V2 on demo") 216 try? await model.runIntegrationTest(newVersion: true, test: false) 217 } 218 } 219 .buttonStyle(.bordered) 220 .disabled(checkDisabled) 221 }.id("demo2runTest") 222 SettingsItem(name: String("Run Integration Test V2"), 223 id1: "test2test", 224 description: String("Perform more test transactions")) { 225 let title = "Test 2" 226 Button(title) { 227 checkDisabled = true // don't run twice 228 Task { // runs on MainActor 229 symLog.log("running integration test V2 on test") 230 try? await model.runIntegrationTest(newVersion: true, test: true) 231 } 232 } 233 .buttonStyle(.bordered) 234 .disabled(checkDisabled) 235 }.id("test2runTest") 236 SettingsItem(name: String("Run Infinite Transaction Loop"), 237 id1: "runInfinite", 238 description: String("Check DB in background")) { 239 let title = "Loop" 240 Button(title) { 241 checkDisabled = true // don't run twice 242 Task { // runs on MainActor 243 symLog.log("Running Infinite Transaction Loop") 244 try? await model.testingInfiniteTransaction(delayMs: 10_000, shouldFetch: true) 245 } 246 } 247 .buttonStyle(.bordered) 248 .disabled(checkDisabled) 249 }.id("runInfiniteLoop") 250 SettingsItem(name: String("Save Logfile"), id1: "save", 251 description: String("Help debugging wallet-core")) { 252 Button("Save") { 253 symLog.log("Saving Log") 254 // FIXME: Save Logfile 255 } 256 .buttonStyle(.bordered) 257 .disabled(true) 258 }.id("saveLog") 259 SettingsItem(name: String("Reset Wallet"), id1: "reset", 260 description: String("Throw away all your money")) { 261 Button("Reset") { 262 showResetAlert = true 263 } 264 .buttonStyle(.bordered) 265 // .disabled(didReset) 266 }.id("resetWallet") 267 } 268 } 269 .id(listID) 270 .listStyle(myListStyle.style).anyView 271 .navigationTitle(navTitle) 272 .onAppear() { 273 showDevelopItems = developerMode 274 DebugViewC.shared.setViewID(VIEW_SETTINGS3, stack: stack.push()) 275 } 276 .onDisappear() { 277 checkDisabled = false // reset 278 withDrawDisabled = false 279 } 280 .alert("Reset Wallet", isPresented: $showResetAlert, 281 actions: { dismissAlertButton 282 resetButton }, 283 message: { Text(verbatim: "Are you sure you want to reset your wallet?\nThis cannot be reverted, all money will be lost.") }) 284 if #available(iOS 26.0, *) { 285 list 286 } else { 287 list 288 .padding(.bottom) 289 } 290 } // body 291 }