diff --git a/app/static/sw.js b/app/static/sw.js index e6b5724..6b0a85d 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-v16'; -const APP_SHELL_CACHE = 'nextsnap-shell-v12'; -const RUNTIME_CACHE = 'nextsnap-runtime-v12'; +const CACHE_VERSION = 'nextsnap-v17'; +const APP_SHELL_CACHE = 'nextsnap-shell-v13'; +const RUNTIME_CACHE = 'nextsnap-runtime-v13'; // Assets to cache on install const APP_SHELL_ASSETS = [ diff --git a/app/templates/queue.html b/app/templates/queue.html index 7d33d26..9d95fd4 100644 --- a/app/templates/queue.html +++ b/app/templates/queue.html @@ -434,7 +434,8 @@ async function loadQueue() { } } - document.getElementById('sync-now-btn').disabled = activePhotos.length === 0 || !navigator.onLine; + hasActiveUploads = activePhotos.length > 0; + document.getElementById('sync-now-btn').disabled = !hasActiveUploads || !navigator.onLine; } function createQueueItem(photo, isCompleted) { @@ -616,21 +617,29 @@ window.addEventListener('offline', () => { // 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 +function scheduleRefresh() { if (refreshTimer) clearTimeout(refreshTimer); refreshTimer = setTimeout(() => { refreshTimer = null; loadQueue(); updateStats(); }, 300); -}); +} +window.addEventListener('photo-updated', scheduleRefresh); -// Fallback: full refresh every 30s in case events are missed +// Poll every 3s while uploads are active, 15s when idle +let hasActiveUploads = true; // assume active until first check +setInterval(() => { + if (hasActiveUploads) { + loadQueue(); + updateStats(); + } +}, 3000); + +// Slower fallback poll for idle state setInterval(() => { loadQueue(); updateStats(); -}, 30000); +}, 15000); {% endblock %}