Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync Flow Error Handling #2264

Merged
merged 6 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 62 additions & 12 deletions DuckDuckGo/SyncSettingsViewController+SyncDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate {
let devices = try await syncService.updateDeviceName(name)
mapDevices(devices)
} catch {
handleError(error)
handleError(SyncError.unableToUpdateDeviceName, error: error)
}
}
}
Expand All @@ -61,15 +61,33 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate {
self.refreshDevices()
navigationController?.topViewController?.dismiss(animated: true, completion: showRecoveryPDF)
} catch {
handleError(error)
handleError(SyncError.unableToSync, error: error)
}
}
}

@MainActor
func handleError(_ error: Error) {
// Work out how to handle this properly later
assertionFailure(error.localizedDescription)
func handleError(_ type: SyncError, error: Error) {
let alertController = UIAlertController(
title: type.title,
message: type.description + "\n" + error.localizedDescription,
preferredStyle: .alert)

let okAction = UIAlertAction(title: UserText.syncPausedAlertOkButton, style: .default, handler: nil)
alertController.addAction(okAction)

if type == .unableToSync {
// Give time to the is syncing view to appear
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.dismissPresentedViewController { [weak self] in
self?.present(alertController, animated: true, completion: nil)
}
}
} else {
self.dismissPresentedViewController { [weak self] in
self?.present(alertController, animated: true, completion: nil)
}
}
}

func showSyncWithAnotherDevice() {
Expand Down Expand Up @@ -158,14 +176,15 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate {
alert.addAction(title: UserText.syncTurnOffConfirmAction, style: .destructive) {
Task { @MainActor in
do {
self.rootView.model.isSyncEnabled = false
try await self.syncService.disconnect()
self.rootView.model.isSyncEnabled = false
AppUserDefaults().isSyncBookmarksPaused = false
AppUserDefaults().isSyncCredentialsPaused = false
continuation.resume(returning: true)
} catch {
self.handleError(error)
self.handleError(SyncError.unableToTurnSyncOff, error: error)
continuation.resume(returning: false)
}
continuation.resume(returning: true)
}
}
self.present(alert, animated: true)
Expand All @@ -183,14 +202,15 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate {
alert.addAction(title: UserText.syncDeleteAllConfirmAction, style: .destructive) {
Task { @MainActor in
do {
self.rootView.model.isSyncEnabled = false
try await self.syncService.deleteAccount()
self.rootView.model.isSyncEnabled = false
AppUserDefaults().isSyncBookmarksPaused = false
AppUserDefaults().isSyncCredentialsPaused = false
continuation.resume(returning: true)
} catch {
self.handleError(error)
self.handleError(SyncError.unableToDeleteData, error: error)
continuation.resume(returning: false)
}
continuation.resume(returning: true)
}
}
self.present(alert, animated: true)
Expand Down Expand Up @@ -224,7 +244,7 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate {
try await syncService.disconnect(deviceId: device.id)
refreshDevices()
} catch {
handleError(error)
handleError(SyncError.unableToRemoveDevice, error: error)
}
}
}
Expand Down Expand Up @@ -261,3 +281,33 @@ private class PortraitNavigationController: UINavigationController {
}

}

enum SyncError {
case unableToSync
case unableToGetDevices
case unableToUpdateDeviceName
case unableToTurnSyncOff
case unableToDeleteData
case unableToRemoveDevice

var title: String {
return UserText.syncErrorAlertTitle
}

var description: String {
switch self {
case .unableToSync:
return UserText.unableToSyncDescription
case .unableToGetDevices:
return UserText.unableToGetDevicesDescription
case .unableToUpdateDeviceName:
return UserText.unableToUpdateDeviceNameDescription
case .unableToTurnSyncOff:
return UserText.unableToTurnSyncOffDescription
case .unableToDeleteData:
return UserText.unableToDeleteDataDescription
case .unableToRemoveDevice:
return UserText.unableToRemoveDeviceDescription
}
}
}
19 changes: 12 additions & 7 deletions DuckDuckGo/SyncSettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Core
import Combine
import SyncUI
import DDGSync
import Common

@MainActor
class SyncSettingsViewController: UIHostingController<SyncSettingsView> {
Expand Down Expand Up @@ -171,10 +172,13 @@ class SyncSettingsViewController: UIHostingController<SyncSettingsView> {
}
}

