taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

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 }