Transient network errors on first upload attempt showed ugly
'Network error: TypeError: Load failed' messages in the queue.
These are normal on iOS Safari and auto-resolve on retry.
- Clean up sync error messages (friendly text, no raw error types)
- Only show error messages in queue after 3+ retries or failed status
- Only show retry counter after 3+ retries
- First couple of retries are silent - just shows Pending status
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
loadQueue() used getElementById('empty-state') on each call, but the
first successful render wiped it with innerHTML=''. Every subsequent
call got null and threw TypeError on .style access, silently killing
all future list refreshes. Counters kept working because updateStats()
runs independently.
Fix: recreate the empty state as innerHTML string instead of
referencing a DOM element that gets destroyed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
The previous change extended the poll from 5s to 30s relying on
photo-updated events, but the service worker may still serve the old
storage.js without event dispatch, leaving the list stale.
Now polls every 3s while there are active uploads (pending/uploading),
15s fallback when idle, plus event-based refresh as a bonus.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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 <noreply@anthropic.com>
iOS Safari evicts Blob file-backed data from IndexedDB under memory
pressure, causing upload POSTs to throw 'Load failed' without ever
reaching the server. Two-pronged fix:
1. Store photos as ArrayBuffer (inline bytes) instead of Blob (file
reference) in IndexedDB — ArrayBuffers are not subject to eviction
2. Request navigator.storage.persist() to signal the browser not to
evict our storage under pressure
Also adds Storage.getBlob() helper for converting stored ArrayBuffer
back to Blob at upload/display time, with backward compat for any
existing Blob-stored photos.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Offline-first photo capture app for Nextcloud with:
- Camera capture with continuous mode (auto-reopens after each photo)
- File browser with fullscreen image gallery, swipe navigation, and rename
- Upload queue with background sync engine
- Admin panel for Nextcloud user management
- Service worker for offline-first caching (v13)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>