From d3b255b7cb8c36e40cf3b6b260656384403bb416 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Tue, 12 Mar 2024 23:25:06 +0700 Subject: [PATCH] ios: migration enhancements (#3893) * onion check * alert and log * correction * refactor * change * refactor * enum * footer * remove non-needed directory if no migration * naming * back * rename everything --------- Co-authored-by: Avently Co-authored-by: Evgeny Poberezkin --- apps/ios/Shared/Model/ChatModel.swift | 2 +- ...erDevice.swift => MigrateFromDevice.swift} | 49 ++++---- ...therDevice.swift => MigrateToDevice.swift} | 116 +++++++++--------- .../Shared/Views/Onboarding/SimpleXInfo.swift | 20 ++- .../Views/UserSettings/SettingsView.swift | 5 +- apps/ios/SimpleX.xcodeproj/project.pbxproj | 16 +-- apps/ios/SimpleXChat/API.swift | 7 +- 7 files changed, 109 insertions(+), 106 deletions(-) rename apps/ios/Shared/Views/Migration/{MigrateToAnotherDevice.swift => MigrateFromDevice.swift} (93%) rename apps/ios/Shared/Views/Migration/{MigrateFromAnotherDevice.swift => MigrateToDevice.swift} (87%) diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index bed5d9b2de..462699e407 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -95,7 +95,7 @@ final class ChatModel: ObservableObject { @Published var remoteCtrlSession: RemoteCtrlSession? // currently showing invitation @Published var showingInvitation: ShowingInvitation? - @Published var migrationState: MigrationFromAnotherDeviceState? = MigrationFromAnotherDeviceState.transform() + @Published var migrationState: MigrationToState? = MigrationToDeviceState.makeMigrationState() // audio recording and playback @Published var stopPreviousRecPlay: URL? = nil // coordinates currently playing source @Published var draft: ComposeState? diff --git a/apps/ios/Shared/Views/Migration/MigrateToAnotherDevice.swift b/apps/ios/Shared/Views/Migration/MigrateFromDevice.swift similarity index 93% rename from apps/ios/Shared/Views/Migration/MigrateToAnotherDevice.swift rename to apps/ios/Shared/Views/Migration/MigrateFromDevice.swift index 01a85aa6db..b3b7269d22 100644 --- a/apps/ios/Shared/Views/Migration/MigrateToAnotherDevice.swift +++ b/apps/ios/Shared/Views/Migration/MigrateFromDevice.swift @@ -1,5 +1,5 @@ // -// MigrateToAnotherDevice.swift +// MigrateFromDevice.swift // SimpleX (iOS) // // Created by Avently on 14.02.2024. @@ -9,7 +9,7 @@ import SwiftUI import SimpleXChat -private enum MigrationToState: Equatable { +private enum MigrationFromState: Equatable { case chatStopInProgress case chatStopFailed(reason: String) case passphraseNotSet @@ -23,7 +23,7 @@ private enum MigrationToState: Equatable { case finished(chatDeletion: Bool) } -private enum MigrateToAnotherDeviceViewAlert: Identifiable { +private enum MigrateFromDeviceViewAlert: Identifiable { case deleteChat(_ title: LocalizedStringKey = "Delete chat profile?", _ text: LocalizedStringKey = "This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.") case startChat(_ title: LocalizedStringKey = "Start chat?", _ text: LocalizedStringKey = "Warning: starting chat on multiple devices is not supported and will cause message delivery failures") @@ -51,15 +51,15 @@ private enum MigrateToAnotherDeviceViewAlert: Identifiable { } } -struct MigrateToAnotherDevice: View { +struct MigrateFromDevice: View { @EnvironmentObject var m: ChatModel @Environment(\.dismiss) var dismiss: DismissAction @Binding var showSettings: Bool @Binding var showProgressOnSettings: Bool - @State private var migrationState: MigrationToState = .chatStopInProgress + @State private var migrationState: MigrationFromState = .chatStopInProgress @State private var useKeychain = storeDBPassphraseGroupDefault.get() @AppStorage(GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE, store: groupDefaults) private var initialRandomDBPassphrase: Bool = false - @State private var alert: MigrateToAnotherDeviceViewAlert? + @State private var alert: MigrateFromDeviceViewAlert? @State private var authorized = !UserDefaults.standard.bool(forKey: DEFAULT_PERFORM_LA) private let tempDatabaseUrl = urlForTemporaryDatabase() @State private var chatReceiver: MigrationChatReceiver? = nil @@ -108,11 +108,8 @@ struct MigrateToAnotherDevice: View { }) .onChange(of: migrationState) { state in backDisabled = switch migrationState { - case .archiving: true - case .linkCreation: true - case .linkShown: true - case .finished: true - default: false + case .chatStopInProgress, .archiving, .linkShown, .finished: true + case .chatStopFailed, .passphraseNotSet, .passphraseConfirmation, .uploadConfirmation, .uploadProgress, .uploadFailed, .linkCreation: false } } .onAppear { @@ -120,7 +117,7 @@ struct MigrateToAnotherDevice: View { } .onDisappear { Task { - if case .linkCreation = migrationState {} else if case .linkShown = migrationState {} else if case .finished = migrationState {} else { + if !backDisabled { await MainActor.run { showProgressOnSettings = true } @@ -252,7 +249,7 @@ struct MigrateToAnotherDevice: View { } } let ratio = Float(uploadedBytes) / Float(totalBytes) - MigrateToAnotherDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: uploadedBytes, countStyle: .binary)) uploaded") + MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: uploadedBytes, countStyle: .binary)) uploaded") } .onAppear { startUploading(totalBytes, archivePath) @@ -306,7 +303,10 @@ struct MigrateToAnotherDevice: View { } } } footer: { - Text("Choose _Migrate from another device_ on the new device and scan QR code.") + VStack(alignment: .leading, spacing: 16) { + Text("**Warning**: the archive will be removed.") + Text("Choose _Migrate from another device_ on the new device and scan QR code.") + } .font(.callout) } Section("Show QR code") { @@ -498,6 +498,9 @@ struct MigrateToAnotherDevice: View { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { migrationState = .linkShown(fileId: fileTransferMeta.fileId, link: data.addToLink(link: rcvURIs[0]), archivePath: archivePath, ctrl: ctrl) } + case .sndFileError: + alert = .error(title: "Upload failed", error: "Check your internet connection and try again") + migrationState = .uploadFailed(totalBytes: totalBytes, archivePath: archivePath) default: logger.debug("unsupported event: \(msg.responseType)") } @@ -587,12 +590,12 @@ struct MigrateToAnotherDevice: View { } private struct PassphraseConfirmationView: View { - @Binding var migrationState: MigrationToState + @Binding var migrationState: MigrationFromState @State private var useKeychain = storeDBPassphraseGroupDefault.get() @State private var currentKey: String = "" @State private var verifyingPassphrase: Bool = false @FocusState private var keyboardVisible: Bool - @Binding var alert: MigrateToAnotherDeviceViewAlert? + @Binding var alert: MigrateFromDeviceViewAlert? var body: some View { ZStack { @@ -638,13 +641,17 @@ private struct PassphraseConfirmationView: View { await MainActor.run { migrationState = .uploadConfirmation } - } catch { - showErrorOnMigrationIfNeeded(.errorNotADatabase(dbFile: ""), $alert) + } catch let error { + if case .chatCmdError(_, .errorDatabase(.errorOpen(.errorNotADatabase))) = error as? ChatResponse { + showErrorOnMigrationIfNeeded(.errorNotADatabase(dbFile: ""), $alert) + } else { + alert = .error(title: "Error", error: NSLocalizedString("Error verifying passphrase:", comment: "") + " " + String(String(describing: error))) + } } } } -private func showErrorOnMigrationIfNeeded(_ status: DBMigrationResult, _ alert: Binding) { +private func showErrorOnMigrationIfNeeded(_ status: DBMigrationResult, _ alert: Binding) { switch status { case .invalidConfirmation: alert.wrappedValue = .invalidConfirmation() @@ -720,8 +727,8 @@ private class MigrationChatReceiver { } } -struct MigrateToAnotherDevice_Previews: PreviewProvider { +struct MigrateFromDevice_Previews: PreviewProvider { static var previews: some View { - MigrateToAnotherDevice(showSettings: Binding.constant(true), showProgressOnSettings: Binding.constant(false)) + MigrateFromDevice(showSettings: Binding.constant(true), showProgressOnSettings: Binding.constant(false)) } } diff --git a/apps/ios/Shared/Views/Migration/MigrateFromAnotherDevice.swift b/apps/ios/Shared/Views/Migration/MigrateToDevice.swift similarity index 87% rename from apps/ios/Shared/Views/Migration/MigrateFromAnotherDevice.swift rename to apps/ios/Shared/Views/Migration/MigrateToDevice.swift index 9022beebd3..9afd0dd406 100644 --- a/apps/ios/Shared/Views/Migration/MigrateFromAnotherDevice.swift +++ b/apps/ios/Shared/Views/Migration/MigrateToDevice.swift @@ -1,5 +1,5 @@ // -// MigrateFromAnotherDevice.swift +// MigrateToDevice.swift // SimpleX (iOS) // // Created by Avently on 23.02.2024. @@ -9,56 +9,47 @@ import SwiftUI import SimpleXChat -enum MigrationFromAnotherDeviceState: Codable, Equatable { +enum MigrationToDeviceState: Codable, Equatable { case downloadProgress(link: String, archiveName: String) case archiveImport(archiveName: String) case passphrase - func makeMigrationState() -> MigrationFromState { - var initial: MigrationFromState = .pasteOrScanLink - //logger.debug("Inited with migrationState: \(String(describing: self))") - switch self { - case let .downloadProgress(link, archiveName): - // iOS changes absolute directory every launch, check this way - let archivePath = getMigrationTempFilesDirectory().path + "/" + archiveName - initial = .downloadFailed(totalBytes: 0, link: link, archivePath: archivePath) + // Here we check whether it's needed to show migration process after app restart or not + // It's important to NOT show the process when archive was corrupted/not fully downloaded + static func makeMigrationState() -> MigrationToState? { + let state: MigrationToDeviceState? = UserDefaults.standard.string(forKey: DEFAULT_MIGRATION_TO_STAGE) != nil ? decodeJSON(UserDefaults.standard.string(forKey: DEFAULT_MIGRATION_TO_STAGE)!) : nil + var initial: MigrationToState? = .pasteOrScanLink + //logger.debug("Inited with migrationState: \(String(describing: state))") + switch state { + case nil: + initial = nil + case .downloadProgress: + // No migration happens at the moment actually since archive were not downloaded fully + logger.debug("MigrateToDevice: archive wasn't fully downloaded, removed broken file") + initial = nil case let .archiveImport(archiveName): let archivePath = getMigrationTempFilesDirectory().path + "/" + archiveName initial = .archiveImportFailed(archivePath: archivePath) case .passphrase: initial = .passphrase(passphrase: "") } + if initial == nil { + UserDefaults.standard.removeObject(forKey: DEFAULT_MIGRATION_TO_STAGE) + try? FileManager.default.removeItem(at: getMigrationTempFilesDirectory()) + } return initial } - // Here we check whether it's needed to show migration process after app restart or not - // It's important to NOT show the process when archive was corrupted/not fully downloaded - static func transform() -> MigrationFromAnotherDeviceState? { - let state: MigrationFromAnotherDeviceState? = UserDefaults.standard.string(forKey: DEFAULT_MIGRATION_STAGE) != nil ? decodeJSON(UserDefaults.standard.string(forKey: DEFAULT_MIGRATION_STAGE)!) : nil - - if case let .downloadProgress(_, archiveName) = state { - // iOS changes absolute directory every launch, check this way - let archivePath = getMigrationTempFilesDirectory().path + "/" + archiveName - try? FileManager.default.removeItem(atPath: archivePath) - UserDefaults.standard.removeObject(forKey: DEFAULT_MIGRATION_STAGE) - // No migration happens at the moment actually since archive were not downloaded fully - logger.debug("MigrateFromDevice: archive wasn't fully downloaded, removed broken file") - return nil - } - return state - } - - static func save(_ state: MigrationFromAnotherDeviceState?, apply: (MigrationFromAnotherDeviceState?) -> Void) { + static func save(_ state: MigrationToDeviceState?) { if let state { - UserDefaults.standard.setValue(encodeJSON(state), forKey: DEFAULT_MIGRATION_STAGE) + UserDefaults.standard.setValue(encodeJSON(state), forKey: DEFAULT_MIGRATION_TO_STAGE) } else { - UserDefaults.standard.removeObject(forKey: DEFAULT_MIGRATION_STAGE) + UserDefaults.standard.removeObject(forKey: DEFAULT_MIGRATION_TO_STAGE) } - apply(state) } } -enum MigrationFromState: Equatable { +enum MigrationToState: Equatable { case pasteOrScanLink case linkDownloading(link: String) case downloadProgress(downloadedBytes: Int64, totalBytes: Int64, fileId: Int64, link: String, archivePath: String, ctrl: chat_ctrl?) @@ -71,7 +62,7 @@ enum MigrationFromState: Equatable { case onion(appSettings: AppSettings) } -private enum MigrateFromAnotherDeviceViewAlert: Identifiable { +private enum MigrateToDeviceViewAlert: Identifiable { case chatImportedWithErrors(title: LocalizedStringKey = "Chat database imported", text: LocalizedStringKey = "Some non-fatal errors occurred during import - you may see Chat console for more details.") @@ -98,13 +89,13 @@ private enum MigrateFromAnotherDeviceViewAlert: Identifiable { } } -struct MigrateFromAnotherDevice: View { +struct MigrateToDevice: View { @EnvironmentObject var m: ChatModel @Environment(\.dismiss) var dismiss: DismissAction @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false - @State var migrationState: MigrationFromState + @Binding var migrationState: MigrationToState? @State private var useKeychain = storeDBPassphraseGroupDefault.get() - @State private var alert: MigrateFromAnotherDeviceViewAlert? + @State private var alert: MigrateToDeviceViewAlert? private let tempDatabaseUrl = urlForTemporaryDatabase() @State private var chatReceiver: MigrationChatReceiver? = nil // Prevent from hiding the view until migration is finished or app deleted @@ -114,6 +105,7 @@ struct MigrateFromAnotherDevice: View { var body: some View { VStack { switch migrationState { + case nil: EmptyView() case .pasteOrScanLink: pasteOrScanLinkView() case let .linkDownloading(link): @@ -138,18 +130,14 @@ struct MigrateFromAnotherDevice: View { } .onAppear { backDisabled = switch migrationState { - case .linkDownloading: false - case .downloadProgress: false - case .archiveImportFailed: false - default: m.migrationState != nil + case nil, .pasteOrScanLink, .linkDownloading, .downloadProgress, .downloadFailed, .archiveImportFailed: false + case .archiveImport, .passphrase, .migrationConfirmation, .migration, .onion: true } } .onChange(of: migrationState) { state in backDisabled = switch state { - case .linkDownloading: false - case .downloadProgress: false - case .archiveImportFailed: false - default: m.migrationState != nil + case nil, .pasteOrScanLink, .linkDownloading, .downloadProgress, .downloadFailed, .archiveImportFailed: false + case .archiveImport, .passphrase, .migrationConfirmation, .migration, .onion: true } } .onDisappear { @@ -164,7 +152,7 @@ struct MigrateFromAnotherDevice: View { chatReceiver?.stopAndCleanUp() if !backDisabled { try? FileManager.default.removeItem(at: getMigrationTempFilesDirectory()) - MigrationFromAnotherDeviceState.save(nil) { m.migrationState = $0 } + MigrationToDeviceState.save(nil) } } } @@ -255,7 +243,7 @@ struct MigrateFromAnotherDevice: View { } } let ratio = Float(downloadedBytes) / Float(max(totalBytes, 1)) - MigrateToAnotherDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: downloadedBytes, countStyle: .binary)) downloaded") + MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: downloadedBytes, countStyle: .binary)) downloaded") } } @@ -280,7 +268,7 @@ struct MigrateFromAnotherDevice: View { .onAppear { chatReceiver?.stopAndCleanUp() try? FileManager.default.removeItem(atPath: archivePath) - MigrationFromAnotherDeviceState.save(nil) { m.migrationState = $0 } + MigrationToDeviceState.save(nil) } } @@ -446,11 +434,16 @@ struct MigrateFromAnotherDevice: View { switch msg { case let .rcvFileProgressXFTP(_, _, receivedSize, totalSize, rcvFileTransfer): migrationState = .downloadProgress(downloadedBytes: receivedSize, totalBytes: totalSize, fileId: rcvFileTransfer.fileId, link: link, archivePath: archivePath, ctrl: ctrl) - MigrationFromAnotherDeviceState.save(.downloadProgress(link: link, archiveName: URL(fileURLWithPath: archivePath).lastPathComponent)) { m.migrationState = $0 } + MigrationToDeviceState.save(.downloadProgress(link: link, archiveName: URL(fileURLWithPath: archivePath).lastPathComponent)) case .rcvStandaloneFileComplete: DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - migrationState = .archiveImport(archivePath: archivePath) - MigrationFromAnotherDeviceState.save(.archiveImport(archiveName: URL(fileURLWithPath: archivePath).lastPathComponent)) { m.migrationState = $0 } + // User closed the whole screen before new state was saved + if migrationState == nil { + MigrationToDeviceState.save(nil) + } else { + migrationState = .archiveImport(archivePath: archivePath) + MigrationToDeviceState.save(.archiveImport(archiveName: URL(fileURLWithPath: archivePath).lastPathComponent)) + } } case .rcvFileError: alert = .error(title: "Download failed", error: "File was deleted or link is invalid") @@ -487,7 +480,7 @@ struct MigrateFromAnotherDevice: View { } await MainActor.run { migrationState = .passphrase(passphrase: "") - MigrationFromAnotherDeviceState.save(.passphrase) { m.migrationState = $0 } + MigrationToDeviceState.save(.passphrase) } } catch let error { await MainActor.run { @@ -523,11 +516,12 @@ struct MigrateFromAnotherDevice: View { resetChatCtrl() try initializeChat(start: false, confirmStart: false, dbKey: passphrase, refreshInvitations: true, confirmMigrations: confirmation) var appSettings = try apiGetAppSettings(settings: AppSettings.current.prepareForExport()) + let hasOnionConfigured = appSettings.networkConfig?.socksProxy != nil || appSettings.networkConfig?.hostMode == .onionHost + appSettings.networkConfig?.socksProxy = nil + appSettings.networkConfig?.hostMode = .publicHost + appSettings.networkConfig?.requiredHostMode = true await MainActor.run { - if appSettings.networkConfig?.hostMode == .onionViaSocks || appSettings.networkConfig?.hostMode == .onionHost || appSettings.networkConfig?.socksProxy != nil { - appSettings.networkConfig?.socksProxy = nil - appSettings.networkConfig?.hostMode = .publicHost - appSettings.networkConfig?.requiredHostMode = true + if hasOnionConfigured { migrationState = .onion(appSettings: appSettings) } else { finishMigration(appSettings) @@ -543,7 +537,7 @@ struct MigrateFromAnotherDevice: View { private func finishMigration(_ appSettings: AppSettings) { do { try? FileManager.default.removeItem(at: getMigrationTempFilesDirectory()) - MigrationFromAnotherDeviceState.save(nil) { m.migrationState = $0 } + MigrationToDeviceState.save(nil) appSettings.importIntoApp() try SimpleX.startChat(refreshInvitations: true) AlertManager.shared.showAlertMsg(title: "Chat migrated!", message: "Finalize migration on another device.") @@ -569,12 +563,12 @@ struct MigrateFromAnotherDevice: View { } private struct PassphraseEnteringView: View { - @Binding var migrationState: MigrationFromState + @Binding var migrationState: MigrationToState? @State private var useKeychain = true @State var currentKey: String @State private var verifyingPassphrase: Bool = false @FocusState private var keyboardVisible: Bool - @Binding var alert: MigrateFromAnotherDeviceViewAlert? + @Binding var alert: MigrateToDeviceViewAlert? var body: some View { ZStack { @@ -643,7 +637,7 @@ private struct PassphraseEnteringView: View { } } -private func showErrorOnMigrationIfNeeded(_ status: DBMigrationResult, _ alert: Binding) { +private func showErrorOnMigrationIfNeeded(_ status: DBMigrationResult, _ alert: Binding) { switch status { case .invalidConfirmation: alert.wrappedValue = .invalidConfirmation() @@ -713,8 +707,8 @@ private class MigrationChatReceiver { } } -struct MigrateFromAnotherDevice_Previews: PreviewProvider { +struct MigrateToDevice_Previews: PreviewProvider { static var previews: some View { - MigrateFromAnotherDevice(migrationState: .pasteOrScanLink) + MigrateToDevice(migrationState: Binding.constant(.pasteOrScanLink)) } } diff --git a/apps/ios/Shared/Views/Onboarding/SimpleXInfo.swift b/apps/ios/Shared/Views/Onboarding/SimpleXInfo.swift index b68c1279b0..94e281be7d 100644 --- a/apps/ios/Shared/Views/Onboarding/SimpleXInfo.swift +++ b/apps/ios/Shared/Views/Onboarding/SimpleXInfo.swift @@ -13,8 +13,6 @@ struct SimpleXInfo: View { @EnvironmentObject var m: ChatModel @Environment(\.colorScheme) var colorScheme: ColorScheme @State private var showHowItWorks = false - @State private var migrationState: MigrationFromState? = nil - @State private var migrateFromAnotherDevice: Bool = false var onboarding: Bool var body: some View { @@ -49,8 +47,7 @@ struct SimpleXInfo: View { Spacer() Button { - migrationState = nil - migrateFromAnotherDevice = true + m.migrationState = .pasteOrScanLink } label: { Label("Migrate from another device", systemImage: "tray.and.arrow.down") .font(.subheadline) @@ -71,16 +68,15 @@ struct SimpleXInfo: View { } .frame(minHeight: g.size.height) } - .onAppear { - if m.migrationState != nil { - migrationState = m.migrationState?.makeMigrationState() - migrateFromAnotherDevice = true - } - } - .sheet(isPresented: $migrateFromAnotherDevice) { + .sheet(isPresented: Binding( + get: { m.migrationState != nil }, + set: { _ in + m.migrationState = nil + MigrationToDeviceState.save(nil) } + )) { NavigationView { VStack(alignment: .leading) { - MigrateFromAnotherDevice(migrationState: migrationState ?? .pasteOrScanLink) + MigrateToDevice(migrationState: $m.migrationState) } .navigationTitle("Migrate here") .background(colorScheme == .light ? Color(uiColor: .tertiarySystemGroupedBackground) : .clear) diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift index 842ccaab4c..1799d8136a 100644 --- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift +++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift @@ -51,7 +51,8 @@ let DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE = "showHiddenProfilesNotice" let DEFAULT_SHOW_MUTE_PROFILE_ALERT = "showMuteProfileAlert" let DEFAULT_WHATS_NEW_VERSION = "defaultWhatsNewVersion" let DEFAULT_ONBOARDING_STAGE = "onboardingStage" -let DEFAULT_MIGRATION_STAGE = "migrationStage" +let DEFAULT_MIGRATION_TO_STAGE = "migrationToStage" +let DEFAULT_MIGRATION_FROM_STAGE = "migrationFromStage" let DEFAULT_CUSTOM_DISAPPEARING_MESSAGE_TIME = "customDisappearingMessageTime" let DEFAULT_SHOW_UNREAD_AND_FAVORITES = "showUnreadAndFavorites" let DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS = "deviceNameForRemoteAccess" @@ -212,7 +213,7 @@ struct SettingsView: View { } NavigationLink { - MigrateToAnotherDevice(showSettings: $showSettings, showProgressOnSettings: $showProgress) + MigrateFromDevice(showSettings: $showSettings, showProgressOnSettings: $showProgress) .navigationTitle("Migrate device") .navigationBarTitleDisplayMode(.large) } label: { diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 5f6fe6c65a..eb96807b6f 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -186,8 +186,8 @@ 64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; }; 8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C05382D2B39887E006436DC /* VideoUtils.swift */; }; 8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */; }; - 8C7D949A2B88952700B7B9E1 /* MigrateFromAnotherDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7D94992B88952700B7B9E1 /* MigrateFromAnotherDevice.swift */; }; - 8C7DF3202B7CDB0A00C886D0 /* MigrateToAnotherDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7DF31F2B7CDB0A00C886D0 /* MigrateToAnotherDevice.swift */; }; + 8C7D949A2B88952700B7B9E1 /* MigrateToDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */; }; + 8C7DF3202B7CDB0A00C886D0 /* MigrateFromDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */; }; D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; }; D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; }; D741547829AF89AF0022400A /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547729AF89AF0022400A /* StoreKit.framework */; }; @@ -477,8 +477,8 @@ 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoHelp.swift; sourceTree = ""; }; 8C05382D2B39887E006436DC /* VideoUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoUtils.swift; sourceTree = ""; }; 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = ""; }; - 8C7D94992B88952700B7B9E1 /* MigrateFromAnotherDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateFromAnotherDevice.swift; sourceTree = ""; }; - 8C7DF31F2B7CDB0A00C886D0 /* MigrateToAnotherDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAnotherDevice.swift; sourceTree = ""; }; + 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToDevice.swift; sourceTree = ""; }; + 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateFromDevice.swift; sourceTree = ""; }; D72A9087294BD7A70047C86D /* NativeTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTextEditor.swift; sourceTree = ""; }; D741547729AF89AF0022400A /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; }; D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; }; @@ -904,8 +904,8 @@ 8C7D94982B8894D300B7B9E1 /* Migration */ = { isa = PBXGroup; children = ( - 8C7DF31F2B7CDB0A00C886D0 /* MigrateToAnotherDevice.swift */, - 8C7D94992B88952700B7B9E1 /* MigrateFromAnotherDevice.swift */, + 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */, + 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */, ); path = Migration; sourceTree = ""; @@ -1141,7 +1141,7 @@ 5CBD285A295711D700EC2CF4 /* ImageUtils.swift in Sources */, 6419EC562AB8BC8B004A607A /* ContextInvitingContactMemberView.swift in Sources */, 5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */, - 8C7D949A2B88952700B7B9E1 /* MigrateFromAnotherDevice.swift in Sources */, + 8C7D949A2B88952700B7B9E1 /* MigrateToDevice.swift in Sources */, 5C3F1D562842B68D00EC8A82 /* IntegrityErrorItemView.swift in Sources */, 5C029EAA283942EA004A9677 /* CallController.swift in Sources */, 5CBE6C142944CC12002D9531 /* ScanCodeView.swift in Sources */, @@ -1239,7 +1239,7 @@ 5CB0BA92282713FD00B3292C /* CreateProfile.swift in Sources */, 5C5F2B7027EBC704006A9D5F /* ProfileImage.swift in Sources */, 5C9329412929248A0090FFF9 /* ScanProtocolServer.swift in Sources */, - 8C7DF3202B7CDB0A00C886D0 /* MigrateToAnotherDevice.swift in Sources */, + 8C7DF3202B7CDB0A00C886D0 /* MigrateFromDevice.swift in Sources */, 64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */, 5C93293F2928E0FD0090FFF9 /* AudioRecPlay.swift in Sources */, 5C029EA82837DBB3004A9677 /* CICallItemView.swift in Sources */, diff --git a/apps/ios/SimpleXChat/API.swift b/apps/ios/SimpleXChat/API.swift index 64249fe09b..3c9f77d791 100644 --- a/apps/ios/SimpleXChat/API.swift +++ b/apps/ios/SimpleXChat/API.swift @@ -68,14 +68,19 @@ public func chatInitTemporaryDatabase(url: URL, key: String? = nil, confirmation public func chatInitControllerRemovingDatabases() { let dbPath = getAppDatabasePath().path + let fm = FileManager.default + // Remove previous databases, otherwise, can be .errorNotADatabase with nil controller + try? fm.removeItem(atPath: dbPath + CHAT_DB) + try? fm.removeItem(atPath: dbPath + AGENT_DB) + let dbKey = randomDatabasePassword() logger.debug("chatInitControllerRemovingDatabases path: \(dbPath)") var cPath = dbPath.cString(using: .utf8)! var cKey = dbKey.cString(using: .utf8)! var cConfirm = MigrationConfirmation.error.rawValue.cString(using: .utf8)! chat_migrate_init_key(&cPath, &cKey, 1, &cConfirm, 0, &chatController) + // We need only controller, not databases - let fm = FileManager.default try? fm.removeItem(atPath: dbPath + CHAT_DB) try? fm.removeItem(atPath: dbPath + AGENT_DB) }