diff --git a/DuckDuckGo/Preferences/Model/SyncPreferences.swift b/DuckDuckGo/Preferences/Model/SyncPreferences.swift index d9c412c322..b19e0d8c7b 100644 --- a/DuckDuckGo/Preferences/Model/SyncPreferences.swift +++ b/DuckDuckGo/Preferences/Model/SyncPreferences.swift @@ -267,15 +267,16 @@ extension SyncPreferences: ManagementDialogModelDelegate { } @MainActor - private func loginAndShowPresentedDialog(_ recoveryKey: SyncCode.RecoveryKey, shouldShowOptions: Bool) async throws { + private func loginAndShowPresentedDialog(_ recoveryKey: SyncCode.RecoveryKey, isActiveDevice: Bool) async throws { let device = deviceInfo() let knownDevices = Set(self.devices.map { $0.id }) let devices = try await syncService.login(recoveryKey, deviceName: device.name, deviceType: device.type) mapDevices(devices) let syncedDevices = self.devices.filter { !knownDevices.contains($0.id) && !$0.isCurrent } + let isSecondDevice = syncedDevices.count == 1 managementDialogModel.endFlow() - presentDialog(for: .deviceSynced(syncedDevices, shouldShowOptions: shouldShowOptions)) + presentDialog(for: .deviceSynced(syncedDevices, shouldShowOptions: isActiveDevice && isSecondDevice)) } func turnOnSync() { @@ -300,7 +301,7 @@ extension SyncPreferences: ManagementDialogModelDelegate { self.connector = try syncService.remoteConnect() self.codeToDisplay = connector?.code if let recoveryKey = try await connector?.pollForRecoveryKey() { - try await loginAndShowPresentedDialog(recoveryKey, shouldShowOptions: false) + try await loginAndShowPresentedDialog(recoveryKey, isActiveDevice: false) } else { // Polling was likeley cancelled elsewhere (e.g. dialog closed) return @@ -318,7 +319,7 @@ extension SyncPreferences: ManagementDialogModelDelegate { self.connector = nil } - func recoverDevice(using recoveryCode: String) { + func recoverDevice(using recoveryCode: String, isActiveDevice: Bool) { Task { @MainActor in do { guard let syncCode = try? SyncCode.decodeBase64String(recoveryCode) else { @@ -327,7 +328,7 @@ extension SyncPreferences: ManagementDialogModelDelegate { } if let recoveryKey = syncCode.recovery { // This will error if the account already exists, we don't have good UI for this just now - try await loginAndShowPresentedDialog(recoveryKey, shouldShowOptions: true) + try await loginAndShowPresentedDialog(recoveryKey, isActiveDevice: isActiveDevice) } else if let connectKey = syncCode.connect { if syncService.account == nil { let device = deviceInfo() @@ -335,6 +336,21 @@ extension SyncPreferences: ManagementDialogModelDelegate { } try await syncService.transmitRecoveryKey(connectKey) + self.$devices + .removeDuplicates() + .dropFirst() + .prefix(1) + .sink { [weak self] devices in + guard let self else { return } + let thisDeviceName = deviceInfo().name + var syncedDevices: [SyncDevice] = [] + for device in devices where device.name != thisDeviceName { + syncedDevices.append(device) + } + + self.managementDialogModel.endFlow() + presentDialog(for: .deviceSynced(syncedDevices, shouldShowOptions: devices.count == 2)) + }.store(in: &cancellables) // The UI will update when the devices list changes. } else { diff --git a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ManagementDialogModel.swift b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ManagementDialogModel.swift index d3e6cca43a..4e6c023147 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ManagementDialogModel.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ManagementDialogModel.swift @@ -22,7 +22,7 @@ import Combine public protocol ManagementDialogModelDelegate: AnyObject { var isUnifiedFavoritesEnabled: Bool { get set } - func recoverDevice(using recoveryCode: String) + func recoverDevice(using recoveryCode: String, isActiveDevice: Bool) func saveRecoveryPDF() func turnOffSync() func updateDeviceName(_ name: String) diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/Dialogs/RecoverAccountView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/Dialogs/RecoverAccountView.swift index 70901d4f8c..13e9a33aa9 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/Dialogs/RecoverAccountView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/Dialogs/RecoverAccountView.swift @@ -23,6 +23,7 @@ struct RecoverAccountView: View { @EnvironmentObject var model: ManagementDialogModel @EnvironmentObject var recoveryCodeModel: RecoveryCodeViewModel let isRecovery: Bool + let isActiveDevice: Bool var instructionText: String { if isRecovery { return UserText.recoverSyncedDataExplanation @@ -38,7 +39,7 @@ struct RecoverAccountView: View { } func submitRecoveryCode() { - model.delegate?.recoverDevice(using: recoveryCodeModel.recoveryCode) + model.delegate?.recoverDevice(using: recoveryCodeModel.recoveryCode, isActiveDevice: isActiveDevice) } var body: some View { diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementDialog.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementDialog.swift index 4efd7004fe..ee5e55a4db 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementDialog.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementDialog.swift @@ -55,9 +55,9 @@ public struct ManagementDialog: View { Group { switch model.currentDialog { case .recoverAccount: - RecoverAccountView(isRecovery: true) + RecoverAccountView(isRecovery: true, isActiveDevice: false) case .manuallyEnterCode: - RecoverAccountView(isRecovery: false) + RecoverAccountView(isRecovery: false, isActiveDevice: true) case .deviceSynced(let devices, let shouldShowOptions): DeviceSyncedView(devices: devices, shouldShowOptions: shouldShowOptions, isSingleDevice: false) case .firstDeviceSetup: