feat(pwa): installable PWA + offline message outbox (LC-98) #160
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lc-98-pwa-offline-outbox"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Makes lets-chat an installable PWA and adds an offline outbox so messages typed without a connection queue and send automatically on reconnect (LC-98).
PWA install
server/assets/manifest.webmanifest: name,display: standalone,start_url: /, theme/background colors, icons at 192 + 512 PNG, a maskable 512, and the SVG. Icons generated from the existing favicon.base.htmllinks the manifest + addstheme-colormeta andapple-touch-icon.Service worker (
sw.js)Alongside the existing push / notificationclick handlers (preserved verbatim):
offline.htmlfallback. Cache name embeds the build's asset version.routes/push.rsnow servessw.jsdynamically, substituting__ASSET_VERSION__, so each deploy gets a fresh cache that theactivatehandler evicts. Served withCache-Control: no-cache.offline.html); cache-first for/assets./room/{id}/messages(+ thread variant; DMs share the room endpoint). On network error it stores the body in IndexedDB and returns a synthetic success so the composer clears, then replays on reconnect via Background Sync (where supported) plus an explicit drain on the page'sonlineevent. Replay failures (room gone, muted) are marked failed and surfaced with Retry / Discard.Page client (
outbox.js)Registers the SW unconditionally (independent of Web Push), relays drain/retry/discard, and renders the SW's queue state into a global banner (
#lc-outbox) plus an inline composer hint (#lc-composer-net). The SW owns the IndexedDB queue as the single source of truth.Notes / open questions from the issue
manual_repeat_nlint indb/analytics.rsthat the toolchain flagged on this branch.Tests
server/tests/routes_pwa.rs: version-substituted worker (content type,no-cache, placeholder gone), retained push + fetch/outbox handlers, and PWA head wiring on the login page. Outbox queue/replay logic is browser/IndexedDB behaviour, exercised manually.just check,just test,just test-saasall green.🤖 Generated with Claude Code
Make lets-chat an installable PWA and let messages typed while offline queue and send automatically on reconnect. PWA install: add server/assets/manifest.webmanifest (name, icons at 192/512 + maskable + SVG, display standalone, start_url /, theme + background colors) and link it from base.html along with a theme-color meta and apple-touch-icon. Icons are generated from the existing favicon. Browsers now surface an "Install lets-chat" prompt and the installed app launches standalone with its own icon. Service worker (server/assets/sw.js) gains, alongside the existing push / notificationclick handlers: a versioned shell cache (pre-caches CSS, the HTMX bundle, icons, manifest, and an offline fallback page), a fetch router (network-first for navigations with an offline.html fallback, cache-first for /assets), and an offline outbox. The cache name embeds the build's asset version: routes/push.rs now serves sw.js dynamically, substituting __ASSET_VERSION__ so each deploy gets a fresh cache that the activate handler evicts. The worker is served with Cache-Control: no-cache so the script itself always revalidates. Offline outbox: the SW intercepts POSTs to /room/{id}/messages (and the thread variant; DMs share the room endpoint). On a network error it stores the request body in IndexedDB and returns a synthetic success so the composer clears, then replays the queue on reconnect via Background Sync (where supported) and an explicit drain triggered by the page's online event. Messages that fail on replay (room gone, muted, ...) are marked failed and surfaced with Retry / Discard controls. server/assets/outbox.js is the page-side client: it registers the SW unconditionally (independent of Web Push), relays drain/retry/discard commands, and renders the SW's queue state into a global banner plus an inline composer hint. The SW owns the IndexedDB queue as the single source of truth. Web Push behaviour is unchanged: the push and notificationclick handlers are preserved verbatim, and the existing push registration reuses the now-always-registered worker. Also fixes a clippy manual_repeat_n lint in db/analytics.rs surfaced by the toolchain on this branch. Tests: server/tests/routes_pwa.rs covers the version-substituted worker (content type, no-cache header, placeholder gone), retention of the push + fetch/outbox handlers, and the PWA head wiring on the unauthenticated login page. just check, just test, and just test-saas all green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>