diff --git a/docs/API-Reference/command/Commands.md b/docs/API-Reference/command/Commands.md index 37a71de251..08e2feb9ff 100644 --- a/docs/API-Reference/command/Commands.md +++ b/docs/API-Reference/command/Commands.md @@ -854,6 +854,12 @@ Checks for updates ## HELP\_AUTO\_UPDATE Toggles auto update +**Kind**: global variable + + +## HELP\_MIGRATE\_DATA +Migrates browser data from the legacy web origin + **Kind**: global variable diff --git a/src/extensionsIntegrated/MigrateAssist/constants.js b/src/extensionsIntegrated/MigrateAssist/constants.js index 1db7f0e090..5b3073eef3 100644 --- a/src/extensionsIntegrated/MigrateAssist/constants.js +++ b/src/extensionsIntegrated/MigrateAssist/constants.js @@ -55,7 +55,7 @@ define(function (require, exports, module) { * The day the legacy origin stops serving. Month is 0 based, so 8 is September. * @type {number} */ - const SUNSET_DATE = Date.UTC(2026, 8, 10); + const SUNSET_DATE = Date.UTC(2026, 8, 1); /** * Android/ChromeOS Trusted Web Activity that wraps the legacy origin. Users launched from this @@ -72,6 +72,20 @@ define(function (require, exports, module) { */ const MIGRATION_DONE_KEY = "migrateAssist.v1.done"; + /** + * PhStore key holding how many times the transfer has actually been started. The automatic path + * gets MAX_AUTO_ATTEMPTS of them, so a single bad network moment does not cost the user their + * migration, while a persistently broken setup stops nagging and points at the Help menu. + * @type {string} + */ + const MIGRATION_ATTEMPTS_KEY = "migrateAssist.v1.attempts"; + + /** + * One attempt, and one retry. + * @type {number} + */ + const MAX_AUTO_ATTEMPTS = 2; + /** * Dev only override, so the whole cross origin flow can be exercised on one dev server. * http://localhost:8000 and http://127.0.0.1:8000 are different origins with separate IndexedDB @@ -203,6 +217,8 @@ define(function (require, exports, module) { exports.SUNSET_DATE = SUNSET_DATE; exports.TWA_STORE_URL = TWA_STORE_URL; exports.MIGRATION_DONE_KEY = MIGRATION_DONE_KEY; + exports.MIGRATION_ATTEMPTS_KEY = MIGRATION_ATTEMPTS_KEY; + exports.MAX_AUTO_ATTEMPTS = MAX_AUTO_ATTEMPTS; exports.getLegacyOrigin = getLegacyOrigin; exports.getMigrateAssistURL = getMigrateAssistURL; exports.getLegacyDomainName = getLegacyDomainName; diff --git a/src/extensionsIntegrated/MigrateAssist/main.js b/src/extensionsIntegrated/MigrateAssist/main.js index 76969e5363..49eb4cb1e8 100644 --- a/src/extensionsIntegrated/MigrateAssist/main.js +++ b/src/extensionsIntegrated/MigrateAssist/main.js @@ -39,6 +39,7 @@ define(function (require, exports, module) { Menus = require("command/Menus"), Strings = require("strings"), StringUtils = require("utils/StringUtils"), + Metrics = require("utils/Metrics"), Constants = require("./constants"), SunsetDialog = require("./sunset-dialog"), Migrator = require("./migrator"); @@ -51,6 +52,11 @@ define(function (require, exports, module) { } function _initNewOrigin() { + if (!Constants.isMigrationSupportedBrowser()) { + // Deliberately excluded, but worth counting: it is the difference between "nobody on + // Safari needed this" and "we never offered it to them". + Metrics.countEvent(Metrics.EVENT_TYPE.PLATFORM, "migrateAssist", "unsupportedBrowser"); + } // The menu entry is only registered here, so it can never show up on the legacy origin or on // desktop. It is also skipped on Safari/iOS, where the migration is deliberately not // implemented: offering an action we do not honour would be worse than not offering it. diff --git a/src/extensionsIntegrated/MigrateAssist/migrator.js b/src/extensionsIntegrated/MigrateAssist/migrator.js index b7276146bd..565fc33fab 100644 --- a/src/extensionsIntegrated/MigrateAssist/migrator.js +++ b/src/extensionsIntegrated/MigrateAssist/migrator.js @@ -35,6 +35,7 @@ define(function (require, exports, module) { const Dialogs = require("widgets/Dialogs"), DefaultDialogs = require("widgets/DefaultDialogs"), + NotificationUI = require("widgets/NotificationUI"), Strings = require("strings"), StringUtils = require("utils/StringUtils"), Metrics = require("utils/Metrics"), @@ -50,7 +51,7 @@ define(function (require, exports, module) { IFRAME_ID = "migrate-assist-frame"; const RESULT_MIGRATED = "migrated", - RESULT_DECLINED = "declined", + RESULT_INTERRUPTED = "interrupted", RESULT_NOTHING = "nothing", RESULT_UNREACHABLE = "unreachable"; @@ -213,54 +214,55 @@ define(function (require, exports, module) { } /** - * Shown once, before anything is copied, so the user knows why their machine is busy. + * Everything after the single up front question is reported at the bottom of the window rather + * than in another modal. The user opted in and went back to work; interrupting them again to say + * it finished would undo the point of moving progress out of a dialog in the first place. */ - function _confirmStart(fileCount) { - return Dialogs.showModalDialog( - DefaultDialogs.DIALOG_ID_INFO, - Strings.MIGRATE_PROGRESS_TITLE, - StringUtils.format(Strings.MIGRATE_START_MESSAGE, fileCount, Constants.getLegacyDomainName()), - [ - { - className: Dialogs.DIALOG_BTN_CLASS_NORMAL, - id: Dialogs.DIALOG_BTN_CANCEL, - text: Strings.CANCEL - }, - { - className: Dialogs.DIALOG_BTN_CLASS_PRIMARY, - id: Dialogs.DIALOG_BTN_OK, - text: Strings.MIGRATE_START_CONFIRM - } - ] - ).getPromise(); + /** + * Errors are the one thing that earns an interruption here. Everything else rides on the status + * bar task: if that is already telling the user what is happening, a dialog repeating it is just + * another click for them. + */ + function _errorDialog(title, message) { + Dialogs.showModalDialog(DefaultDialogs.DIALOG_ID_ERROR, title, message); + } + + function _toast(title, message, style, $extra) { + const $content = $("