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:
@@ -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 = [
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
Reference in New Issue
Block a user