func dismissPresentedViewController() {
func dismissPresentedViewController(completion: (() -> Void)? = nil) {
guard let presentedViewController = navigationController?.presentedViewController,
!(presentedViewController is UIHostingController<SyncSettingsView>) else { return }
presentedViewController.dismiss(animated: true, completion: nil)
!(presentedViewController is UIHostingController<SyncSettingsView>) else {
completion?()
return
}
presentedViewController.dismiss(animated: true, completion: completion)
endConnectMode()
}

Expand All @@ -190,7 +194,8 @@ class SyncSettingsViewController: UIHostingController<SyncSettingsView> {
let devices = try await syncService.fetchDevices()
mapDevices(devices)
} catch {
handleError(error)
// Not displaying error since there is the spinner and it is called every few seconds
os_log(error.localizedDescription, log: .syncLog, type: .error)
}
}
}
Expand Down Expand Up @@ -223,7 +228,7 @@ extension SyncSettingsViewController: ScanOrPasteCodeViewModelDelegate {
self.startPolling()
return self.connector?.code
} catch {
self.handleError(error)
self.handleError(SyncError.unableToSync, error: error)
return nil
}
}
Expand All @@ -249,7 +254,7 @@ extension SyncSettingsViewController: ScanOrPasteCodeViewModelDelegate {
return
}
} catch {
handleError(error)
handleError(SyncError.unableToSync, error: error)
}
}
}
Expand Down Expand Up @@ -292,7 +297,7 @@ extension SyncSettingsViewController: ScanOrPasteCodeViewModelDelegate {
}

} catch {
handleError(error)
handleError(SyncError.unableToSync, error: error)
}
return false
}
Expand Down
7 changes: 7 additions & 0 deletions DuckDuckGo/UserText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,13 @@ But if you *do* want a peek under the hood, you can find more information about
static let syncCredentialsPausedAlertDescription = NSLocalizedString("alert.sync-credentials-paused-description", value: "You have exceeded the passwords sync limit. Try deleting some passwords. Until this is resolved your passwords will not be backed up.", comment: "Description for alert shown when sync credentials paused for too many items")
public static let syncPausedAlertOkButton = NSLocalizedString("alert.sync-paused-alert-ok-button", value: "OK", comment: "Confirmation button in alert")
public static let syncPausedAlertLearnMoreButton = NSLocalizedString("alert.sync-paused-alert-learn-more-button", value: "Learn More", comment: "Learn more button in alert")
public static let syncErrorAlertTitle = NSLocalizedString("alert.sync-error", value: "Sync Error", comment: "Title for sync error alert")
public static let unableToSyncDescription = NSLocalizedString("alert.unable-to-sync-description", value: "Unable to sync.", comment: "Description for unable to sync error")
public static let unableToGetDevicesDescription = NSLocalizedString("alert.unable-to-get-devices-description", value: "Unable to retrieve the list of connected devices.", comment: "Description for unable to get devices error")
public static let unableToUpdateDeviceNameDescription = NSLocalizedString("alert.unable-to-update-device-name-description", value: "Unable to update the name of the device.", comment: "Description for unable to update device name error")
public static let unableToTurnSyncOffDescription = NSLocalizedString("alert.unable-to-turn-sync-off-description", value: "Unable to turn sync off.", comment: "Description for unable to turn sync off error")
public static let unableToDeleteDataDescription = NSLocalizedString("alert.unable-to-delete-data-description", value: "Unable to delete data on the server.", comment: "Description for unable to delete data error")
public static let unableToRemoveDeviceDescription = NSLocalizedString("alert.unable-to-remove-device-description", value: "Unable to remove the specified device from the synchronized devices.", comment: "Description for unable to remove device error")

static let preemptiveCrashTitle = NSLocalizedString("error.preemptive-crash.title", value: "App issue detected", comment: "Alert title")
static let preemptiveCrashBody = NSLocalizedString("error.preemptive-crash.body", value: "Looks like there's an issue with the app and it needs to close. Please reopen to continue.", comment: "Alert message")
Expand Down
21 changes: 21 additions & 0 deletions DuckDuckGo/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@
/* Title for alert shown when sync credentials paused for too many items */
"alert.sync-credentials-paused-title" = "Passwords Sync is Paused";

/* Title for sync error alert */
"alert.sync-error" = "Sync Error";

/* Learn more button in alert */
"alert.sync-paused-alert-learn-more-button" = "Learn More";

Expand All @@ -160,6 +163,24 @@
/* Save Favorite action */
"alert.title.save.favorite" = "Save Favorite";

/* Description for unable to delete data error */
"alert.unable-to-delete-data-description" = "Unable to delete data on the server.";

/* Description for unable to get devices error */
"alert.unable-to-get-devices-description" = "Unable to retrieve the list of connected devices.";

/* Description for unable to remove device error */
"alert.unable-to-remove-device-description" = "Unable to remove the specified device from the synchronized devices.";

/* Description for unable to sync error */
"alert.unable-to-sync-description" = "Unable to sync.";

/* Description for unable to turn sync off error */
"alert.unable-to-turn-sync-off-description" = "Unable to turn sync off.";

/* Description for unable to update device name error */
"alert.unable-to-update-device-name-description" = "Unable to update the name of the device.";

/* Shown on authentication screen */
"app.authentication.unlock" = "Unlock DuckDuckGo.";

Expand Down
Loading