Skip to content

Commit 0c3ec90

Browse files
committed
feat: improve auto update ux by remove user attention seeking dialogs and flows
1 parent b9127c7 commit 0c3ec90

4 files changed

Lines changed: 50 additions & 28 deletions

File tree

src/extensionsIntegrated/appUpdater/main.js

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ define(function (require, exports, module) {
3737
Strings = require("strings"),
3838
marked = require('thirdparty/marked.min'),
3939
semver = require("thirdparty/semver.browser"),
40+
NotificationUI = require("widgets/NotificationUI"),
4041
TaskManager = require("features/TaskManager"),
4142
StringUtils = require("utils/StringUtils"),
4243
NativeApp = require("utils/NativeApp"),
@@ -48,6 +49,7 @@ define(function (require, exports, module) {
4849
KEY_UPDATE_AVAILABLE = "PH_UPDATE_AVAILABLE";
4950

5051
const PREFS_AUTO_UPDATE = "autoUpdate";
52+
let isAutoUpdateFlow = true;
5153

5254
function showOrHideUpdateIcon() {
5355
if(!updaterWindow){
@@ -56,17 +58,23 @@ define(function (require, exports, module) {
5658
if(updaterWindow && !updateTask) {
5759
updateTask = TaskManager.addNewTask(Strings.UPDATING_APP, Strings.UPDATING_APP_MESSAGE,
5860
`<i class="fa-solid fa-cogs"></i>`, {
61+
noSpinnerNotification: isAutoUpdateFlow, // for auto updates, don't get user attention with spinner
5962
onSelect: function () {
6063
if(updatePendingRestart){
61-
Dialogs.showInfoDialog(Strings.UPDATE_READY_RESTART_TITLE, Strings.UPDATE_READY_RESTART_MESSAGE);
64+
Dialogs.showInfoDialog(Strings.UPDATE_READY_RESTART_TITLE,
65+
Strings.UPDATE_READY_RESTART_INSTALL_MESSAGE);
6266
} else if(updateFailed){
6367
Dialogs.showInfoDialog(Strings.UPDATE_FAILED_TITLE, Strings.UPDATE_FAILED_MESSAGE);
64-
}else {
68+
} else {
6569
Dialogs.showInfoDialog(Strings.UPDATING_APP, Strings.UPDATING_APP_DIALOG_MESSAGE);
6670
}
6771
}
6872
});
69-
updateTask.show();
73+
if(!isAutoUpdateFlow) {
74+
updateTask.show();
75+
} else {
76+
updateTask.flashSpinnerForAttention();
77+
}
7078
}
7179
let updateAvailable = PreferencesManager.getViewState(KEY_UPDATE_AVAILABLE);
7280
if(updateAvailable){
@@ -243,6 +251,7 @@ define(function (require, exports, module) {
243251
}
244252

245253
async function checkForUpdates(isAutoUpdate) {
254+
isAutoUpdateFlow = isAutoUpdate;
246255
showOrHideUpdateIcon();
247256
if(!navigator.onLine) {
248257
return;
@@ -261,7 +270,8 @@ define(function (require, exports, module) {
261270
}
262271
if(updatePendingRestart || updateDetails.updatePendingRestart){
263272
if(!isAutoUpdate){
264-
Dialogs.showInfoDialog(Strings.UPDATE_READY_RESTART_TITLE, Strings.UPDATE_READY_RESTART_MESSAGE);
273+
Dialogs.showInfoDialog(Strings.UPDATE_READY_RESTART_TITLE,
274+
Strings.UPDATE_READY_RESTART_INSTALL_MESSAGE);
265275
// the dialog will only be shown in explicit check for updates, else its annoying that this comes
266276
// up at every new window create from app.
267277
}
@@ -302,8 +312,7 @@ define(function (require, exports, module) {
302312
DOWNLOADING: "DOWNLOADING",
303313
INSTALLER_DOWNLOADED: "INSTALLER_DOWNLOADED",
304314
FAILED: "FAILED",
305-
FAILED_UNKNOWN_OS: "FAILED_UNKNOWN_OS",
306-
INSTALLED: "INSTALLED"
315+
FAILED_UNKNOWN_OS: "FAILED_UNKNOWN_OS"
307316
};
308317

309318
function _sendUpdateCommand(command, data) {
@@ -536,22 +545,18 @@ define(function (require, exports, module) {
536545
updateTask.setFailed();
537546
updateTask.setMessage(Strings.UPDATE_FAILED_TITLE);
538547
Dialogs.showInfoDialog(Strings.UPDATE_FAILED_TITLE, Strings.UPDATE_FAILED_MESSAGE);
539-
} else if(data === UPDATE_STATUS.INSTALLED && !updateInstalledDialogShown){
540-
updateInstalledDialogShown = true;
541-
Metrics.countEvent(Metrics.EVENT_TYPE.UPDATES, 'done', Phoenix.platform);
542-
updatePendingRestart = true;
543-
updateTask.setSucceded();
544-
updateTask.setTitle(Strings.UPDATE_DONE);
545-
updateTask.setMessage(Strings.UPDATE_RESTART);
546-
Dialogs.showInfoDialog(Strings.UPDATE_READY_RESTART_TITLE, Strings.UPDATE_READY_RESTART_MESSAGE);
547548
} else if(data === UPDATE_STATUS.INSTALLER_DOWNLOADED){
548549
Metrics.countEvent(Metrics.EVENT_TYPE.UPDATES, 'downloaded', Phoenix.platform);
549550
updatePendingRestart = true;
550551
updateTask.setSucceded();
551552
updateTask.setTitle(Strings.UPDATE_DONE);
552553
updateTask.setMessage(Strings.UPDATE_RESTART_INSTALL);
553554
if(!updateInstalledDialogShown){
554-
Dialogs.showInfoDialog(Strings.UPDATE_READY_RESTART_TITLE, Strings.UPDATE_READY_RESTART_INSTALL_MESSAGE);
555+
NotificationUI.createToastFromTemplate(Strings.UPDATE_READY_RESTART_TITLE,
556+
`<div>${Strings.UPDATE_READY_RESTART_INSTALL_MESSAGE}</div>`, {
557+
toastStyle: NotificationUI.NOTIFICATION_STYLES_CSS_CLASS.SUCCESS,
558+
dismissOnClick: true
559+
});
555560
updateInstalledDialogShown = true;
556561
}
557562
_sendUpdateCommand(UPDATE_COMMANDS.GET_INSTALLER_LOCATION);

src/features/TaskManager.js

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ define(function (require, exports, module) {
9393
* determines what the spinner icon to show(green-for success), red-fail, blue normal based on the active
9494
* tasks in list and renders. IF the active tasks has already been notified, it wont notify again.
9595
*/
96-
function renderSpinnerIcon(newTaskAdded) {
96+
function renderSpinnerIcon(showNormalSpinnerIfNone) {
9797
let unackSuccessTaskFound = false;
9898
if(currentSpinnerType && currentSpinnerType !== SPINNER_NORMAL) {
9999
// there is a success/fail spinner visible, clean it. For the normal spinner, it will be
@@ -116,7 +116,7 @@ define(function (require, exports, module) {
116116

117117
// for normal spinner, we dont show anything as its only shown briefly till SPINNER_HIDE_TIME
118118
// which was already handled, except when newTaskAdded
119-
if(newTaskAdded) {
119+
if(showNormalSpinnerIfNone) {
120120
_showSpinnerIcon(SPINNER_NORMAL);
121121
}
122122
}
@@ -137,6 +137,7 @@ define(function (require, exports, module) {
137137
exports.taskSelect = taskSelect;
138138
exports.SPINNER_HIDE_TIME = SPINNER_HIDE_TIME;
139139
}
140+
hideSpinnerIcon();
140141
}
141142
function _renderItem(item, index) {
142143
if(item === Strings.STATUSBAR_TASKS_UNKNOWN_EXTENSION_TASK){
@@ -326,6 +327,7 @@ define(function (require, exports, module) {
326327
* @property {function(): void} hidePauseIcon - Hides the pause icon.
327328
* @property {function(string): void} showRestartIcon - Shows the restart (retry) icon with an optional tooltip message.
328329
* @property {function(): void} hideRestartIcon - Hides the restart (retry) icon.
330+
* @property {function(): void} flashSpinnerForAttention - briefly flashes the task spinner icon for attention.
329331
*/
330332

331333
/**
@@ -343,6 +345,8 @@ define(function (require, exports, module) {
343345
* @param {Function} [options.onRetryClick] - Callback function triggered when the retry button is clicked.
344346
* @param {Function} [options.onSelect] - Callback function triggered when the task is selected from the dropdown.
345347
* @param {number} [options.progressPercent] - Initial progress percentage of the task.
348+
* @param {boolean} [options.noSpinnerNotification] - If set to true, will not show the task spinners for this task.
349+
* This can be used for silent background tasks where user attention is not needed.
346350
* @returns {TaskObject} Returns a task object with methods for updating the task's state and UI representation,
347351
* such as `setProgressPercent`, `setMessage`, `setSucceeded`, `setFailed`, and control visibility methods
348352
* like `showStopIcon`, `hideStopIcon`, etc.
@@ -378,7 +382,8 @@ define(function (require, exports, module) {
378382
onStopClick: null,
379383
onRetryClick: null,
380384
onSelect: null,
381-
progressPercent: null
385+
progressPercent: null,
386+
noSpinnerNotification: false
382387
}) {
383388
if(!taskTitle){
384389
throw new Error("taskTitle is required to call addNewTask");
@@ -399,8 +404,10 @@ define(function (require, exports, module) {
399404
_percent: options && options.progressPercent,
400405
_completedStatus: STATUS_INCOMPLETE,
401406
_iconHTML: iconHTML,
402-
_spinnerIconAck: false // This is set when the user has seen the spinner icon spinning and clicked to see
403-
// weather the task succeeded or failed.
407+
_noSpinnerNotification: options && options.noSpinnerNotification,
408+
_spinnerIconAck: options ? !!options.noSpinnerNotification : false
409+
// Spinner ack is set when the user has seen the spinner icon spinning and clicked to see
410+
// weather the task succeeded or failed. Some tasks may want to be silent by setting noSpinnerNotification.
404411
};
405412
function close() {
406413
delete taskList[task._id];
@@ -451,7 +458,9 @@ define(function (require, exports, module) {
451458
function setFailed(){
452459
task._completedStatus = STATUS_FAIL;
453460
_renderProgressbar(task);
454-
task._spinnerIconAck= false;
461+
if(!task._noSpinnerNotification){
462+
task._spinnerIconAck= false;
463+
}
455464
renderSpinnerIcon();
456465
}
457466
function isFailed(){
@@ -460,7 +469,9 @@ define(function (require, exports, module) {
460469
function setSucceeded(){
461470
task._completedStatus = STATUS_SUCCESS;
462471
_renderProgressbar(task);
463-
task._spinnerIconAck= false;
472+
if(!task._noSpinnerNotification){
473+
task._spinnerIconAck= false;
474+
}
464475
renderSpinnerIcon();
465476
}
466477
function isSucceeded(){
@@ -499,6 +510,9 @@ define(function (require, exports, module) {
499510
task._showRestartIcon = null;
500511
_renderPlayIcons(task);
501512
}
513+
function flashSpinnerForAttention() {
514+
renderSpinnerIcon(true);
515+
}
502516

503517
task.show = show;
504518
task.close = close;
@@ -521,10 +535,15 @@ define(function (require, exports, module) {
521535
task.hidePauseIcon = hidePauseIcon;
522536
task.showRestartIcon = showRestartIcon;
523537
task.hideRestartIcon = hideRestartIcon;
538+
task.flashSpinnerForAttention = flashSpinnerForAttention;
524539
taskList[task._id] = task;
525540
EventDispatcher.makeEventDispatcher(task);
526541
_showOrHideStatusBarIfNeeded();
527-
renderSpinnerIcon(true);
542+
if(!task._noSpinnerNotification){
543+
renderSpinnerIcon(true);
544+
} else {
545+
renderSpinnerIcon();
546+
}
528547
return task;
529548
}
530549

src/nls/root/strings.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -612,9 +612,8 @@ define({
612612
"UPDATE_NOT_AVAILABLE_TITLE": "{APP_NAME} is up to date",
613613
"UPDATE_UP_TO_DATE": "Your version of {APP_NAME} is the latest!",
614614
"UPDATE_AVAILABLE_TITLE": "Update Available",
615-
"UPDATE_READY_RESTART_TITLE": "Update Ready: Restart Required",
616-
"UPDATE_READY_RESTART_MESSAGE": "Close all {APP_NAME} app windows and restart the app to launch the updated version.",
617-
"UPDATE_READY_RESTART_INSTALL_MESSAGE": "Update Successfully Downloaded: Please close all {APP_NAME} app windows to apply the latest updates.",
615+
"UPDATE_READY_RESTART_TITLE": "Restart App to Update",
616+
"UPDATE_READY_RESTART_INSTALL_MESSAGE": "{APP_NAME} will be updated automatically when you close all open windows.",
618617
"UPDATE_FAILED_TITLE": "Update Failed",
619618
"UPDATE_INSTALLING": "Installing Update\u2026",
620619
"UPDATE_INSTALLING_MESSAGE": "Update Installation in Progress: {APP_NAME} is currently installing the latest updates. The application will automatically close once the installation is complete.",

src/tauri-updater.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020
DOWNLOADING: "DOWNLOADING",
2121
INSTALLER_DOWNLOADED: "INSTALLER_DOWNLOADED",
2222
FAILED: "FAILED",
23-
FAILED_UNKNOWN_OS: "FAILED_UNKNOWN_OS",
24-
INSTALLED: "INSTALLED"
23+
FAILED_UNKNOWN_OS: "FAILED_UNKNOWN_OS"
2524
};
2625
let status = UPDATE_STATUS.STARTED;
2726
let progressPercent = 0, fileSize = 0, installerLocation;

0 commit comments

Comments
 (0)