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 }