ios: migration via link
This commit is contained in:
@@ -1701,34 +1701,48 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
}
|
||||
case let .rcvFileAccepted(user, aChatItem): // usually rcvFileAccepted is a response, but it's also an event for XFTP files auto-accepted from NSE
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
case let .rcvFileStart(user, aChatItem):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
case let .rcvFileStart(user, aChatItem, _):
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
}
|
||||
case let .rcvFileComplete(user, aChatItem):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
case let .rcvFileSndCancelled(user, aChatItem, _):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupFile(aChatItem) }
|
||||
case let .rcvFileProgressXFTP(user, aChatItem, _, _):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
case let .rcvFileError(user, aChatItem):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupFile(aChatItem) }
|
||||
case let .rcvFileProgressXFTP(user, aChatItem, _, _, _):
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
}
|
||||
case let .rcvFileError(user, aChatItem, _):
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupFile(aChatItem) }
|
||||
}
|
||||
case let .sndFileStart(user, aChatItem, _):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
case let .sndFileComplete(user, aChatItem, _):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupDirectFile(aChatItem) }
|
||||
case let .sndFileRcvCancelled(user, aChatItem, _):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupDirectFile(aChatItem) }
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupDirectFile(aChatItem) }
|
||||
}
|
||||
case let .sndFileProgressXFTP(user, aChatItem, _, _, _):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
case let .sndFileCompleteXFTP(user, aChatItem, _):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupFile(aChatItem) }
|
||||
case let .sndFileError(user, aChatItem):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupFile(aChatItem) }
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
}
|
||||
case let .sndFileCompleteXFTP(user, aChatItem, _, _):
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupFile(aChatItem) }
|
||||
}
|
||||
case let .sndFileError(user, aChatItem, _):
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupFile(aChatItem) }
|
||||
}
|
||||
case let .callInvitation(invitation):
|
||||
await MainActor.run {
|
||||
m.callInvitations[invitation.contact.id] = invitation
|
||||
|
||||
296
apps/ios/Shared/Views/UserSettings/MigrateToAnotherDevice.swift
Normal file
296
apps/ios/Shared/Views/UserSettings/MigrateToAnotherDevice.swift
Normal file
@@ -0,0 +1,296 @@
|
||||
//
|
||||
// MigrateToAnotherDevice.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Avently on 14.02.2024.
|
||||
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
public enum MigrationState: Equatable {
|
||||
case initial
|
||||
case chatStopInProgress
|
||||
case chatStopFailed(reason: String)
|
||||
case passphraseNotSet
|
||||
case passphraseConfirmation
|
||||
case uploadConfirmation
|
||||
case uploadProgress(uploadedKb: Int64, totalKb: Int64)
|
||||
case uploadDone(link: String)
|
||||
}
|
||||
|
||||
struct MigrateToAnotherDevice: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@State private var migrationState: MigrationState = .initial
|
||||
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
|
||||
@State private var alert: MigrateToAnotherDeviceViewAlert?
|
||||
private let chatWasStoppedInitially: Bool = AppChatState.shared.value == .stopped
|
||||
|
||||
enum MigrateToAnotherDeviceViewAlert: Identifiable {
|
||||
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case let .error(title, _): return "error \(title)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
List {
|
||||
switch migrationState {
|
||||
case .initial: EmptyView()
|
||||
case .chatStopInProgress:
|
||||
progressView("Stopping chat")
|
||||
case let .chatStopFailed(reason):
|
||||
chatStopFailedView(reason)
|
||||
case .passphraseNotSet:
|
||||
passphraseNotSetView()
|
||||
case .passphraseConfirmation:
|
||||
PassphraseConfirmationView(migrationState: $migrationState)
|
||||
case .uploadConfirmation:
|
||||
uploadConfirmationView()
|
||||
case let .uploadProgress(uploaded, total):
|
||||
uploadProgressView(uploaded, totalKb: total)
|
||||
case let .uploadDone(link):
|
||||
uploadDoneView(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if case .initial = migrationState {
|
||||
if AppChatState.shared.value == .stopped {
|
||||
migrationState = initialRandomDBPassphraseGroupDefault.get() ? .passphraseNotSet : .passphraseConfirmation
|
||||
} else {
|
||||
migrationState = .chatStopInProgress
|
||||
stopChat()
|
||||
}
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
if !chatWasStoppedInitially {
|
||||
Task {
|
||||
try? startChat(refreshInvitations: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert(item: $alert) { alert in
|
||||
switch alert {
|
||||
case let .error(title, error):
|
||||
return Alert(title: Text(title), message: Text(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func chatStopFailedView(_ reason: String) -> some View {
|
||||
Section {
|
||||
Text(reason)
|
||||
Button(action: stopChat) {
|
||||
settingsRow("stop.fill", color: .red) {
|
||||
Text("Stop chat")
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Error stopping chat")
|
||||
} footer: {
|
||||
Text("In order to continue, chat should be stopped")
|
||||
}
|
||||
}
|
||||
|
||||
private func passphraseNotSetView() -> some View {
|
||||
Section {
|
||||
Text("Database is encrypted using a random passphrase. Please set your own password before migrating.")
|
||||
NavigationLink {
|
||||
DatabaseEncryptionView(useKeychain: $useKeychain)
|
||||
.navigationTitle("Database passphrase")
|
||||
} label: {
|
||||
settingsRow("lock.open", color: .secondary) {
|
||||
Text("Set passphrase")
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Set passphrase to export")
|
||||
}
|
||||
.onAppear {
|
||||
if !initialRandomDBPassphraseGroupDefault.get() {
|
||||
migrationState = .uploadConfirmation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func uploadConfirmationView() -> some View {
|
||||
Section {
|
||||
Button(action: startUploading) {
|
||||
settingsRow("tray.and.arrow.up", color: .secondary) {
|
||||
Text("Start uploading")
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Confirm upload")
|
||||
} footer: {
|
||||
Text("Do you want to start uploading now?")
|
||||
}
|
||||
}
|
||||
|
||||
private func uploadProgressView(_ uploadedKb: Int64, totalKb: Int64) -> some View {
|
||||
progressView("Uploaded \(ByteCountFormatter.string(fromByteCount: uploadedKb, countStyle: .binary)) from \(ByteCountFormatter.string(fromByteCount: totalKb, countStyle: .binary))")
|
||||
}
|
||||
|
||||
private func uploadDoneView(_ link: String) -> some View {
|
||||
Section {
|
||||
SimpleXLinkQRCode(uri: link)
|
||||
shareLinkButton(link)
|
||||
} header: {
|
||||
Text("Link to uploaded archive")
|
||||
}
|
||||
}
|
||||
|
||||
private func shareLinkButton(_ link: String) -> some View {
|
||||
Button {
|
||||
showShareSheet(items: [simplexChatLink(link)])
|
||||
} label: {
|
||||
Label("Share", systemImage: "square.and.arrow.up")
|
||||
}
|
||||
}
|
||||
|
||||
private func stopChat() {
|
||||
Task {
|
||||
do {
|
||||
try await stopChatAsync()
|
||||
migrationState = initialRandomDBPassphraseGroupDefault.get() ? .passphraseNotSet : .passphraseConfirmation
|
||||
} catch let e {
|
||||
migrationState = .chatStopFailed(reason: e.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func startUploading() {
|
||||
Task {
|
||||
for i in 1...10 {
|
||||
try? await Task.sleep(nanoseconds: 100_000000)
|
||||
await MainActor.run {
|
||||
migrationState = .uploadProgress(uploadedKb: Int64(100 * i), totalKb: 1000)
|
||||
if i == 10 {
|
||||
migrationState = .uploadDone(link: "https://simplex.chat")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct PassphraseConfirmationView: View {
|
||||
@Binding var migrationState: MigrationState
|
||||
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
|
||||
@State private var currentKey: String = ""
|
||||
@State private var verifyingPassphrase: Bool = false
|
||||
@State private var alert: PassphraseConfirmationViewAlert?
|
||||
|
||||
enum PassphraseConfirmationViewAlert: Identifiable {
|
||||
case wrongPassphrase(title: LocalizedStringKey = "Wrong passphrase!", message: LocalizedStringKey = "Enter correct passphrase.")
|
||||
case invalidConfirmation(title: LocalizedStringKey = "Invalid migration confirmation")
|
||||
case keychainError(_ title: LocalizedStringKey = "Keychain error")
|
||||
case databaseError(_ title: LocalizedStringKey = "Database error", message: String)
|
||||
case unknownError(_ title: LocalizedStringKey = "Unknown error", message: String)
|
||||
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case .wrongPassphrase: return "wrongPassphrase"
|
||||
case .invalidConfirmation: return "invalidConfirmation"
|
||||
case .keychainError: return "keychainError"
|
||||
case let .databaseError(title, message): return "\(title) \(message)"
|
||||
case let .unknownError(title, message): return "\(title) \(message)"
|
||||
case let .error(title, _): return "error \(title)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .topLeading) {
|
||||
VStack {
|
||||
Section {
|
||||
VStack {
|
||||
PassphraseField(key: $currentKey, placeholder: "Current passphrase…", valid: validKey(currentKey))
|
||||
Button(action: { checkDatabasePassphrase(currentKey, $verifyingPassphrase) }) {
|
||||
settingsRow(useKeychain ? "key" : "lock", color: .secondary) {
|
||||
Text("Check passphrase")
|
||||
}
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Enter database passphrase")
|
||||
} footer: {
|
||||
Text("Make sure you remember database passphrase before migrating")
|
||||
}
|
||||
}
|
||||
if (verifyingPassphrase) {
|
||||
progressView("Checking passphrase…")
|
||||
}
|
||||
}
|
||||
.alert(item: $alert) { alert in
|
||||
switch alert {
|
||||
case let .wrongPassphrase(title, message):
|
||||
return Alert(title: Text(title), message: Text(message))
|
||||
case let .invalidConfirmation(title):
|
||||
return Alert(title: Text(title))
|
||||
case let .keychainError(title):
|
||||
return Alert(title: Text(title))
|
||||
case let .databaseError(title, message):
|
||||
return Alert(title: Text(title), message: Text(message))
|
||||
case let .unknownError(title, message):
|
||||
return Alert(title: Text(title), message: Text(message))
|
||||
case let .error(title, error):
|
||||
return Alert(title: Text(title), message: Text(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func checkDatabasePassphrase(_ dbKey: String, _ verifyingPassphrase: Binding<Bool>) {
|
||||
verifyingPassphrase.wrappedValue = true
|
||||
defer {
|
||||
verifyingPassphrase.wrappedValue = false
|
||||
}
|
||||
do {
|
||||
resetChatCtrl()
|
||||
try initializeChat(start: false, confirmStart: false, dbKey: dbKey, confirmMigrations: nil)
|
||||
if let s = ChatModel.shared.chatDbStatus {
|
||||
let am = AlertManager.shared
|
||||
switch s {
|
||||
case .invalidConfirmation:
|
||||
am.showAlert(Alert(title: Text(String("Invalid migration confirmation"))))
|
||||
case .errorNotADatabase:
|
||||
alert = .wrongPassphrase()
|
||||
case .errorKeychain:
|
||||
alert = .keychainError()
|
||||
case let .errorSQL(_, error):
|
||||
alert = .databaseError(message: error)
|
||||
case let .unknown(error):
|
||||
alert = .unknownError(message: error)
|
||||
case .errorMigration: ()
|
||||
case .ok:
|
||||
migrationState = .uploadConfirmation
|
||||
}
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("initializeChat \(responseError(error))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func progressView(_ text: LocalizedStringKey) -> some View {
|
||||
VStack {
|
||||
ProgressView().scaleEffect(2)
|
||||
Text(text)
|
||||
.padding()
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity )
|
||||
}
|
||||
|
||||
struct MigrateToAnotherDevice_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
MigrateToAnotherDevice()
|
||||
}
|
||||
}
|
||||
@@ -205,6 +205,16 @@ struct SettingsView: View {
|
||||
}
|
||||
.disabled(chatModel.chatRunning != true)
|
||||
|
||||
Section {
|
||||
NavigationLink {
|
||||
MigrateToAnotherDevice()
|
||||
.navigationTitle("Migrate to another device")
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
settingsRow("tray.and.arrow.up") { Text("Migrate to another device") }
|
||||
}
|
||||
}
|
||||
|
||||
Section("Settings") {
|
||||
NavigationLink {
|
||||
NotificationsView()
|
||||
|
||||
@@ -642,10 +642,14 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
|
||||
cleanupDirectFile(aChatItem)
|
||||
return nil
|
||||
case let .sndFileRcvCancelled(_, aChatItem, _):
|
||||
cleanupDirectFile(aChatItem)
|
||||
if let aChatItem = aChatItem {
|
||||
cleanupDirectFile(aChatItem)
|
||||
}
|
||||
return nil
|
||||
case let .sndFileCompleteXFTP(_, aChatItem, _):
|
||||
cleanupFile(aChatItem)
|
||||
case let .sndFileCompleteXFTP(_, aChatItem, _, _):
|
||||
if let aChatItem = aChatItem {
|
||||
cleanupFile(aChatItem)
|
||||
}
|
||||
return nil
|
||||
case let .callInvitation(invitation):
|
||||
// Do not post it without CallKit support, iOS will stop launching the app without showing CallKit
|
||||
|
||||
@@ -185,6 +185,7 @@
|
||||
64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */; };
|
||||
64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; };
|
||||
8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C05382D2B39887E006436DC /* VideoUtils.swift */; };
|
||||
8C7DF3202B7CDB0A00C886D0 /* MigrateToAnotherDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7DF31F2B7CDB0A00C886D0 /* MigrateToAnotherDevice.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 */; };
|
||||
@@ -473,6 +474,7 @@
|
||||
64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIGroupInvitationView.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
8C7DF31F2B7CDB0A00C886D0 /* MigrateToAnotherDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAnotherDevice.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; };
|
||||
@@ -766,6 +768,7 @@
|
||||
64D0C2BF29F9688300B38D5F /* UserAddressView.swift */,
|
||||
64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */,
|
||||
5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */,
|
||||
8C7DF31F2B7CDB0A00C886D0 /* MigrateToAnotherDevice.swift */,
|
||||
);
|
||||
path = UserSettings;
|
||||
sourceTree = "<group>";
|
||||
@@ -1220,6 +1223,7 @@
|
||||
5CB0BA92282713FD00B3292C /* CreateProfile.swift in Sources */,
|
||||
5C5F2B7027EBC704006A9D5F /* ProfileImage.swift in Sources */,
|
||||
5C9329412929248A0090FFF9 /* ScanProtocolServer.swift in Sources */,
|
||||
8C7DF3202B7CDB0A00C886D0 /* MigrateToAnotherDevice.swift in Sources */,
|
||||
64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */,
|
||||
5C93293F2928E0FD0090FFF9 /* AudioRecPlay.swift in Sources */,
|
||||
5C029EA82837DBB3004A9677 /* CICallItemView.swift in Sources */,
|
||||
|
||||
@@ -131,6 +131,8 @@ public enum ChatCommand {
|
||||
case listRemoteCtrls
|
||||
case stopRemoteCtrl
|
||||
case deleteRemoteCtrl(remoteCtrlId: Int64)
|
||||
case apiUploadFileDirectly(userId: Int64, file: CryptoFile)
|
||||
case apiDownloadFileDirectly(userId: Int64, url: String, file: CryptoFile)
|
||||
// misc
|
||||
case showVersion
|
||||
case string(String)
|
||||
@@ -284,6 +286,8 @@ public enum ChatCommand {
|
||||
case .listRemoteCtrls: return "/list remote ctrls"
|
||||
case .stopRemoteCtrl: return "/stop remote ctrl"
|
||||
case let .deleteRemoteCtrl(rcId): return "/delete remote ctrl \(rcId)"
|
||||
case let .apiUploadFileDirectly(userId, file): return "/_upload \(userId) \(encodeJSON(file))"
|
||||
case let .apiDownloadFileDirectly(userId, link, file): return "/_download \(userId) \(link) \(encodeJSON(file))"
|
||||
case .showVersion: return "/version"
|
||||
case let .string(str): return str
|
||||
}
|
||||
@@ -409,6 +413,8 @@ public enum ChatCommand {
|
||||
case .listRemoteCtrls: return "listRemoteCtrls"
|
||||
case .stopRemoteCtrl: return "stopRemoteCtrl"
|
||||
case .deleteRemoteCtrl: return "deleteRemoteCtrl"
|
||||
case .apiUploadFileDirectly: return "apiUploadFileDirectly"
|
||||
case .apiDownloadFileDirectly: return "apiDownloadFileDirectly"
|
||||
case .showVersion: return "showVersion"
|
||||
case .string: return "console command"
|
||||
}
|
||||
@@ -591,20 +597,25 @@ public enum ChatResponse: Decodable, Error {
|
||||
// receiving file events
|
||||
case rcvFileAccepted(user: UserRef, chatItem: AChatItem)
|
||||
case rcvFileAcceptedSndCancelled(user: UserRef, rcvFileTransfer: RcvFileTransfer)
|
||||
case rcvFileStart(user: UserRef, chatItem: AChatItem)
|
||||
case rcvFileProgressXFTP(user: UserRef, chatItem: AChatItem, receivedSize: Int64, totalSize: Int64)
|
||||
case rcvFileStart(user: UserRef, chatItem_: AChatItem?, rcvFileTransfer: RcvFileTransfer)
|
||||
case rcvFileProgressXFTP(user: UserRef, chatItem_: AChatItem?, receivedSize: Int64, totalSize: Int64, rcvFileTransfer: RcvFileTransfer)
|
||||
case rcvFileComplete(user: UserRef, chatItem: AChatItem)
|
||||
case rcvFileCompleteXFTP(user: UserRef, targetPath: String, rcvFileTransfer: RcvFileTransfer)
|
||||
case rcvFileCancelled(user: UserRef, chatItem: AChatItem, rcvFileTransfer: RcvFileTransfer)
|
||||
case rcvFileSndCancelled(user: UserRef, chatItem: AChatItem, rcvFileTransfer: RcvFileTransfer)
|
||||
case rcvFileError(user: UserRef, chatItem: AChatItem)
|
||||
case rcvFileError(user: UserRef, chatItem_: AChatItem?, rcvFileTransfer: RcvFileTransfer)
|
||||
// sending file events
|
||||
case sndFileStart(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer)
|
||||
case sndFileComplete(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer)
|
||||
case sndFileCancelled(user: UserRef, chatItem: AChatItem, fileTransferMeta: FileTransferMeta, sndFileTransfers: [SndFileTransfer])
|
||||
case sndFileRcvCancelled(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer)
|
||||
case sndFileProgressXFTP(user: UserRef, chatItem: AChatItem, fileTransferMeta: FileTransferMeta, sentSize: Int64, totalSize: Int64)
|
||||
case sndFileCompleteXFTP(user: UserRef, chatItem: AChatItem, fileTransferMeta: FileTransferMeta)
|
||||
case sndFileError(user: UserRef, chatItem: AChatItem)
|
||||
case sndFileRcvCancelled(user: UserRef, chatItem_: AChatItem?, sndFileTransfer: SndFileTransfer)
|
||||
case sndFileStartXFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta)
|
||||
case sndFileStartXFTPDirect(user: UserRef, fileTransferMeta: FileTransferMeta)
|
||||
case sndFileProgressXFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, sentSize: Int64, totalSize: Int64)
|
||||
case sndFileRedirectXFTP(user: UserRef, fileTransferMeta: FileTransferMeta, redirectMeta: FileTransferMeta)
|
||||
case sndFileCompleteXFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, rcvURIs: [String])
|
||||
case sndFileCancelledFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta)
|
||||
case sndFileError(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta)
|
||||
// call events
|
||||
case callInvitation(callInvitation: RcvCallInvitation)
|
||||
case callOffer(user: UserRef, contact: Contact, callType: CallType, offer: WebRTCSession, sharedKey: String?, askConfirmation: Bool)
|
||||
@@ -745,15 +756,20 @@ public enum ChatResponse: Decodable, Error {
|
||||
case .rcvFileStart: return "rcvFileStart"
|
||||
case .rcvFileProgressXFTP: return "rcvFileProgressXFTP"
|
||||
case .rcvFileComplete: return "rcvFileComplete"
|
||||
case .rcvFileCompleteXFTP: return "rcvFileCompleteXFTP"
|
||||
case .rcvFileCancelled: return "rcvFileCancelled"
|
||||
case .rcvFileSndCancelled: return "rcvFileSndCancelled"
|
||||
case .rcvFileError: return "rcvFileError"
|
||||
case .sndFileStart: return "sndFileStart"
|
||||
case .sndFileComplete: return "sndFileComplete"
|
||||
case .sndFileCancelled: return "sndFileCancelled"
|
||||
case .sndFileRcvCancelled: return "sndFileRcvCancelled"
|
||||
case .sndFileStartXFTP: return "sndFileStartXFTP"
|
||||
case .sndFileStartXFTPDirect: return "sndFileStartXFTPDirect"
|
||||
case .sndFileProgressXFTP: return "sndFileProgressXFTP"
|
||||
case .sndFileRedirectXFTP: return "sndFileRedirectXFTP"
|
||||
case .sndFileRcvCancelled: return "sndFileRcvCancelled"
|
||||
case .sndFileCompleteXFTP: return "sndFileCompleteXFTP"
|
||||
case .sndFileCancelledFTP: return "sndFileCancelledFTP"
|
||||
case .sndFileError: return "sndFileError"
|
||||
case .callInvitation: return "callInvitation"
|
||||
case .callOffer: return "callOffer"
|
||||
@@ -892,19 +908,24 @@ public enum ChatResponse: Decodable, Error {
|
||||
case let .newMemberContactReceivedInv(u, contact, groupInfo, member): return withUser(u, "contact: \(contact)\ngroupInfo: \(groupInfo)\nmember: \(member)")
|
||||
case let .rcvFileAccepted(u, chatItem): return withUser(u, String(describing: chatItem))
|
||||
case .rcvFileAcceptedSndCancelled: return noDetails
|
||||
case let .rcvFileStart(u, chatItem): return withUser(u, String(describing: chatItem))
|
||||
case let .rcvFileProgressXFTP(u, chatItem, receivedSize, totalSize): return withUser(u, "chatItem: \(String(describing: chatItem))\nreceivedSize: \(receivedSize)\ntotalSize: \(totalSize)")
|
||||
case let .rcvFileStart(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .rcvFileProgressXFTP(u, chatItem, receivedSize, totalSize, _): return withUser(u, "chatItem: \(String(describing: chatItem))\nreceivedSize: \(receivedSize)\ntotalSize: \(totalSize)")
|
||||
case let .rcvFileComplete(u, chatItem): return withUser(u, String(describing: chatItem))
|
||||
case let .rcvFileCompleteXFTP(u, targetPath, _): return withUser(u, targetPath)
|
||||
case let .rcvFileCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .rcvFileSndCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .rcvFileError(u, chatItem): return withUser(u, String(describing: chatItem))
|
||||
case let .rcvFileError(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileStart(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileComplete(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileCancelled(u, chatItem, _, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileStartXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case .sndFileStartXFTPDirect(_, _): return noDetails
|
||||
case let .sndFileRcvCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileProgressXFTP(u, chatItem, _, sentSize, totalSize): return withUser(u, "chatItem: \(String(describing: chatItem))\nsentSize: \(sentSize)\ntotalSize: \(totalSize)")
|
||||
case let .sndFileCompleteXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileError(u, chatItem): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileRedirectXFTP(u, _, redirectMeta): return withUser(u, String(describing: redirectMeta))
|
||||
case let .sndFileCompleteXFTP(u, chatItem, _, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileCancelledFTP(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileError(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .callInvitation(inv): return String(describing: inv)
|
||||
case let .callOffer(u, contact, callType, offer, sharedKey, askConfirmation): return withUser(u, "contact: \(contact.id)\ncallType: \(String(describing: callType))\nsharedKey: \(sharedKey ?? "")\naskConfirmation: \(askConfirmation)\noffer: \(String(describing: offer))")
|
||||
case let .callAnswer(u, contact, answer): return withUser(u, "contact: \(contact.id)\nanswer: \(String(describing: answer))")
|
||||
@@ -1732,6 +1753,7 @@ public enum StoreError: Decodable {
|
||||
case fileIdNotFoundBySharedMsgId(sharedMsgId: String)
|
||||
case sndFileNotFoundXFTP(agentSndFileId: String)
|
||||
case rcvFileNotFoundXFTP(agentRcvFileId: String)
|
||||
case extraFileDescrNotFoundXFTP(fileId: Int64)
|
||||
case connectionNotFound(agentConnId: String)
|
||||
case connectionNotFoundById(connId: Int64)
|
||||
case connectionNotFoundByMemberId(groupMemberId: Int64)
|
||||
|
||||
Reference in New Issue
Block a user