feat(backup): auto-refresh the backup page when a backup/restore finishes

The page has no live feed, so a completed backup/restore wasn't reflected
until a manual Refresh or re-navigation. Subscribe to the TaskEventBus
'taskCompleted' event and repaint on backup/restore completions. Debounced
to coalesce the burst when several per-app tasks finish together; only the
mounted instance reacts and a stale listener removes itself. The Refresh
button stays as a manual pull.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-05-28 18:25:24 +01:00
parent 7a302d1af0
commit d448d34f67

View File

@ -98,6 +98,8 @@ class BackupPage {
this.engines = []; // [{id,name,supported_types}, ...] — fetched once
this.taskManager = (typeof TaskManager !== 'undefined') ? new TaskManager() : null;
this.eventBound = false;
this._taskRefreshTimer = null;
this._onTaskCompleted = this._onTaskCompleted.bind(this);
}
async init() {
@ -111,6 +113,29 @@ class BackupPage {
this.updatePrimaryAction();
}
/* Backups/restores complete asynchronously and the page has no live feed,
so repaint when one finishes (snapshot lists, last-backup times, sizes).
The Refresh button stays as a manual pull. Debounced because picking
several apps queues a task each, which finish in a burst. Only the
mounted instance reacts; a stale listener removes itself. */
_onTaskCompleted(e) {
if (window.backupPage !== this || !document.getElementById('backup-page')) {
window.removeEventListener('taskCompleted', this._onTaskCompleted);
return;
}
const d = e?.detail || {};
const cmd = d.task?.command || '';
const relevant = d.action === 'backup' || d.action === 'restore'
|| /^libreportal\s+(backup|restore)\b/.test(cmd);
if (!relevant) return;
clearTimeout(this._taskRefreshTimer);
this._taskRefreshTimer = setTimeout(() => {
if (window.backupPage === this && document.getElementById('backup-page')) {
this.refreshAll().then(() => this.render());
}
}, 600);
}
/* Read the active tab slug from window.location, supporting both
/backup?=dashboard (the legacy libreportal ?= form used on /config)
and /backup?backup=dashboard (standard query string) so links from
@ -146,6 +171,8 @@ class BackupPage {
if (this.eventBound) return;
this.eventBound = true;
window.addEventListener('taskCompleted', this._onTaskCompleted);
// Browser back/forward is handled by the SPA's popstate listener —
// pushTabToUrl includes a `route` field in state so the SPA's
// handler picks it up and re-runs handleBackup, which re-parses