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