taler-ios

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

MigrationView.swift (6565B)


      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 SymLog
     10 import taler_swift
     11 
     12 struct MigrationView: View {
     13 
     14     @Environment(\.colorScheme) private var colorScheme
     15     @Environment(\.colorSchemeContrast) private var colorSchemeContrast
     16     @EnvironmentObject private var controller: Controller
     17     @EnvironmentObject private var model: WalletModel
     18 #if DEBUG
     19     @AppStorage("developerMode") var developerMode: Bool = true
     20 #else
     21     @AppStorage("developerMode") var developerMode: Bool = false
     22 #endif
     23     @AppStorage("preferredColorScheme") var preferredColorScheme: Int = 0
     24     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
     25 
     26     @State private var isMigrating: Bool = false
     27     @State private var fakeMigration: Bool = false
     28     @State private var isFinished: Bool = false
     29     @State private var isCancelled: Bool = false
     30     @State private var progress: Double = 0
     31     @State private var error2: Error? = nil
     32 
     33     func buttonAction() {
     34         if !isMigrating {
     35             withAnimation {
     36                 self.isMigrating = true
     37             }
     38             Task {
     39                 let result = try? await model.migrateDatabase()
     40                 if let migrated = result?.migrated {
     41                     isFinished = migrated
     42                 }
     43             }
     44         }
     45     }
     46 
     47     func fakeAction() {
     48         if !isMigrating {
     49             fakeMigration = true
     50             withAnimation {
     51                 self.isMigrating = true
     52             }
     53         }
     54     }
     55 
     56     // Too much hassle for wallet-core to check this everywhere
     57 //    func cancelAction() {
     58 //        self.isMigrating = false
     59 //        self.isCancelled = true
     60 //        if let operation = controller.progressOperation,
     61 //           let token = controller.progressToken {
     62 //            Task {
     63 //                try? await model.cancelProgressToken(operation, token: token)
     64 //                controller.progressOperation = nil
     65 //                controller.progressToken = nil
     66 //            }
     67 //        }
     68 //    }
     69 
     70     func finishAction() {
     71         withAnimation {
     72             controller.backendState = .ready
     73         }
     74     }
     75 
     76     var body: some View {
     77         let list = List {
     78             Section(header: Text("Update database")) {
     79                 if isMigrating || isFinished || isCancelled {
     80                     if fakeMigration {
     81                         Text("Faking migration")
     82                             .talerFont(.title)
     83                             .task {
     84                                 for x in 0...100 {
     85                                     await Task.sleep(20_000_000)
     86                                     self.progress = Double(x) / 100
     87                                 }
     88                                 withAnimation {
     89                                     isFinished = true
     90                                 }
     91                             }
     92                     } else {
     93                         Text("Updating the database:")
     94                             .talerFont(.title)
     95                     }
     96                     ProgressView(value: progress)
     97 //                    Button("Cancel", action: cancelAction)
     98 //                        .buttonStyle(TalerButtonStyle(type: .bordered))
     99 //                        .padding()
    100                     if isFinished || isCancelled {
    101                         Text(isCancelled ? "Cancelled" : "Finished")
    102                             .talerFont(.title)
    103                             .task {
    104                                 DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    105                                     finishAction()
    106                                 }
    107                             }
    108                     } else {
    109                         Text("Please don't close the app now, or we need to start over next time.")
    110                             .talerFont(.body)
    111                     }
    112                 } else {
    113                     Text("The \(controller.localizedAppName) database needs to be updated.")
    114                         .talerFont(.title)
    115                     Text("This might take from a few seconds to a minute or two, depending on how many transactions you made.")
    116                         .talerFont(.body)
    117 
    118                     Button("Update now", action: buttonAction)
    119                         .buttonStyle(TalerButtonStyle(type: .bordered, disabled: isMigrating))
    120                         .disabled(isMigrating)
    121                         .padding()
    122                     if developerMode {
    123                         Button("Fake update", action: fakeAction)
    124                             .buttonStyle(TalerButtonStyle(type: .bordered, disabled: isMigrating))
    125                             .disabled(isMigrating)
    126                             .padding()
    127                     }
    128                     Button("Update later", action: finishAction)
    129                         .buttonStyle(TalerButtonStyle(type: .bordered))
    130                         .padding()
    131                 }
    132             }
    133             .listRowSeparator(.hidden)
    134         }.id("list")
    135         .listStyle(myListStyle.style).anyView
    136         .talerFont(.title2)
    137         .onNotification(.DatabaseMaintenance) { notification in
    138             if let payload = notification.userInfo?[NOTIFICATIONPAYLOAD] as? WalletCore.Payload {
    139                 if let error = payload.error {
    140                     self.error2 = WalletBackendError.walletCoreError(error)
    141                 } else if let percent = payload.completionPercent {
    142                     progress = Double(percent) / 100
    143                     if percent == 100 {
    144                         withAnimation {
    145                             isFinished = true
    146                         }
    147                     }
    148                 }
    149             }
    150         }
    151         let stack = ZStack {
    152             if preferredColorScheme == 3, #available(iOS 17.0, *) {
    153                 list.scrollContentBackground(.hidden)
    154             } else {
    155                 list
    156             }
    157             if let error2 {
    158                 ErrorView(nil, error: error2, devMode: developerMode) {
    159                     self.error2 = nil
    160                 }.interactiveDismissDisabled()
    161             }
    162         }.id("stack")
    163         NavigationView {
    164             ZStack {
    165                 WalletHeader(showSettings: false, content: stack)
    166             }.frame(maxWidth: .infinity, maxHeight: .infinity)
    167                 .background(FullBackground())
    168         }.navigationViewStyle(.stack)
    169     }
    170 }
    171 // MARK: -
    172 struct MigrationView_Previews: PreviewProvider {
    173     static var previews: some View {
    174         MigrationView()
    175     }
    176 }