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