taler-ios

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

commit 52d5e6a5dae2dd485e4115d6a276e4cf44e01423
parent 27410a89b54d116b1f09120b95d2aef82af69d20
Author: Marc Stibane <marc@taler.net>
Date:   Sat, 28 Jun 2025 01:44:19 +0200

(Don't) Show QR automatically

Diffstat:
MTalerWallet1/Controllers/PublicConstants.swift | 3++-
MTalerWallet1/Views/HelperViews/GradientBorder.swift | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
MTalerWallet1/Views/HelperViews/QRCodeDetailView.swift | 43++++++++++++++++++++++++++-----------------
MTalerWallet1/Views/Settings/AboutView.swift | 2+-
MTalerWallet1/Views/Settings/SettingsView.swift | 33+++++++++++++++++++++++----------
5 files changed, 117 insertions(+), 50 deletions(-)

diff --git a/TalerWallet1/Controllers/PublicConstants.swift b/TalerWallet1/Controllers/PublicConstants.swift @@ -47,7 +47,8 @@ public let UNKNOWN = String(localized: "UNKNOWN", comment: "merchant didn't spec public let BALANCES = "chart.bar.xaxis" // 􀣉 2.0 (iOS 14) public let SETTINGS = "gear" // 􀍟 1.0 (iOS 13) public let QRBUTTON = "qrcode.viewfinder" // 􀎻 1.0 (iOS 13) -public let NFCLOGO = "wave.3.right.circle" // +public let QRCODE = "qrcode" // 􀖂 1.0 (iOS 13) +public let NFCLOGO = "wave.3.right.circle" // 􀭹 2.0 (iOS 14) public let CONFIRM_BANK = "circle.fill" // 􀀁 badge in PendingRow, TransactionRow and TransactionSummary public let NEEDS_KYC = "star.fill" // 􀋃 badge in PendingRow, TransactionRow and TransactionSummary diff --git a/TalerWallet1/Views/HelperViews/GradientBorder.swift b/TalerWallet1/Views/HelperViews/GradientBorder.swift @@ -29,43 +29,87 @@ struct BorderWithNFC<Content: View>: View { let talerURI: String let nfcHint: Bool let size: CGFloat + let scanHints: (String, String)? var content: () -> Content @AppStorage("minimalistic") var minimalistic: Bool = false + @AppStorage("showQRauto16") var showQRauto16: Bool = true + @AppStorage("showQRauto17") var showQRauto17: Bool = false @StateObject private var tagEmulation = TagEmulation.shared + @State private var showQRcode = false var body: some View { - let _ = Self._printChanges() +// let _ = Self._printChanges() + let qrCode = Image(systemName: QRCODE) // 􀖂 "qrcode" + let qrButton = Button(minimalistic ? "\(qrCode)" : "\(qrCode) Show QR code") { + withAnimation { showQRcode = true } + } + + let hint = Group { + if let scanHints { + Text(scanHints.0) + .accessibilityLabel(scanHints.1) + .multilineTextAlignment(.leading) + .talerFont(.title3) + } + } + if tagEmulation.canUseHCE { + let nfcLogo = Image(systemName: NFCLOGO) // 􀭹 "wave.3.right.circle" + let nfcButton = Button(minimalistic ? "\(nfcLogo)" : "\(nfcLogo) Start NFC") { + tagEmulation.emulateTag(talerURI) + }.buttonStyle(TalerButtonStyle(type: .prominent)) + VStack { if nfcHint { - if !minimalistic { - let nfcLogo = Image(systemName: NFCLOGO) // 􀎻 "qrcode.viewfinder" - Text("\(nfcLogo) Tap for NFC") - .talerFont(.subheadline) - .padding(.vertical, -4) + if showQRcode { + if !minimalistic { + Text("\(nfcLogo) Tap for NFC") + .talerFont(.subheadline) + .padding(.vertical, -4) + } + let screenWidth = UIScreen.screenWidth + GradientBorder(size: size + 20.0, + color: .accentColor, + background: WalletColors().backgroundColor) + { + content() + .onTapGesture(count: 1) { tagEmulation.emulateTag(talerURI) } + } + hint + } else { + nfcButton + qrButton.buttonStyle(TalerButtonStyle(type: .bordered)) } - let screenWidth = UIScreen.screenWidth - GradientBorder(size: size + 20.0, - color: .accentColor, - background: WalletColors().backgroundColor) - { + } else { + if showQRcode { content() - .onTapGesture(count: 1) { - tagEmulation.emulateTag(talerURI) - } + .onTapGesture(count: 2) { tagEmulation.emulateTag(talerURI) } + hint + } else { + nfcButton + qrButton.buttonStyle(TalerButtonStyle(type: .bordered)) } - } else { - content() - .onTapGesture(count: 2) { - tagEmulation.emulateTag(talerURI) - } } - }.onDisappear() { + }.listRowSeparator(.hidden) + .onDisappear() { tagEmulation.killEmulation() + }.task { + withAnimation { + if #available(iOS 17.7, *) { + showQRcode = showQRauto17 + } else { + showQRcode = showQRauto16 + } + } } } else { - content() + if showQRcode { + content() + hint + } else { + qrButton.buttonStyle(TalerButtonStyle(type: .prominent)) + } } } } diff --git a/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift b/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift @@ -18,7 +18,10 @@ struct QRCodeDetailView: View { @EnvironmentObject private var controller: Controller @AppStorage("minimalistic") var minimalistic: Bool = false + @AppStorage("showQRauto16") var showQRauto16: Bool = true + @AppStorage("showQRauto17") var showQRauto17: Bool = false + @State private var showQRcode = false @State private var currencyInfo: CurrencyInfo? private func currencyTickerChanged() async { @@ -60,35 +63,41 @@ struct QRCodeDetailView: View { var body: some View { if talerURI.count > 10 { Section { + let amountStr = amountStr(currencyInfo) + let scanLong = incoming ? (requesting(amountStr.0), requesting(amountStr.1)) + : (sending(amountStr.0), sending(amountStr.1)) let size = 240.0 let qrView = QRGeneratorView(text: talerURI, size: size) .frame(maxWidth: .infinity, alignment: .center) - Group { - if #available(iOS 17.7, *) { - BorderWithNFC(talerURI: talerURI, nfcHint: true, size: size) { - qrView - } - } else { + .accessibilityLabel(Text("QR Code", comment: "a11y")) + if #available(iOS 17.7, *) { + BorderWithNFC(talerURI: talerURI, nfcHint: true, size: size, scanHints: scanLong) { qrView } + } else if showQRcode { + qrView + } else { + let qrCode = Image(systemName: QRCODE) // 􀖂 "qrcode" + Button(minimalistic ? "\(qrCode)" : "\(qrCode) Show QR code") { + withAnimation { showQRcode = true } + }.buttonStyle(TalerButtonStyle(type: .prominent)) } - .accessibilityLabel(Text("QR Code", comment: "a11y")) - .listRowSeparator(.hidden) - let amountStr = amountStr(currencyInfo) - let scanLong = incoming ? (requesting(amountStr.0), requesting(amountStr.1)) - : (sending(amountStr.0), sending(amountStr.1)) - Text(scanLong.0) - .accessibilityLabel(scanLong.1) - .multilineTextAlignment(.leading) - .talerFont(.title3) - .listRowSeparator(.hidden) - .task(id: controller.currencyTicker) { await currencyTickerChanged() } CopyShare(textToCopy: talerCopyShare) .disabled(false) // .padding(.bottom) .listRowSeparator(.hidden) } + .task(id: controller.currencyTicker) { await currencyTickerChanged() } + .task { + withAnimation { + if #available(iOS 17.7, *) { + showQRcode = showQRauto17 + } else { + showQRcode = showQRauto16 + } + } + } } } } diff --git a/TalerWallet1/Views/Settings/AboutView.swift b/TalerWallet1/Views/Settings/AboutView.swift @@ -47,7 +47,7 @@ struct AboutView: View { List { if #available(iOS 17.7, *) { let talerURI = TALER_NET - BorderWithNFC(talerURI: talerURI, nfcHint: false, size: size) { + BorderWithNFC(talerURI: talerURI, nfcHint: false, size: size, scanHints: nil) { rotatingTaler } } else { diff --git a/TalerWallet1/Views/Settings/SettingsView.swift b/TalerWallet1/Views/Settings/SettingsView.swift @@ -45,6 +45,8 @@ struct SettingsView: View { @AppStorage("localConsoleL") var localConsoleL: Bool = false // for Logs @AppStorage("localConsoleO") var localConsoleO: Int = 0 // for Observability @AppStorage("useAuthentication") var useAuthentication: Bool = false + @AppStorage("showQRauto16") var showQRauto16: Bool = true + @AppStorage("showQRauto17") var showQRauto17: Bool = false @State private var checkDisabled = false @State private var withDrawDisabled = false @@ -121,8 +123,17 @@ struct SettingsView: View { SettingsItem(name: bankAccountsTitle, id1: "bankAccounts", description: hideDescriptions ? nil : String(localized: "Your accounts for deposit...")) {} } + let showQRstring = String(localized: "Show QR codes") + let showQRhint = String(localized: "Automatically for P2P transactions") + if #available(iOS 17.7, *) { + SettingsToggle(name: showQRstring, value: $showQRauto17, id1: "showQRautomatic", + description: minimalistic ? nil : showQRhint) {} + } else { + SettingsToggle(name: showQRstring, value: $showQRauto16, id1: "showQRautomatic", + description: minimalistic ? nil : showQRhint) {} + } SettingsToggle(name: String(localized: "Use FaceID / TouchID"), value: $useAuthentication, id1: "useFaceID", - description: minimalistic ? nil : String(localized: "Protect your money")) { + description: minimalistic ? nil : String(localized: "Protect your money")) { biometricService.isAuthenticated = false } #if TALER_NIGHTLY || DEBUG @@ -133,11 +144,11 @@ struct SettingsView: View { backupDest } label: { SettingsItem(name: backupTitle, id1: "backup", - description: hideDescriptions ? nil : String(localized: "Backup your money...")) {} + description: hideDescriptions ? nil : String(localized: "Backup your money...")) {} } #endif SettingsToggle(name: String(localized: "Minimalistic"), value: $minimalistic, id1: "minimal", - description: hideDescriptions ? nil : String(localized: "Omit text where possible")) { + description: hideDescriptions ? nil : String(localized: "Omit text where possible")) { hideDescriptions = minimalistic //withAnimation { hideDescriptions = minimalistic } } if controller.hapticCapability.supportsHaptics { @@ -145,7 +156,7 @@ struct SettingsView: View { description: hideDescriptions ? nil : String(localized: "Vibration Feedback")) } SettingsToggle(name: String(localized: "Play Payment Sounds"), value: $playSoundsB, id1: "playSounds", - description: hideDescriptions ? nil : String(localized: "When a transaction finished")) + description: hideDescriptions ? nil : String(localized: "When a transaction finished")) SettingsToggle(name: String(localized: "Show Warnings"), value: $shouldShowWarning, id1: "warnings", description: hideDescriptions ? nil : String(localized: "For Delete, Abandon & Abort buttons")) // SettingsFont(title: String(localized: "Font:"), value: talerFontIndex, action: redraw) @@ -156,7 +167,7 @@ struct SettingsView: View { let observability = String(localized: "Observe walletCore") SettingsTriState(name: observability, value: $localConsoleO.onChange({ isObserving in walletCore.isObserving = isObserving}), - description: hideDescriptions ? nil : localConsStr) { isObserving in + description: hideDescriptions ? nil : localConsStr) { isObserving in let consoleManager = LCManager.shared consoleManager.isVisible = localConsoleO != 0 || localConsoleL consoleManager.clear() @@ -170,7 +181,7 @@ struct SettingsView: View { let showLogs = String(localized: "Show logs") SettingsToggle(name: showLogs, value: $localConsoleL.onChange({ isLogging in walletCore.isLogging = isLogging}), id1: "localConsoleL", - description: hideDescriptions ? nil : localConsStr) { + description: hideDescriptions ? nil : localConsStr) { let consoleManager = LCManager.shared consoleManager.isVisible = localConsoleO != 0 || localConsoleL consoleManager.clear() @@ -181,7 +192,8 @@ struct SettingsView: View { } #if DEBUG if showDevelopItems { - let banks = ["taler.fdold.eu", "regio-taler.fdold.eu", "taler.grothoff.org", "taler.ar", + let banks = ["glstest.taler.net", "glsint.fdold.eu", "taler.fdold.eu", "regio-taler.fdold.eu", + "taler.grothoff.org", "taler.ar", "head.taler.net", "test.taler.net", "demo.taler.net", "kyctest.taler.net"] ForEach(banks, id: \.self) { bank in let urlStr = "https://bank." + bank @@ -211,7 +223,8 @@ struct SettingsView: View { withDrawDisabled = true // don't run twice Task { // runs on MainActor symLog.log("Withdraw TESTKUDOS") - let amount = Amount(currency: TESTCURRENCY, cent: 1100) + let cent = UInt64.random(in: 110...195) * 100 + let amount = Amount(currency: DEMOCURRENCY, cent: cent) try? await model.loadTestKudos(1, amount: amount) } } @@ -219,7 +232,7 @@ struct SettingsView: View { .disabled(withDrawDisabled) }.id("test1withdraw") SettingsItem(name: String("HEAD"), id1: "head1with", - description: hideDescriptions ? nil : String("Get money for testing")) { + description: hideDescriptions ? nil : String("Get money for testing")) { let title = "Withdraw" Button(title) { withDrawDisabled = true // don't run twice @@ -333,7 +346,7 @@ struct SettingsView: View { showResetAlert = true } .buttonStyle(.bordered) - .disabled(didReset) +// .disabled(didReset) }.id("resetWallet") } }