feat(pwa): rebuild offline support with Workbox and modernize install UX - #88
Merged
Conversation
The hand-written public/sw.js precached CRA-era paths (/static/js/bundle.js, /static/css/main.css) that 404 in the Vite build, so cache.addAll rejected and the service worker never finished installing — offline was broken entirely. Replace it with vite-plugin-pwa (injectManifest): Workbox injects the real hashed-asset precache manifest, and a custom src/sw.ts adds a NetworkFirst navigation handler with an app-shell offline fallback, StaleWhileRevalidate for Supabase REST reads (now covering family prayers too), and a bounded CacheFirst for storage images. Navigations stay network-first so online visitors and crawlers always get the freshly prerendered HTML — the Pages deploy/prerender pipeline is unchanged (SW is bypassed during snapshotting). Make the installed app more useful and cross-platform: - maskable icons (192/512) with Android safe-zone padding - apple-touch-icon (180), Apple meta tags, and iOS splash screens - wired favicons and light/dark theme-color - manifest id/scope/lang/categories, app shortcuts, and install screenshots - in-app "update available" toast and an Install button (beforeinstallprompt) Assets are generated by scripts/generate-pwa-assets.mjs (reuses the existing puppeteer + cached Chromium; not part of CI). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The hand-written
public/sw.jsprecached CRA-era paths (/static/js/bundle.js,/static/css/main.css) that 404 in this Vite build.cache.addAllrejected → the service worker install failed entirely → the app shell was never cached and offline was broken. The install experience was also weak: no offline navigation fallback, no maskable icon (Android clips it), noapple-touch-icon/Apple meta (poor iOS install), and an unlinked favicon.What
Root-cause fix —
vite-plugin-pwa(injectManifest)src/sw.ts, compiled by the plugin, which injects the real hashed-asset precache manifest (self.__WB_MANIFEST).NetworkFirstfor navigations (online visitors/crawlers always get fresh prerendered HTML), falling back to the last-visited page, then a precached app shell.StaleWhileRevalidatefor all/rest/v1/reads (now covers family prayers, not just weekly) andCacheFirst+ bounded expiration for storage images.public/sw.jsandpublic/manifest.jsonremoved; manifest is now generated by the plugin.Cross-platform install polish
apple-touch-icon(180); Apple meta tags + 10 iOS splash screens; wired favicons; light/darktheme-color.id/scope/lang/dir/categories, app shortcuts (Weekly/Family prayers), and install screenshots (narrow + wide).beforeinstallprompt. New i18n keys in both locales.scripts/generate-pwa-assets.mjs(reuses existing puppeteer + cached Chromium; not part of CI).Prerender/deploy safety
The Pages prerender pipeline is preserved: it already
setBypassServiceWorker(true),createRootis unchanged, and navigations are network-first so snapshots never serve a stale precached shell. Onlyindex.html(the shell, used solely as the offline fallback) is precached; per-route snapshots are served fresh at runtime.Verification
format:check,lint,build(the three CI gates)build:static(sitemap + headless prerender): 139 ok, 0 failed; prerendered/prayersretains ~16k chars of real content + full PWA headdist/sw.jsprecaches real hashedassets/*.js|css(0 CRA paths); all 18 manifest-referenced assets present indist/🤖 Generated with Claude Code