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