commit 0fced1abeef0671698e34b3659f40c82532a1137 parent 76014e30cad1fbfb3d562637be6246945985aaff Author: Marc Stibane <marc@taler.net> Date: Sat, 31 Oct 2026 14:25:36 +0100 debugging, cleanup Diffstat:
16 files changed, 110 insertions(+), 81 deletions(-)
diff --git a/TalerCommon/HelperViews/CopyShare.swift b/TalerCommon/HelperViews/CopyShare.swift @@ -246,12 +246,15 @@ struct ShareButton: View { } // MARK: - struct CopyShare: View { + let stack: CallStack @Environment(\.isEnabled) private var isEnabled: Bool let textToCopy: String let image: UIImage? - init(_ string: String, image: UIImage? = nil) { + init(_ stack: CallStack, _ string: String, image: UIImage? = nil, + ) { + self.stack = stack self.textToCopy = string self.image = image } @@ -284,6 +287,6 @@ struct CopyShare: View { // MARK: - struct CopyShare_Previews: PreviewProvider { static var previews: some View { - CopyShare("Hallö", image: nil) + CopyShare(CallStack("Preview"), "Hallö", image: nil) } } diff --git a/TalerCommon/HelperViews/CurrencyInputView.swift b/TalerCommon/HelperViews/CurrencyInputView.swift @@ -216,7 +216,7 @@ struct CurrencyInputView: View { } .padding(.vertical, 6) } // iOS 16+ only - }.onAppear { // make CurrencyField show the keyboard after 0.4 seconds + }.onAppear { // make CurrencyField show the keyboard after 0.7 seconds #if OIM let oimModeActive = controller.oimModeActive #else diff --git a/TalerWallet1/Controllers/Controller.swift b/TalerWallet1/Controllers/Controller.swift @@ -138,6 +138,8 @@ class Controller: ObservableObject { @Published var currencyTicker: Int = 0 // updates whenever a new currency is added @Published var userAction: Int = 0 // make Action button jump + @Published var tcDelay: Int = 0 + @Published var fake500: Int = 0 @Published var isConnected: Bool = true @Published var networkUnavailable: Bool = false @Published var slowConnection: Bool = false @@ -158,7 +160,7 @@ class Controller: ObservableObject { @AppStorage("developerMode") var developerMode: Bool = false #endif @AppStorage("deviceTokenAPNs") var deviceTokenAPNs: String? - @AppStorage("developDelay") var developDelay: Bool = false + let hapticCapability = CHHapticEngine.capabilitiesForHardware() let logger = Logger(subsystem: "net.taler.gnu", category: "Controller") let player = AVQueuePlayer() @@ -350,7 +352,7 @@ class Controller: ObservableObject { @discardableResult func loadBalances(_ stack: CallStack) async -> Int? { let model = WalletModel.shared - if let (response, id) = try? await model.getBalances(stack.push()) { + if let (response, debugId) = try? await model.getBalances(stack.push()) { let reloaded = response.balances if reloaded != balances { for balance in reloaded { @@ -358,13 +360,13 @@ class Controller: ObservableObject { checkInfo(for: scope) } #if DEBUG - self.logger.log("\(id) ••Got new balances, will redraw") + self.logger.log("\(debugId) ••Got new balances, will redraw") #endif - lastNewBalances = id + lastNewBalances = debugId balances = reloaded // redraw } else { #if DEBUG - self.logger.log("\(id) ••Same balances as \(self.lastNewBalances), no redraw") + self.logger.log("\(debugId) ••Same balances as \(self.lastNewBalances), no redraw") #endif } haveProdBalance = response.haveProdBalance @@ -584,9 +586,33 @@ class Controller: ObservableObject { currencyInfos[scope] = newInfo currencyTicker += 1 // triggers published view update } + + @MainActor + func setDelayAsync(_ delay: Int, _ f500: Int) async { + let delayTime = delay > 0 ? 3000 + : delay < 0 ? 6000 + : 0 + let fakeValue = delay != 0 ? ( f500 > 0 ? 2 + : f500 < 0 ? 4 + : 0 ) : 0 + symLog.log(delay != 0 ? "wallet-core delaying by \(delayTime) milliseconds, with \(fakeValue) fake_500" + : "wallet-core stop delaying") +// try? await model.setConfig(setTesting: newVal) + tcDelay = delay + fake500 = f500 + let model = WalletModel.shared + try? await model.devExperimentT("taler://dev-experiment/start-tc?fake_500_count=\(fakeValue)&delay_resp=\(delayTime)") + } + + @MainActor + func setDelay(_ delay: Int, _ f500: Int) { + Task { + await setDelayAsync(delay, f500) + } + } // MARK: - @MainActor - func initWalletCore(setTesting: Bool, delay: TimeInterval) + func initWalletCore(stack: CallStack, setTesting: Bool, delay: TimeInterval) async throws { if backendState == .instantiated { backendState = .initing @@ -598,18 +624,11 @@ class Controller: ObservableObject { walletCore.versionInfo = response.versionInfo walletCore.nativeDB = (response.databaseBackend == "sqlite") if developerMode { - // best-effort dev-mode setup: a network hiccup here must not abort - // wallet-core startup (see the force `try!` at the call site) do { - try await model.setConfig(setTesting: true) - if developDelay == true { - try await model.devExperimentT( - "taler://dev-experiment/start-tc?delay_resp=\(TCDELAY)") - } -// try await model.devExperimentT("taler://dev-experiment/start-tc?fake_500=0.7") +// try await model.setConfig(setTesting: true) // try await model.devExperimentT("taler://dev-experiment/default-exchange-demo?val=1") - try await model.devExperimentT( - "taler://dev-experiment/demo-shortcuts?val=KUDOS:4,KUDOS:8,KUDOS:16,KUDOS:32") +// try await model.devExperimentT( +// "taler://dev-experiment/demo-shortcuts?val=KUDOS:4,KUDOS:8,KUDOS:16,KUDOS:32") } catch { self.logger.error("developer-mode setup failed, continuing without it: \(error.localizedDescription)") } @@ -635,21 +654,20 @@ class Controller: ObservableObject { #endif let launchDelay: TimeInterval = walletCore.nativeDB ? 0.1 : delay DispatchQueue.main.asyncAfter(deadline: .now() + launchDelay) { - // dismiss the launch animation + // dismiss the launch animation after delay + self.symLog.log("backendState = ready") self.backendState = walletCore.nativeDB ? .ready : .update } - await loadBalances(CallStack()) - } catch { // rethrows +// await loadBalances(stack.push()) + } catch { self.logger.error("\(error.localizedDescription)") backendState = .error(error.toEquatableError()) // ❗️Yikes app cannot continue -// throw error // will not be catched by MainView.task anyway } } else { self.logger.fault("Yikes❗️ wallet-core already initialized") } } } - // MARK: - extension Controller { func urlCommand(_ url: URL, stack: CallStack) -> (UrlCommand, URL) { @@ -680,13 +698,14 @@ extension Controller { // MARK: - extension Controller { func httpsScheme(_ url: URL) -> (UrlCommand, URL) { - // https://wallet.taler.net/#taler://${action}/${rest} - // https://wallet.taler.net/#/${action}/${rest} - // https://wallet.taler.net/#${action}/${rest} + // https://wallet.taler.net/link/#taler://${action}/${rest} + // https://wallet.taler.net/link/#/${action}/${rest} + // https://wallet.taler.net/link/#${action}/${rest} // are all mapped to taler://${action}/${rest} if let host = url.host, host == "wallet.taler.net" { if url.path == "/link" { if let fragment = url.fragment { + // 'fragment(percentEncoded:)' is only available in iOS 16.0 or newer var split = fragment.split(separator: "/") if split.first == "taler:" { split.removeFirst() diff --git a/TalerWallet1/Controllers/PublicConstants.swift b/TalerWallet1/Controllers/PublicConstants.swift @@ -19,11 +19,6 @@ let TALER_LOGO_QR = "taler-logo-qr" let EXCHANGE_LOGO = "Exchange-logo-blue" let SWISS_QR = "swiss_qr" -#if DEBUG -public let TCDELAY = 12000 -#else -public let TCDELAY = 5000 // 5 seconds delay after each network call -#endif public let INVISIBLE = 0.001 public let MAXEXCHANGES = 1000 // per currency public let CHF_4217 = "CHF" // ISO-4217 Swiss Francs diff --git a/TalerWallet1/Controllers/TalerWallet1App.swift b/TalerWallet1/Controllers/TalerWallet1App.swift @@ -16,6 +16,7 @@ import SymLog @main struct TalerWallet1App: App { #if TALER_NIGHTLY + // we need an AppDelegate for APNs @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate #endif private let symLog = SymLogV(-1, Bundle.main.bundleName) @@ -32,6 +33,7 @@ struct TalerWallet1App: App { @StateObject private var viewState2 = ViewState2.shared // popToRootView() @State private var pastedString: String? = nil @State private var pastedWebURL: String? = nil // TODO: debounce + @State private var stack = CallStack("App") private let walletCore = WalletCore.shared private let controller = Controller.shared @@ -106,7 +108,7 @@ struct TalerWallet1App: App { let colorScheme = preferredColorScheme > 1 ? ColorScheme.dark : preferredColorScheme > 0 ? ColorScheme.light : nil // use the current scheme - let mainView = MainView(logger: logger, stack: CallStack("App")) + let mainView = MainView(logger: logger, stack: stack.push()) .preferredColorScheme(colorScheme) .environmentObject(debugViewC) // change viewID / sheetID .environmentObject(viewState) // popToRoot @@ -122,12 +124,14 @@ struct TalerWallet1App: App { .task { #if DEBUG let testing = true - let delay: TimeInterval = 0.001 + let delay: TimeInterval = 1.001 #else let testing = false let delay: TimeInterval = 2.25 #endif - try? await controller.initWalletCore(setTesting: testing, delay: delay) + try? await controller.initWalletCore(stack: stack.push("mainView.task"), + setTesting: testing, + delay: delay) } if #available(iOS 16.4, *) { return WindowGroup { @@ -150,7 +154,7 @@ struct TalerWallet1App: App { } #if TALER_NIGHTLY .backgroundTask(.appRefresh("net.taler.refresh")) { - symLog.log("backgroundTask running") + await symLog.log("backgroundTask running") } #endif } else { diff --git a/TalerWallet1/Model/Transaction.swift b/TalerWallet1/Model/Transaction.swift @@ -622,7 +622,7 @@ struct P2PTransactionDetails: Codable, Sendable { var talerUri: String? // only if we initiated the transaction var info: P2pShortInfo } -struct P2PTransaction : Sendable { +struct P2PTransaction: Sendable { var common: TransactionCommon var details: P2PTransactionDetails } @@ -630,7 +630,7 @@ struct P2PTransaction : Sendable { struct RecoupTransactionDetails: Decodable { var recoupReason: String? } -struct RecoupTransaction : Sendable { +struct RecoupTransaction: Sendable { var common: TransactionCommon var details: RecoupTransactionDetails } diff --git a/TalerWallet1/Model/WalletModel.swift b/TalerWallet1/Model/WalletModel.swift @@ -686,15 +686,13 @@ extension WalletModel { } } // MARK: - -fileprivate struct DevExperimentRequest: WalletBackendFormattedRequest { - var operation: String { "applyDevExperiment" } - func args() -> Args { Args(devExperimentUri: talerUri) } - +fileprivate struct ApplyDevExperiment: WalletBackendFormattedRequest { var talerUri: String - + var operation: String { "applyDevExperiment" } struct Args: Encodable { var devExperimentUri: String } + func args() -> Args { Args(devExperimentUri: talerUri) } struct Response: Decodable {} } @@ -702,7 +700,7 @@ extension WalletModel { /// tell wallet-core to mock new transactions nonisolated func devExperimentT(_ talerUri: String, viewHandles: Bool = false) async throws { // T for any Thread - let request = DevExperimentRequest(talerUri: talerUri) + let request = ApplyDevExperiment(talerUri: talerUri) _ = try await sendRequest(request, viewHandles: viewHandles) } } diff --git a/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift b/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift @@ -10,10 +10,12 @@ import taler_swift import AVFoundation struct QRCodeDetailView: View { + let stack: CallStack let talerURI: String let incoming: Bool let amount: Amount let scope: ScopeInfo? + let subject: String? @EnvironmentObject private var controller: Controller @AppStorage("minimalistic") var minimalistic: Bool = false @@ -95,7 +97,7 @@ struct QRCodeDetailView: View { withAnimation { showQRcode = true } }.buttonStyle(TalerButtonStyle(type: .prominent)) } - CopyShare(talerCopyShare, image: qrImage) + CopyShare(stack.push(), talerCopyShare, image: qrImage) .disabled(false) // .padding(.bottom) .listRowSeparator(.hidden) diff --git a/TalerWallet1/Views/Main/MainView.swift b/TalerWallet1/Views/Main/MainView.swift @@ -170,8 +170,8 @@ struct MainView: View { #if TALER_NIGHTLY var showScanner: Bool { sheetType == .scanner } var showUrlSheet: Bool { sheetType != nil && sheetType != .scanner && sheetType != .action } - #endif + var body: some View { #if PRINT_CHANGES let _ = Self._printChanges() diff --git a/TalerWallet1/Views/Main/WalletEmptyView.swift b/TalerWallet1/Views/Main/WalletEmptyView.swift @@ -91,8 +91,8 @@ struct ProdSectionView: View { let currency = defaultExchange.currency let title = String(localized: "LinkTitle_Withdraw", defaultValue: "Withdraw \(currency)") Button(title) { - if let talerUri = URL(string: defaultExchange.talerUri) { - controller.talerURI = talerUri + if let talerURI = URL(string: defaultExchange.talerUri) { + controller.talerURI = talerURI } } .buttonStyle(TalerButtonStyle(type: isEmpty ? .prominent : .bordered, diff --git a/TalerWallet1/Views/Settings/DebugSettingsView.swift b/TalerWallet1/Views/Settings/DebugSettingsView.swift @@ -25,7 +25,6 @@ struct DebugSettingsView: View { #endif @AppStorage("logTransactions") var logTransactions: Bool = false @AppStorage("useHaptics") var useHaptics: Bool = true - @AppStorage("developDelay") var developDelay: Bool = false @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic @AppStorage("minimalistic") var minimalistic: Bool = false @AppStorage("localConsoleL") var localConsoleL: Bool = false // for Logs @@ -56,13 +55,6 @@ struct DebugSettingsView: View { } @State private var listID = UUID() - func setDelay(_ doDelay: Bool, for walletCore: WalletCore) async { - symLog.log(doDelay ? "wallet-core delaying by 5 seconds" - : "wallet-core stop delaying") - let delayTime = doDelay ? TCDELAY : 0 - try? await model.devExperimentT("taler://dev-experiment/start-tc?delay_resp=\(delayTime)") - } - var body: some View { #if PRINT_CHANGES let _ = Self._printChanges() @@ -74,15 +66,34 @@ struct DebugSettingsView: View { id1: "devMode", description: String("More information intended for debugging")) { newVal in withAnimation(Animation.linear.delay(0.8)) { showDevelopItems = developerMode } - Task { - symLog.log((newVal ? "enabling" : "disabling") + " applyDevExperiment") - try? await model.setConfig(setTesting: newVal) - if newVal && developDelay == true { - await setDelay(true, for: walletCore) - } - } + symLog.log((newVal ? "enabling" : "disabling") + " applyDevExperiment") + controller.setDelay(newVal ? controller.tcDelay : 0, controller.fake500) } if showDevelopItems { + let doDelay = String("WalletCore delay") + let delay = controller.tcDelay + SettingsTriState(name: doDelay, + value: $controller.tcDelay, + id1: "delay", + description: delay > 0 ? String("3s after each network call") + : delay < 0 ? String("6s after each network call") + : String("After each network call")) + { delay in + controller.setDelay(delay, controller.fake500) + } + if controller.tcDelay != 0 { + let doFake = String("fake html 500") + let fake = controller.fake500 + SettingsTriState(name: doFake, + value: $controller.fake500, + id1: "fake500", + description: fake > 0 ? String("2 after each network call") + : fake < 0 ? String("4 after each network call") + : String("After each network call")) + { fake in + controller.setDelay(controller.tcDelay, fake) + } + } #if DEBUG SettingsToggle(name: String("Log Transactions"), value: $logTransactions, id1: "logTransactions", @@ -111,15 +122,7 @@ struct DebugSettingsView: View { consoleManager.clear() } } - SettingsToggle(name: String("Set 5 seconds delay"), - value: $developDelay.onChange({ delay in - Task { - try await setDelay(delay, for: walletCore) - } - }), - id1: "delay", - description: String("After each network call")) - .id("delay") + SettingsToggle(name: String(localized: "Show view sizes"), value: $debugViews, id1: "debugViews", description: String(localized: "Debug view layout and flickering")) diff --git a/TalerWallet1/Views/Settings/ReportView.swift b/TalerWallet1/Views/Settings/ReportView.swift @@ -61,7 +61,7 @@ struct ReportView: View { .onTapGesture { expanded.toggle() } - CopyShare(report, image: nil) + CopyShare(stack.push(), report) } } .listRowSeparator(.hidden) diff --git a/TalerWallet1/Views/Transactions/ManualDetailsV.swift b/TalerWallet1/Views/Transactions/ManualDetailsV.swift @@ -145,7 +145,7 @@ struct WireTransferView: View { } else if countB > 0 { // show the single QR directly Text("If your banking software runs on another device, you can scan this QR code:") .listRowSeparator(.hidden) - QRcodeCopyShare(option: firstOption) + QRcodeCopyShare(stack: stack.push(), option: firstOption) .listRowSeparator(.automatic) } }.id(listID) diff --git a/TalerWallet1/Views/Transactions/QRcodesForPayto.swift b/TalerWallet1/Views/Transactions/QRcodesForPayto.swift @@ -10,6 +10,7 @@ import OrderedCollections import taler_swift struct QRcodeCopyShare: View { + let stack: CallStack let option: TransferOption @State private var qrImage: UIImage? = nil @@ -46,7 +47,7 @@ struct QRcodeCopyShare: View { // .accessibilityHidden(true) // .foregroundColor(Color.clear) // Spacer() - CopyShare(textToShare, image: qrImage) + CopyShare(stack.push(), textToShare, image: qrImage) .disabled(false) } } @@ -68,7 +69,7 @@ struct QRcodesForPayto: View { .listRowSeparator(.hidden) } ForEach(transferOptions, id: \.self) { option in - QRcodeCopyShare(option: option) + QRcodeCopyShare(stack: stack.push(), option: option) .listRowSeparator(.automatic) } } diff --git a/TalerWallet1/Views/Transactions/TransactionSummaryList.swift b/TalerWallet1/Views/Transactions/TransactionSummaryList.swift @@ -514,17 +514,21 @@ struct TransactionSummaryList: View { } // MARK: - struct QRCodeDetails: View { + let stack: CallStack var transaction : TalerTransaction var body: some View { let details = transaction.detailsToShow() let keys = details.keys if keys.contains(TALERURI) { if let talerURI = details[TALERURI] { + let summary = details["summary"] if talerURI.count > 10 { - QRCodeDetailView(talerURI: talerURI, - incoming: transaction.isP2pIncoming, - amount: transaction.common.amountRaw, - scope: transaction.common.scopes.first) + QRCodeDetailView(stack: stack.push(), + talerURI: talerURI, + incoming: transaction.isP2pIncoming, + amount: transaction.common.amountRaw, + scope: transaction.common.scopes.first, + subject: summary) // scopes shouldn't (- but might) be nil! } } diff --git a/TalerWallet1/Views/Transactions/TransactionTypeDetail.swift b/TalerWallet1/Views/Transactions/TransactionTypeDetail.swift @@ -252,7 +252,7 @@ struct TransactionTypeDetail: View { // TODO: details.info.summary if pending { if transaction.isPendingReady { - QRCodeDetails(transaction: transaction) + QRCodeDetails(stack: stack.push(), transaction: transaction) if hasDone { Text("QR code and link can also be scanned or copied / shared from Transactions later.") .multilineTextAlignment(.leading)