mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-10 17:18:31 +00:00
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 <avently@local> Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
committed by
GitHub
parent
96fba950ff
commit
d3b255b7cb
@@ -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?
|
||||
|
||||
+28
-21
@@ -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<MigrateToAnotherDeviceViewAlert?>) {
|
||||
private func showErrorOnMigrationIfNeeded(_ status: DBMigrationResult, _ alert: Binding<MigrateFromDeviceViewAlert?>) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
+55
-61
@@ -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<MigrateFromAnotherDeviceViewAlert?>) {
|
||||
private func showErrorOnMigrationIfNeeded(_ status: DBMigrationResult, _ alert: Binding<MigrateToDeviceViewAlert?>) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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 = "<group>"; };
|
||||
8C05382D2B39887E006436DC /* VideoUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoUtils.swift; sourceTree = "<group>"; };
|
||||
8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
|
||||
8C7D94992B88952700B7B9E1 /* MigrateFromAnotherDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateFromAnotherDevice.swift; sourceTree = "<group>"; };
|
||||
8C7DF31F2B7CDB0A00C886D0 /* MigrateToAnotherDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAnotherDevice.swift; sourceTree = "<group>"; };
|
||||
8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToDevice.swift; sourceTree = "<group>"; };
|
||||
8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateFromDevice.swift; sourceTree = "<group>"; };
|
||||
D72A9087294BD7A70047C86D /* NativeTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTextEditor.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>";
|
||||
@@ -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 */,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user