taler-ios

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

commit d42dbe420faaca8ea921f2f6b06cc08a0992febe
parent fbd3c0b37db3482b85b1553d4decc5ca95832157
Author: Marc Stibane <marc@taler.net>
Date:   Sun, 15 Jun 2025 08:57:11 +0200

Backup

Diffstat:
MTalerWallet1/Controllers/DebugViewC.swift | 1+
ATalerWallet1/Views/Settings/BackupView.swift | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 135 insertions(+), 0 deletions(-)

diff --git a/TalerWallet1/Controllers/DebugViewC.swift b/TalerWallet1/Controllers/DebugViewC.swift @@ -31,6 +31,7 @@ public let VIEW_SETTINGS = VIEW_BALANCES + 1 // 12 Settin public let VIEW_ABOUT = VIEW_SETTINGS + 1 // 13 AboutView public let VIEW_PAYMENT_SERVICES = VIEW_ABOUT + 1 // 14 ExchangeListView public let VIEW_BANK_ACCOUNTS = VIEW_PAYMENT_SERVICES + 1 // 15 BankListView +public let VIEW_BACKUP = VIEW_BANK_ACCOUNTS + 1 // 16 BackupView // MARK: Transactions public let VIEW_EMPTY_HISTORY = VIEW_EMPTY_WALLET + 10 // 20 TransactionsEmptyView diff --git a/TalerWallet1/Views/Settings/BackupView.swift b/TalerWallet1/Views/Settings/BackupView.swift @@ -0,0 +1,134 @@ +/* + * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. + * See LICENSE.md + */ +/** + * @author Marc Stibane + */ +import SwiftUI +import taler_swift +import SymLog + +let PREFIX = "Taler-" + +/// This view shows the list of backups +struct BackupView: View { + private let symLog = SymLogV(0) + let stack: CallStack + let navTitle: String + + @EnvironmentObject private var model: WalletModel + @EnvironmentObject private var controller: Controller + @AppStorage("minimalistic") var minimalistic: Bool = false + @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic + + @State private var amountLastUsed = Amount.zero(currency: EMPTYSTRING) // needed for Deposit, ignore + @State private var created: Bool = false + @State private var files: [String] = [] + @State private var filename: String = "" + @State private var selectedBackup: String? = nil + @State private var showRestoreAlert: Bool = false + + + private func createBackup() { + created = true + Task { + let dateString = Date().iso + let stem = String(PREFIX + dateString) + if let backupFile = try? await model.exportDbToFile(stem: stem) { + filename = backupFile + } + } + } + + private var dismissAlertButton: some View { + Button("Cancel", role: .cancel) { + showRestoreAlert = false + selectedBackup = nil + } + } + private var resetButton: some View { + Button("Restore", role: .destructive) { // TODO: WalletColors().errorColor +// didReset = true + + showRestoreAlert = false + Task { // runs on MainActor + symLog.log("❗️Restore \(selectedBackup)❗️") +// try? await model.resetWalletCore() + selectedBackup = nil + } + } + } + + + + @MainActor + private func viewDidLoad() async { + let fm = FileManager.default + if let docDirUrl = URL.docDirUrl { + if let contentsOfDocDir = try? fm.contentsOfDirectory(at: docDirUrl, + includingPropertiesForKeys: nil, + options: []) { + withAnimation { files = contentsOfDocDir.map { $0.lastPathComponent } } + } + } + } + + var body: some View { +#if PRINT_CHANGES + let _ = Self._printChanges() + let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear +#endif + let a11yLabelStr = String(localized: "Add bank account", comment: "a11y for the + button") + let addTitleStr = String(localized: "Add bank account", comment: "title of the addExchange alert") + let buttonTitle = String(localized: "Create Backup", comment: "button") + + + let backupHint = Text("Tap 'Create Backup' to make a copy of your digital money. Connect your iPhone to a computer, then use the Files dialog and copy that backup to your computer's harddisk.") + + let restoreHint = Text("To restore your digital money, connect your iPhone to your computer, then use the Files dialog and copy a previously saved backup from your computer into the Taler Wallet.") + + let backups = files.filter { $0.hasPrefix(PREFIX) } + List { + Section { + backupHint + .listRowSeparator(.hidden) + Button(buttonTitle) { createBackup() } + .buttonStyle(TalerButtonStyle(type: .bordered)) + .padding() + .listRowSeparator(.hidden) + .disabled(created) + restoreHint + } +#if DEBUG + Text(filename) +#endif + if !backups.isEmpty { + Section { + ForEach(backups, id: \.self) { file in + Text(file) + .onTapGesture { + selectedBackup = file + } + } + } + } + } + .listStyle(myListStyle.style).anyView + .task { await viewDidLoad() } + .navigationTitle(navTitle) + .onChange(of: selectedBackup) { selected in + if selected != nil { + showRestoreAlert = true + } + } + .alert("Overwrite Wallet", + isPresented: $showRestoreAlert, + actions: { dismissAlertButton + resetButton }, + message: { Text("Are you sure you want to overwrite your wallet with this backup?\nThis cannot be reverted, all money which is now still in your wallet will be lost.") }) + .onAppear() { + DebugViewC.shared.setViewID(VIEW_BANK_ACCOUNTS, stack: stack.push()) + } + } // body +}