Previously a single 'pending-annot-upload' IDB key was used, so only
the last-annotated book's pending upload survived an app kill. With
multiple books annotated offline (or across multiple tabs), earlier
books' uploads were silently dropped from the queue. A related bug
caused stale in-memory state from a previous book to be used on Sync
after navigating between books in the same tab, potentially sending the
wrong annotations to the wrong book endpoint.
Changes:
- IDB key is now 'pending-annot-upload:{library_id}/{book_id}/{fmt}',
one entry per book, so all books' pending uploads survive independently
- New get_all_pending_annot_uploads() uses an IDB cursor range query to
retrieve every pending entry
- clear_pending_annot_upload() now takes book identity params and a
completion callback so the next upload starts only after the IDB
delete has committed
- _make_annot_upload_done() returns a per-book closure used as the ajax
callback, replacing the single _annot_upload_done method
- After each successful upload, _upload_next_from_idb() fetches and
uploads the next pending entry, draining the queue sequentially
- _on_network_restored() no longer requires a book to be open, so
pending uploads from other books are flushed even from the homepage
- load_book() clears unsynced_amap and the indicator timer/state so
stale in-memory state from the previous book is never used
When multiple tabs open simultaneously, coordinate with navigator.locks
using ifAvailable so only the first tab calls persist(). Tabs that lose
the lock skip the call entirely, preventing the browser from showing
redundant permission prompts. Falls back to the persisted() check for
browsers without Web Locks support.
Check navigator.storage.persisted() before calling persist(), so tabs
opened after permission was already granted do not trigger a redundant
browser prompt.
Replace hardcoded #d4a017 with builtin_color('yellow', is_dark_theme()),
which resolves to #ffeb6b (light theme) or #906e00 (dark theme) — the
same values used for yellow text highlights. Single source of truth in
calibre/constants.py.
Replace the single 1.5s one-shot retry with a repeating 5s timer loop.
Each 401 response cancels any pending retry and schedules a new one 5s
later, so auth retries continue indefinitely until the upload succeeds —
regardless of how long the user takes to submit credentials in the
browser auth dialog.
On success the retry timer is cancelled. A manually triggered
upload_pending_annotations call also cancels any pending retry before
firing its own request.
When the calibre server restarts with user/password auth, an in-flight
annotation upload receives a 401. The browser shows the auth dialog and
the user provides credentials, but nothing retried the upload afterward.
Fix: on a 401 response, schedule one retry 1.5s later (giving the
browser time to process the auth dialog and store credentials). A flag
prevents cascading retries — if the second attempt also fails the
indicator stays on and the user can sync manually via the Sync button.
The flag resets on success or at the start of any new upload attempt.
When the page reloads after an app kill, upload_pending_annotations
finds the surviving IDB entry and sets unsynced_amap, but never called
_reschedule_unsynced_indicator — so the 60s timer never started and
the indicator never appeared even if the upload failed.
Fix: call _reschedule_unsynced_indicator after loading unsynced_amap
from IDB, giving the same behaviour as a fresh annotation upload.
- Tint the Sync button icon amber (#d4a017) when annotation uploads
have been failing for >60s
- Show persistent "Unsynced changes" text in the bottom footer margin
at the same 60s threshold, using the existing current_status_message
override slot in update_header_footer
- Track indicator state with a reschedulable timer in ReadUI so rapid
annotation saves don't flicker the indicator; cleared automatically
on successful upload
- Request persistent IDB storage so Firefox Android never evicts origin data
- Persist pending annotation uploads to IDB so they survive page reloads
- Auto-retry upload on network recovery and on book open
- Fix SyncBook race: upload annotations and position before downloading