- Implemented a service worker (sw.js) that handles installation, activation, and fetch events. - Added caching logic for public assets and a fallback for offline navigation. - Created unit tests (sw.test.ts) to validate service worker behavior, including asset caching, navigation handling, and activation logic. - Ensured that private API routes and third-party requests are excluded from caching.
104 lines
5.6 KiB
Markdown
104 lines
5.6 KiB
Markdown
# Progressive web app
|
|
|
|
Mina Habits has an installable production build with a public offline app shell.
|
|
Its manifest supplies a stable app identity and root scope, standalone display,
|
|
192px and 512px icons, a separate maskable icon, app colors and descriptions.
|
|
The HTML includes an Apple touch icon, mobile metadata, theme color and safe-area
|
|
support. Browser chrome follows the selected light or dark theme.
|
|
|
|
Install from **Install app** in the footer. Supporting browsers show their native
|
|
install prompt. Other browsers receive instructions for Add to Home Screen or
|
|
Add to Dock. Installed windows hide the install action.
|
|
|
|
## Offline behavior
|
|
|
|
After one successful online visit and service-worker installation, the application
|
|
shell, fonts, icons and lazy browser chunks are available offline. Opening or
|
|
reloading the app offline shows a reconnect screen instead of the browser error
|
|
page. Nested routes also resolve correctly. Accounts retry on reconnection and
|
|
periodically while a failed connection is visible, including when the browser
|
|
incorrectly reports that it is online. An account request has a 10-second timeout;
|
|
page navigations fall back to the cached shell after four seconds.
|
|
|
|
**Account access, habit changes, sign-in, exports and Discord actions require a
|
|
connection.** Existing dashboard content can remain visible in an open window,
|
|
but no account responses are persisted for offline use and no mutations are
|
|
queued or replayed. Failed saves must be retried by the user. This is an offline
|
|
fallback, not offline habit editing or background synchronization.
|
|
|
|
All `/api` traffic, including OAuth callback navigations, bypasses the service
|
|
worker. API responses have `Cache-Control: no-store`. Only explicitly public
|
|
build assets enter Cache Storage; server JavaScript, source maps, account data,
|
|
exports and third-party responses are excluded. Clearing site data removes the
|
|
public offline files; it does not remove account records from the server.
|
|
|
|
## Build and updates
|
|
|
|
`bun run build` runs `scripts/build-pwa.ts` after Bun emits the browser and server
|
|
bundles. It derives a release identifier from the worker source and public asset
|
|
contents, copies a version-specific public HTML shell, and emits `dist/pwa/sw.js`
|
|
with an explicit precache list. A failed precache download prevents installation
|
|
of that release. The stable `/sw.js` route is served as JavaScript with
|
|
`Cache-Control: no-cache` and root service-worker scope.
|
|
|
|
Updates wait until existing windows close or the user chooses **Update app**,
|
|
then **Reload and update**. The confirmation tells the user to save unfinished
|
|
changes. Only the accepting window reloads; other windows retain their drafts
|
|
and receive their own update action. Old static caches remain available for their
|
|
lazy chunks. A subsequent activation with no open app windows removes obsolete
|
|
Mina Habits caches, leaving unrelated caches untouched. Checking for updates on
|
|
return to the app, reconnection and hourly supports long-lived installed windows.
|
|
|
|
The source manifest is `src/pwa/manifest.webmanifest`. Bun fingerprints its HTML
|
|
link; an explicit `/manifest.webmanifest` endpoint serves the same document. The
|
|
manifest's stable `id` keeps app identity independent of its asset filename.
|
|
|
|
Install icons reuse `docs/design/mina-habits-icon.png`; regenerate their exact
|
|
sizes with `bun scripts/pwa-icons.ts`. The maskable derivative adds opaque padding
|
|
to keep the monogram inside the central safe zone. Existing favicon and Apple
|
|
icons are preserved.
|
|
|
|
## Serving requirements
|
|
|
|
Use the normal production build/start or Docker workflow at the root of an HTTPS
|
|
origin. Local testing may use `http://localhost` or `http://127.0.0.1`. The current
|
|
production configuration already requires an HTTPS origin outside loopback and
|
|
uses Caddy for TLS. No new deployment environment variables are required.
|
|
|
|
Service-worker registration is deliberately disabled in development/HMR mode;
|
|
run the production smoke check to verify caching. Deploy the entire build
|
|
atomically, including `dist/pwa`, rather than uploading individual browser files.
|
|
Do not apply immutable HTTP caching to `/sw.js` or add a proxy cache for `/api`.
|
|
|
|
PWA installation support and its interface vary by browser. Push notifications,
|
|
background sync and app-store packaging are optional capabilities, not
|
|
installation requirements. This implementation follows the current
|
|
[MDN installability requirements](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Making_PWAs_installable)
|
|
and the documented
|
|
[service-worker lifecycle](https://web.dev/articles/service-worker-lifecycle).
|
|
|
|
## Verification
|
|
|
|
```sh
|
|
bun run typecheck
|
|
bun test
|
|
bun x playwright install chromium
|
|
bun run test:pwa
|
|
bun run test:smoke
|
|
```
|
|
|
|
`test:pwa` copies the production build into a temporary directory, starts it with
|
|
a disposable SQLite database and no Discord credentials, then runs Chromium.
|
|
It verifies manifest parsing and Chrome installability errors, icon dimensions,
|
|
MIME/cache headers, first installation, offline startup and nested/lazy routes,
|
|
reconnection, responsive layouts, theme color, failed-update recovery, a two-window
|
|
update with an unsaved draft, API-cache exclusion, sign-out and mobile install
|
|
instructions. Screenshots are written to `.cache/pwa/`. Temporary data and the
|
|
server are removed when the check finishes. Worker unit tests cover routing,
|
|
installation failure and cache cleanup.
|
|
|
|
Before a public release, also exercise installation and standalone relaunch on a
|
|
physical iPhone/iPad and Android device over the production HTTPS origin. Desktop
|
|
Chromium checks and mobile emulation do not establish native OS installation or
|
|
Safari behavior. The implementation does not claim a universal PWA certification.
|