feat: clarify update progress states
Use a full-width green download track, preserve completed progress in red when cancellation or failure occurs, move status copy below the track, and smooth percentage transitions.
This commit is contained in:
+14
-11
@@ -7492,7 +7492,7 @@ function showUpdateBanner(info) {
|
|||||||
installButton.disabled = false;
|
installButton.disabled = false;
|
||||||
installButton.textContent = 'Jetzt installieren';
|
installButton.textContent = 'Jetzt installieren';
|
||||||
}
|
}
|
||||||
_setUpdateProgress(0, 'Bereit zum Download');
|
_setUpdateProgress(0, 'Bereit zum Download', 'ready');
|
||||||
_setUpdateDialogBusy(false);
|
_setUpdateDialogBusy(false);
|
||||||
_syncHeaderUpdateState();
|
_syncHeaderUpdateState();
|
||||||
_setUpdateDialogVisible(true);
|
_setUpdateDialogVisible(true);
|
||||||
@@ -7505,38 +7505,39 @@ function handleUpdateProgress(data) {
|
|||||||
if (progress.stage === 'starting') {
|
if (progress.stage === 'starting') {
|
||||||
_updateInstallBusy = true;
|
_updateInstallBusy = true;
|
||||||
_setUpdateDialogBusy(true, true);
|
_setUpdateDialogBusy(true, true);
|
||||||
_setUpdateProgress(0, 'Download 0%');
|
_setUpdateProgress(0, 'Download 0%', 'downloading');
|
||||||
if (message) message.hidden = true;
|
if (message) message.hidden = true;
|
||||||
if (button) button.textContent = 'Download 0%';
|
if (button) button.textContent = 'Download 0%';
|
||||||
} else if (progress.stage === 'downloading') {
|
} else if (progress.stage === 'downloading') {
|
||||||
const percent = Math.max(0, Math.min(100, Math.round(Number(progress.percent) || 0)));
|
const percent = Math.max(0, Math.min(100, Math.round(Number(progress.percent) || 0)));
|
||||||
_updateInstallBusy = true;
|
_updateInstallBusy = true;
|
||||||
_setUpdateDialogBusy(true, true);
|
_setUpdateDialogBusy(true, true);
|
||||||
_setUpdateProgress(percent, `Download ${percent}%`);
|
_setUpdateProgress(percent, `Download ${percent}%`, 'downloading');
|
||||||
if (message) message.hidden = true;
|
if (message) message.hidden = true;
|
||||||
if (button) button.textContent = `Download ${percent}%`;
|
if (button) button.textContent = `Download ${percent}%`;
|
||||||
} else if (progress.stage === 'verifying') {
|
} else if (progress.stage === 'verifying') {
|
||||||
_updateInstallBusy = true;
|
_updateInstallBusy = true;
|
||||||
_setUpdateDialogBusy(true, false);
|
_setUpdateDialogBusy(true, false);
|
||||||
_setUpdateProgress(100, 'Prüfen…');
|
_setUpdateProgress(100, 'Prüfen…', 'verifying');
|
||||||
if (message) message.hidden = true;
|
if (message) message.hidden = true;
|
||||||
if (button) button.textContent = 'Prüfen…';
|
if (button) button.textContent = 'Prüfen…';
|
||||||
} else if (progress.stage === 'prepared') {
|
} else if (progress.stage === 'prepared') {
|
||||||
_updateInstallBusy = true;
|
_updateInstallBusy = true;
|
||||||
_setUpdateDialogBusy(true, false);
|
_setUpdateDialogBusy(true, false);
|
||||||
_setUpdateProgress(100, 'Neustart…');
|
_setUpdateProgress(100, 'Neustart…', 'prepared');
|
||||||
if (message) message.hidden = true;
|
if (message) message.hidden = true;
|
||||||
if (button) button.textContent = 'Neustart…';
|
if (button) button.textContent = 'Neustart…';
|
||||||
} else if (progress.stage === 'launching' || progress.stage === 'done') {
|
} else if (progress.stage === 'launching' || progress.stage === 'done') {
|
||||||
_updateInstallBusy = true;
|
_updateInstallBusy = true;
|
||||||
_setUpdateDialogBusy(true, false);
|
_setUpdateDialogBusy(true, false);
|
||||||
_setUpdateProgress(100, 'Neustart…');
|
_setUpdateProgress(100, 'Neustart…', 'prepared');
|
||||||
if (message) message.hidden = true;
|
if (message) message.hidden = true;
|
||||||
if (button) button.textContent = 'Neustart…';
|
if (button) button.textContent = 'Neustart…';
|
||||||
} else if (progress.stage === 'aborted') {
|
} else if (progress.stage === 'aborted') {
|
||||||
_updateInstallBusy = false;
|
_updateInstallBusy = false;
|
||||||
_setUpdateDialogBusy(false);
|
_setUpdateDialogBusy(false);
|
||||||
_setUpdateProgress(0, 'Download abgebrochen');
|
const currentProgress = Number(document.getElementById('updateProgressBar')?.getAttribute('aria-valuenow')) || 0;
|
||||||
|
_setUpdateProgress(currentProgress, 'Download abgebrochen', 'aborted');
|
||||||
if (message) {
|
if (message) {
|
||||||
message.hidden = false;
|
message.hidden = false;
|
||||||
message.textContent = 'Download abgebrochen';
|
message.textContent = 'Download abgebrochen';
|
||||||
@@ -7549,7 +7550,8 @@ function handleUpdateProgress(data) {
|
|||||||
} else if (progress.stage === 'error') {
|
} else if (progress.stage === 'error') {
|
||||||
_updateInstallBusy = false;
|
_updateInstallBusy = false;
|
||||||
_setUpdateDialogBusy(false);
|
_setUpdateDialogBusy(false);
|
||||||
_setUpdateProgress(0, 'Update fehlgeschlagen');
|
const currentProgress = Number(document.getElementById('updateProgressBar')?.getAttribute('aria-valuenow')) || 0;
|
||||||
|
_setUpdateProgress(currentProgress, 'Update fehlgeschlagen', 'error');
|
||||||
if (message) {
|
if (message) {
|
||||||
message.hidden = false;
|
message.hidden = false;
|
||||||
message.textContent = `Update fehlgeschlagen: ${String(progress.error || 'Unbekannter Fehler').slice(0, 400)}`;
|
message.textContent = `Update fehlgeschlagen: ${String(progress.error || 'Unbekannter Fehler').slice(0, 400)}`;
|
||||||
@@ -7669,7 +7671,7 @@ async function handleUpdateDismiss() {
|
|||||||
if (!_updateDownloadCancelable || _updateCancelBusy) return false;
|
if (!_updateDownloadCancelable || _updateCancelBusy) return false;
|
||||||
_updateCancelBusy = true;
|
_updateCancelBusy = true;
|
||||||
_setUpdateDialogBusy(true, true);
|
_setUpdateDialogBusy(true, true);
|
||||||
_setUpdateProgress(Number(document.getElementById('updateProgressBar')?.getAttribute('aria-valuenow')) || 0, 'Abbrechen…');
|
_setUpdateProgress(Number(document.getElementById('updateProgressBar')?.getAttribute('aria-valuenow')) || 0, 'Abbrechen…', 'aborted');
|
||||||
try {
|
try {
|
||||||
const canceled = await window.api.abortUpdate();
|
const canceled = await window.api.abortUpdate();
|
||||||
if (!canceled) handleUpdateProgress({ stage: 'error', error: 'Update konnte nicht abgebrochen werden' });
|
if (!canceled) handleUpdateProgress({ stage: 'error', error: 'Update konnte nicht abgebrochen werden' });
|
||||||
@@ -7699,7 +7701,7 @@ function _setUpdateDialogBusy(busy, cancelable = false) {
|
|||||||
if (busy && dialog && (!dialog.contains(document.activeElement) || document.activeElement.matches?.(':disabled'))) dialog.focus();
|
if (busy && dialog && (!dialog.contains(document.activeElement) || document.activeElement.matches?.(':disabled'))) dialog.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _setUpdateProgress(percent, text) {
|
function _setUpdateProgress(percent, text, state = 'ready') {
|
||||||
const value = Math.max(0, Math.min(100, Math.round(Number(percent) || 0)));
|
const value = Math.max(0, Math.min(100, Math.round(Number(percent) || 0)));
|
||||||
const progressText = document.getElementById('updateProgressText');
|
const progressText = document.getElementById('updateProgressText');
|
||||||
const progressBar = document.getElementById('updateProgressBar');
|
const progressBar = document.getElementById('updateProgressBar');
|
||||||
@@ -7707,6 +7709,7 @@ function _setUpdateProgress(percent, text) {
|
|||||||
if (progressBar) {
|
if (progressBar) {
|
||||||
progressBar.setAttribute('aria-valuenow', String(value));
|
progressBar.setAttribute('aria-valuenow', String(value));
|
||||||
progressBar.setAttribute('aria-valuetext', String(text || `${value}%`));
|
progressBar.setAttribute('aria-valuetext', String(text || `${value}%`));
|
||||||
|
progressBar.dataset.state = state;
|
||||||
if ('value' in progressBar) progressBar.value = value;
|
if ('value' in progressBar) progressBar.value = value;
|
||||||
progressBar.style.width = `${value}%`;
|
progressBar.style.width = `${value}%`;
|
||||||
}
|
}
|
||||||
@@ -7720,7 +7723,7 @@ async function installKnownUpdate() {
|
|||||||
}
|
}
|
||||||
_updateInstallBusy = true;
|
_updateInstallBusy = true;
|
||||||
_setUpdateDialogBusy(true, true);
|
_setUpdateDialogBusy(true, true);
|
||||||
_setUpdateProgress(0, 'Download 0%');
|
_setUpdateProgress(0, 'Download 0%', 'downloading');
|
||||||
const message = document.getElementById('updateMessage');
|
const message = document.getElementById('updateMessage');
|
||||||
const button = document.getElementById('installUpdateBtn');
|
const button = document.getElementById('installUpdateBtn');
|
||||||
if (message) message.hidden = true;
|
if (message) message.hidden = true;
|
||||||
|
|||||||
+10
-5
@@ -2424,9 +2424,8 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
|||||||
.update-progress {
|
.update-progress {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1fr) auto;
|
grid-template-columns: minmax(0, 1fr);
|
||||||
align-items: center;
|
gap: 7px;
|
||||||
gap: 10px;
|
|
||||||
margin-top: 22px;
|
margin-top: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2473,12 +2472,18 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
background: var(--accent);
|
background: var(--success);
|
||||||
transition: width .2s ease;
|
transition: width .48s cubic-bezier(.22, 1, .36, 1), background-color .16s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-progress-track span[data-state="aborted"],
|
||||||
|
.update-progress-track span[data-state="error"] {
|
||||||
|
background: var(--danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateProgressText {
|
#updateProgressText {
|
||||||
min-width: 34px;
|
min-width: 34px;
|
||||||
|
justify-self: end;
|
||||||
color: var(--text-dim);
|
color: var(--text-dim);
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|||||||
+9
-3
@@ -2475,15 +2475,21 @@ setTimeout(async () => {
|
|||||||
messageHidden: document.getElementById('updateMessage').hidden,
|
messageHidden: document.getElementById('updateMessage').hidden,
|
||||||
messageText: document.getElementById('updateMessage').textContent,
|
messageText: document.getElementById('updateMessage').textContent,
|
||||||
progressLabel: progress.getAttribute('aria-label'),
|
progressLabel: progress.getAttribute('aria-label'),
|
||||||
progressText: progress.getAttribute('aria-valuetext')
|
progressText: progress.getAttribute('aria-valuetext'),
|
||||||
|
progressState: progress.dataset.state,
|
||||||
|
progressColor: getComputedStyle(progress).backgroundColor,
|
||||||
|
progressTrackWidth: progress.parentElement.getBoundingClientRect().width,
|
||||||
|
progressTextTop: document.getElementById('updateProgressText').getBoundingClientRect().top,
|
||||||
|
progressTrackBottom: progress.parentElement.getBoundingClientRect().bottom
|
||||||
};
|
};
|
||||||
})()\`);
|
})()\`);
|
||||||
check('Busy update keeps its progress dialog open with a dangerous download cancellation action', busyUpdateState.display === 'flex' && busyUpdateState.hidden === 'false' && busyUpdateState.closeDisabled === true && busyUpdateState.dismissDisabled === false && busyUpdateState.dismissText === 'Download abbrechen' && busyUpdateState.dismissDanger === true && busyUpdateState.headerHidden === false);
|
check('Busy update keeps its progress dialog open with a dangerous download cancellation action', busyUpdateState.display === 'flex' && busyUpdateState.hidden === 'false' && busyUpdateState.closeDisabled === true && busyUpdateState.dismissDisabled === false && busyUpdateState.dismissText === 'Download abbrechen' && busyUpdateState.dismissDanger === true && busyUpdateState.headerHidden === false);
|
||||||
check('Update progress exposes an accessible live value', busyUpdateState.progressLabel === 'Update-Fortschritt' && busyUpdateState.progressText === 'Download 50%');
|
check('Update progress exposes an accessible live value', busyUpdateState.progressLabel === 'Update-Fortschritt' && busyUpdateState.progressText === 'Download 50%');
|
||||||
|
check('Update download progress is green, wide, and places its status below the line', busyUpdateState.progressState === 'downloading' && busyUpdateState.progressColor === 'rgb(117, 211, 155)' && busyUpdateState.progressTrackWidth >= 390 && busyUpdateState.progressTextTop >= busyUpdateState.progressTrackBottom);
|
||||||
check('Busy update shows progress only below the bar', busyUpdateState.messageHidden === true && busyUpdateState.messageText === 'Update v9.9.9 verfügbar');
|
check('Busy update shows progress only below the bar', busyUpdateState.messageHidden === true && busyUpdateState.messageText === 'Update v9.9.9 verfügbar');
|
||||||
|
|
||||||
const updateErrorRecovery = await wc.executeJavaScript('handleUpdateProgress({ stage: "error", error: "Netzwerkfehler" }); document.getElementById("dismissUpdateBtn").click(); document.getElementById("updateBanner").style.display + "|" + document.getElementById("updateCloseBtn").disabled + "|" + document.getElementById("dismissUpdateBtn").disabled + "|" + document.getElementById("headerUpdateBtn").hidden');
|
const updateErrorRecovery = await wc.executeJavaScript('handleUpdateProgress({ stage: "aborted", error: "Update abgebrochen" }); (() => { const bar = document.getElementById("updateProgressBar"); const state = [bar.style.width, bar.dataset.state, getComputedStyle(bar).backgroundColor].join("|"); document.getElementById("dismissUpdateBtn").click(); return state + "|" + document.getElementById("updateBanner").style.display + "|" + document.getElementById("updateCloseBtn").disabled + "|" + document.getElementById("dismissUpdateBtn").disabled + "|" + document.getElementById("headerUpdateBtn").hidden; })()');
|
||||||
check('Update errors restore all close actions', updateErrorRecovery === 'none|false|false|false');
|
check('Canceled updates preserve progress in red and restore all close actions', updateErrorRecovery === '50%|aborted|rgb(255, 133, 140)|none|false|false|false');
|
||||||
|
|
||||||
const initialInstallUpdateHandler = initialIpcHandlers.get('app:install-update');
|
const initialInstallUpdateHandler = initialIpcHandlers.get('app:install-update');
|
||||||
const initialUpdateQueueHandler = initialIpcHandlers.get('save-pending-queue');
|
const initialUpdateQueueHandler = initialIpcHandlers.get('save-pending-queue');
|
||||||
|
|||||||
Reference in New Issue
Block a user