DebugSettingsView.swift (14194B)
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 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 Group { 65 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 let localConsStr = String("on LocalConsole") 79 let observability = String("Observe walletCore") 80 SettingsTriState(name: observability, value: $localConsoleO.onChange({ isObserving in 81 walletCore.isObserving = isObserving}), 82 id1: "observe", 83 description: localConsStr) { isObserving in 84 let consoleManager = LCManager.shared 85 consoleManager.isVisible = localConsoleO != 0 || localConsoleL 86 consoleManager.clear() 87 } 88 let showLogs = String("Show logs") 89 SettingsToggle(name: showLogs, value: $localConsoleL.onChange({ isLogging in 90 walletCore.isLogging = isLogging}), 91 id1: "localConsoleL", 92 description: localConsStr) { _ in 93 let consoleManager = LCManager.shared 94 consoleManager.isVisible = localConsoleO != 0 || localConsoleL 95 consoleManager.clear() 96 } 97 #if DEBUG 98 let banks = ["glstest.taler.net", "taler.fdold.eu", "regio-taler.fdold.eu", 99 "taler.grothoff.org", "taler.ar", 100 "head.taler.net", "test.taler.net", "demo.taler.net", "kyctest.taler.net"] 101 ForEach(banks, id: \.self) { bank in 102 let urlStr = "https://bank." + bank 103 Link(bank, destination: URL(string: urlStr)!) 104 } 105 #endif 106 } // showDevelopItems 107 SettingsItem(name: String("DEMO"), id1: "demo1with", 108 description: String("Get money for testing")) { 109 let title = "Withdraw" 110 Button(title) { 111 withDrawDisabled = true // don't run twice 112 Task { // runs on MainActor 113 symLog.log("Withdraw DEMO KUDOS") 114 let amount = Amount(currency: DEMOCURRENCY, cent: 11100) 115 try? await model.loadTestKudos(0, amount: amount) 116 } 117 } 118 .buttonStyle(.bordered) 119 .disabled(withDrawDisabled) 120 }.id("demo1withdraw") 121 SettingsItem(name: String("TEST"), id1: "test1with", 122 description: String("Get money for testing")) { 123 let title = "Withdraw" 124 Button(title) { 125 withDrawDisabled = true // don't run twice 126 Task { // runs on MainActor 127 symLog.log("Withdraw TESTKUDOS") 128 let cent = UInt64.random(in: 110...195) * 100 129 let amount = Amount(currency: TESTCURRENCY, cent: cent) 130 try? await model.loadTestKudos(1, amount: amount) 131 } 132 } 133 .buttonStyle(.bordered) 134 .disabled(withDrawDisabled) 135 }.id("test1withdraw") 136 if showDevelopItems { 137 SettingsItem(name: String("HEAD"), id1: "head1with", 138 description: String("Get money for testing")) { 139 let title = "Withdraw" 140 Button(title) { 141 withDrawDisabled = true // don't run twice 142 Task { // runs on MainActor 143 symLog.log("Withdraw HEAD KUDOS") 144 let amount = Amount(currency: DEMOCURRENCY, cent: 1100) 145 try? await model.loadTestKudos(2, amount: amount) 146 } 147 } 148 .buttonStyle(.bordered) 149 .disabled(withDrawDisabled) 150 }.id("head1withdraw") 151 SettingsToggle(name: String("Set 2 seconds delay"), 152 value: $developDelay.onChange({ delay in 153 walletCore.developDelay = delay}), 154 id1: "delay", 155 description: String("After each wallet-core action")) 156 .id("delay") 157 #if DEBUG 158 SettingsItem(name: String("Run Dev Experiment Refresh"), 159 id1: "applyDevExperiment", 160 description: "dev-experiment/insert-pending-refresh") { 161 let title = "Refresh" 162 Button(title) { 163 Task { // runs on MainActor 164 symLog.log("running applyDevExperiment Refresh") 165 try? await model.setConfig(setTesting: true) 166 try? await model.devExperimentT(talerUri: "taler://dev-experiment/start-block-refresh") 167 try? await model.devExperimentT(talerUri: "taler://dev-experiment/insert-pending-refresh") 168 } 169 } 170 .buttonStyle(.bordered) 171 }.id("Refresh") 172 #endif 173 SettingsItem(name: String("Run Integration Test"), 174 id1: "demo1test", 175 description: String("Perform basic test transactions")) { 176 let title = "Demo 1" 177 Button(title) { 178 checkDisabled = true // don't run twice 179 Task { // runs on MainActor 180 symLog.log("running integration test on demo") 181 try? await model.runIntegrationTest(newVersion: false, test: false) 182 } 183 } 184 .buttonStyle(.bordered) 185 .disabled(checkDisabled) 186 }.id("demo1runTest") 187 SettingsItem(name: String("Run Integration Test"), 188 id1: "test1test", 189 description: "Perform basic test transactions") { 190 let title = "Test 1" 191 Button(title) { 192 checkDisabled = true // don't run twice 193 Task { // runs on MainActor 194 symLog.log("running integration test on test") 195 try? await model.runIntegrationTest(newVersion: false, test: true) 196 } 197 } 198 .buttonStyle(.bordered) 199 .disabled(checkDisabled) 200 }.id("test1runTest") 201 SettingsItem(name: String("Run Integration Test V2"), 202 id1: "demo2test", 203 description: String("Perform more test transactions")) { 204 let title = "Demo 2" 205 Button(title) { 206 checkDisabled = true // don't run twice 207 Task { // runs on MainActor 208 symLog.log("running integration test V2 on demo") 209 try? await model.runIntegrationTest(newVersion: true, test: false) 210 } 211 } 212 .buttonStyle(.bordered) 213 .disabled(checkDisabled) 214 }.id("demo2runTest") 215 SettingsItem(name: String("Run Integration Test V2"), 216 id1: "test2test", 217 description: String("Perform more test transactions")) { 218 let title = "Test 2" 219 Button(title) { 220 checkDisabled = true // don't run twice 221 Task { // runs on MainActor 222 symLog.log("running integration test V2 on test") 223 try? await model.runIntegrationTest(newVersion: true, test: true) 224 } 225 } 226 .buttonStyle(.bordered) 227 .disabled(checkDisabled) 228 }.id("test2runTest") 229 SettingsItem(name: String("Run Infinite Transaction Loop"), 230 id1: "runInfinite", 231 description: String("Check DB in background")) { 232 let title = "Loop" 233 Button(title) { 234 checkDisabled = true // don't run twice 235 Task { // runs on MainActor 236 symLog.log("Running Infinite Transaction Loop") 237 try? await model.testingInfiniteTransaction(delayMs: 10_000, shouldFetch: true) 238 } 239 } 240 .buttonStyle(.bordered) 241 .disabled(checkDisabled) 242 }.id("runInfiniteLoop") 243 SettingsItem(name: String("Save Logfile"), id1: "save", 244 description: String("Help debugging wallet-core")) { 245 Button("Save") { 246 symLog.log("Saving Log") 247 // FIXME: Save Logfile 248 } 249 .buttonStyle(.bordered) 250 .disabled(true) 251 }.id("saveLog") 252 SettingsItem(name: String("Reset Wallet"), id1: "reset", 253 description: String("Throw away all your money")) { 254 Button("Reset") { 255 showResetAlert = true 256 } 257 .buttonStyle(.bordered) 258 // .disabled(didReset) 259 }.id("resetWallet") 260 } 261 } 262 .padding(.bottom) 263 .id(listID) 264 .listStyle(myListStyle.style).anyView 265 } 266 .navigationTitle(navTitle) 267 .onAppear() { 268 showDevelopItems = developerMode 269 DebugViewC.shared.setViewID(VIEW_SETTINGS, stack: stack.push()) 270 } 271 .onDisappear() { 272 checkDisabled = false // reset 273 withDrawDisabled = false 274 } 275 .alert("Reset Wallet", 276 isPresented: $showResetAlert, 277 actions: { dismissAlertButton 278 resetButton }, 279 message: { Text(verbatim: "Are you sure you want to reset your wallet?\nThis cannot be reverted, all money will be lost.") }) 280 281 } // body 282 }