feat: offline save-for-later + background sync for mark-cooked (v0.65.0)

Adds a "Save offline" button on the recipe page that force-refetches
the current page so the service worker's cache picks up a fresh copy
right now, plus a small IndexedDB-backed list (lib/offline-db.ts) of
what's been saved. The /offline fallback page now reads that list and
renders it instead of being a dead end with just a "go back" link.

Also fixes the service worker's network-first fetch handler, which
never wrote successful responses into its cache -- meaning the
existing offline page's claim that "recently visited recipes are
available" was never actually true. It populates the cache on every
successful GET now.

Background sync: marking a batch-cook dish as cooked while offline
(the only existing mark-cooked call site in the app) now queues the
request in IndexedDB instead of just failing, and registers a
Background Sync (public/sw.js's "sync" listener replays the queue)
for Chromium; lib/offline-queue.ts's online-event fallback covers
Safari/Firefox, which never fire that event at all. Both replay paths
read/write the same IndexedDB store so either one drains it.

Also removes apps/web/public/manifest.json, a stale static manifest
that layout.tsx used to link to before the previous commit pointed it
at the real generated route (app/manifest.ts) -- it had gone
unnoticed and unused since.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Arnaud
2026-07-21 00:04:17 +02:00
parent 5f270dee08
commit 9ba2996022
17 changed files with 405 additions and 29 deletions
+1
View File
@@ -146,6 +146,7 @@
"batchCookMarkCooked": "Mark as cooked today",
"batchCookMarkSuccess": "Marked as cooked",
"batchCookMarkFailed": "Failed to mark as cooked",
"batchCookMarkQueuedOffline": "Saved offline — will sync when you're back online",
"batchCookExpiresOn": "Cooked — good until {date}",
"servings": "{count} servings",
"prep": "{mins}m prep",
+1
View File
@@ -146,6 +146,7 @@
"batchCookMarkCooked": "Marquer comme cuisiné aujourd'hui",
"batchCookMarkSuccess": "Marqué comme cuisiné",
"batchCookMarkFailed": "Échec du marquage",
"batchCookMarkQueuedOffline": "Enregistré hors ligne — sera synchronisé au retour de la connexion",
"batchCookExpiresOn": "Cuisiné — bon jusqu'au {date}",
"servings": "{count} portions",
"prep": "{mins}m prép.",