taler-ios

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

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 }