From 3f0b0ea2e263783ee7779397a8cffcc746eca70a Mon Sep 17 00:00:00 2001 From: kamaji Date: Sat, 7 Feb 2026 16:07:53 -0600 Subject: [PATCH] Live-update queue list when upload status changes Storage.updatePhoto() now fires a 'photo-updated' CustomEvent so the queue page refreshes immediately (300ms debounce) when the sync engine changes a photo's status, instead of waiting for the 5s poll. Also reduces background poll to 30s (just a fallback now), and revokes stale ObjectURLs on each rebuild to prevent memory leaks. Co-Authored-By: Claude Opus 4.6 --- app/static/js/storage.js | 9 ++++++++- app/static/sw.js | 6 +++--- app/templates/queue.html | 21 +++++++++++++++++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/app/static/js/storage.js b/app/static/js/storage.js index 0fbf03c..76b54da 100644 --- a/app/static/js/storage.js +++ b/app/static/js/storage.js @@ -93,7 +93,14 @@ const Storage = { }, async updatePhoto(id, updates) { - return await this.db.photos.update(id, updates); + const result = await this.db.photos.update(id, updates); + // Notify any listening UI (e.g. queue page) of the change + try { + window.dispatchEvent(new CustomEvent('photo-updated', { + detail: { id: id, updates: updates } + })); + } catch (e) { /* ignore if no window context */ } + return result; }, async deletePhoto(id) { diff --git a/app/static/sw.js b/app/static/sw.js index 968ae06..e6b5724 100644 --- a/app/static/sw.js +++ b/app/static/sw.js @@ -1,9 +1,9 @@ // NextSnap Service Worker // Provides offline-first caching for the app shell -const CACHE_VERSION = 'nextsnap-v15'; -const APP_SHELL_CACHE = 'nextsnap-shell-v11'; -const RUNTIME_CACHE = 'nextsnap-runtime-v11'; +const CACHE_VERSION = 'nextsnap-v16'; +const APP_SHELL_CACHE = 'nextsnap-shell-v12'; +const RUNTIME_CACHE = 'nextsnap-runtime-v12'; // Assets to cache on install const APP_SHELL_ASSETS = [ diff --git a/app/templates/queue.html b/app/templates/queue.html index fb5d37e..7d33d26 100644 --- a/app/templates/queue.html +++ b/app/templates/queue.html @@ -411,6 +411,10 @@ async function loadQueue() { } emptyState.style.display = 'none'; + // Revoke old ObjectURLs before rebuilding to prevent memory leaks + queueList.querySelectorAll('img[src^="blob:"]').forEach(img => { + URL.revokeObjectURL(img.src); + }); queueList.innerHTML = ''; // Show active uploads first (newest first) @@ -610,10 +614,23 @@ window.addEventListener('offline', () => { document.getElementById('sync-now-btn').disabled = true; }); -// Refresh queue periodically +// Live updates: listen for photo status changes from the sync engine +let refreshTimer = null; +window.addEventListener('photo-updated', () => { + // Debounce: rapid status transitions (pending→uploading→verified) + // would otherwise trigger multiple full rebuilds + if (refreshTimer) clearTimeout(refreshTimer); + refreshTimer = setTimeout(() => { + refreshTimer = null; + loadQueue(); + updateStats(); + }, 300); +}); + +// Fallback: full refresh every 30s in case events are missed setInterval(() => { loadQueue(); updateStats(); -}, 5000); +}, 30000); {% endblock %}