Fix queue list silent crash and missing uploaded status

Three fixes for queue list not updating:
1. Safe getBlob fallback - works even if old storage.js is cached
   by the service worker (no Storage.getBlob method)
2. Include 'uploaded' status in active filter - photos briefly in
   uploaded state (between upload and verify) were invisible
3. Wrap all interval refreshes in try/catch so one error doesn't
   silently kill all future updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-07 16:28:45 -06:00
parent 4e6fec43d3
commit 9047758c38
2 changed files with 29 additions and 19 deletions

View File

@@ -1,9 +1,9 @@
// NextSnap Service Worker
// Provides offline-first caching for the app shell
const CACHE_VERSION = 'nextsnap-v17';
const APP_SHELL_CACHE = 'nextsnap-shell-v13';
const RUNTIME_CACHE = 'nextsnap-runtime-v13';
const CACHE_VERSION = 'nextsnap-v18';
const APP_SHELL_CACHE = 'nextsnap-shell-v14';
const RUNTIME_CACHE = 'nextsnap-runtime-v14';
// Assets to cache on install
const APP_SHELL_ASSETS = [

View File

@@ -383,6 +383,15 @@
const currentUsername = '{{ username }}';
let deletePhotoId = null;
// Safe blob accessor — works even if old storage.js is cached without getBlob()
function safeGetBlob(photo) {
if (!photo || !photo.blob) return null;
if (typeof Storage.getBlob === 'function') return Storage.getBlob(photo);
if (photo.blob instanceof Blob) return photo.blob;
// ArrayBuffer stored by new storage.js but old storage.js loaded (no getBlob)
try { return new Blob([photo.blob], { type: 'image/jpeg' }); } catch (e) { return null; }
}
// Initialize storage and load queue
Storage.init().then(() => {
SyncEngine.init(currentUsername);
@@ -398,9 +407,11 @@ async function loadQueue() {
.where('username').equals(currentUsername)
.sortBy('timestamp');
// Split into active (pending/uploading) and completed (verified/failed)
const activePhotos = photos.filter(p => p.status === 'pending' || p.status === 'uploading');
const completedPhotos = photos.filter(p => p.status === 'verified' || p.status === 'failed');
// Split into active and completed
const activePhotos = photos.filter(p =>
p.status === 'pending' || p.status === 'uploading' || p.status === 'uploaded');
const completedPhotos = photos.filter(p =>
p.status === 'verified' || p.status === 'failed');
if (photos.length === 0) {
emptyState.style.display = 'block';
@@ -446,7 +457,7 @@ function createQueueItem(photo, isCompleted) {
item.classList.add('completed');
}
if (photo.status === 'uploading') {
if (photo.status === 'uploading' || photo.status === 'uploaded') {
item.classList.add('uploading');
} else if (photo.status === 'verified') {
item.classList.add('verified');
@@ -457,7 +468,7 @@ function createQueueItem(photo, isCompleted) {
// Create thumbnail (use placeholder if blob was stripped)
const thumbnail = document.createElement('div');
thumbnail.className = 'queue-item-thumbnail';
const photoBlob = Storage.getBlob(photo);
const photoBlob = safeGetBlob(photo);
if (photoBlob && photoBlob.size > 0) {
const img = document.createElement('img');
img.src = URL.createObjectURL(photoBlob);
@@ -558,7 +569,7 @@ async function updateStats() {
const pendingCount = photos.filter(p => p.status === 'pending').length;
const uploadingCount = photos.filter(p => p.status === 'uploading').length;
const totalSize = photos
.map(p => Storage.getBlob(p))
.map(p => safeGetBlob(p))
.filter(b => b && b.size)
.reduce((sum, b) => sum + b.size, 0) / 1024 / 1024;
@@ -621,25 +632,24 @@ function scheduleRefresh() {
if (refreshTimer) clearTimeout(refreshTimer);
refreshTimer = setTimeout(() => {
refreshTimer = null;
loadQueue();
updateStats();
refreshUI();
}, 300);
}
window.addEventListener('photo-updated', scheduleRefresh);
// Safe refresh wrapper — prevents silent failures from killing updates
async function refreshUI() {
try { await loadQueue(); } catch (e) { console.error('[QUEUE] loadQueue error:', e); }
try { await updateStats(); } catch (e) { console.error('[QUEUE] updateStats error:', e); }
}
// Poll every 3s while uploads are active, 15s when idle
let hasActiveUploads = true; // assume active until first check
setInterval(() => {
if (hasActiveUploads) {
loadQueue();
updateStats();
}
if (hasActiveUploads) refreshUI();
}, 3000);
// Slower fallback poll for idle state
setInterval(() => {
loadQueue();
updateStats();
}, 15000);
setInterval(refreshUI, 15000);
</script>
{% endblock %}