From a6e6915b0bdf7b9c62551ac1f3573fe18f7b47e9 Mon Sep 17 00:00:00 2001 From: Oier Bravo Urtasun Date: Mon, 18 May 2026 06:59:29 +0200 Subject: [PATCH] feat(fase-14): PWA auto-update toast (14.1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate from `registerSW({ immediate: true })` to `useRegisterSW` from `virtual:pwa-register/svelte`, mirror its `needRefresh` / `offlineReady` stores into stable layout-local writables, and render a new `UpdateToast` pill (bottom-center) that surfaces: - "New version available" + a "Reload" button that calls `updateServiceWorker(true)` - "Ready to use offline" (auto-dismiss after 4s) on first install Adds `[pwa] …` console.info telemetry on register / need-refresh / apply. Stand-in for the unavailable `updateViaCache: 'none'` runtime option (RegisterSWOptions doesn't expose it in vite-plugin-pwa 0.21.2): poll `reg.update()` every 15min, which keeps a too-aggressively-cached SW file from pinning users on the previous build for up to 24h. `apps/web/src/global.d.ts` declares the virtual module's surface inline so svelte-check picks it up without a relative path into node_modules; the same file pre-declares the Fase-14.3 `__APP_VERSION__` triplet. PU-01 stubs the registrar via `window.__pwaRegisterStub` (the layout checks for it before calling the real `useRegisterSW`) so the toast flow can be driven from a Playwright test without a second build. Co-Authored-By: Claude Opus 4.7 --- apps/web/messages/en.json | 16 +++ apps/web/messages/es.json | 16 +++ apps/web/src/global.d.ts | 40 +++++++ .../web/src/lib/components/UpdateToast.svelte | 92 ++++++++++++++++ apps/web/src/routes/+layout.svelte | 81 +++++++++++++- apps/web/tests/e2e/pwa-update.test.ts | 102 ++++++++++++++++++ 6 files changed, 342 insertions(+), 5 deletions(-) create mode 100644 apps/web/src/global.d.ts create mode 100644 apps/web/src/lib/components/UpdateToast.svelte create mode 100644 apps/web/tests/e2e/pwa-update.test.ts diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 1f359ef..f401618 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -164,6 +164,22 @@ "edit": "Edit", "add": "Add", "done": "Done", + "pwa_update_available": "New version available", + "pwa_update_reload": "Reload", + "pwa_offline_ready": "Ready to use offline", + "pwa_offline_chip": "Offline", + "pwa_offline_tooltip": "No connection — changes will sync when you reconnect", + "pwa_pending_ops": "{count} pending change", + "pwa_pending_ops_plural": "{count} pending changes", + "pwa_sync_conflicts_banner": "Sync conflicts need attention", + "pwa_sync_conflicts_review": "Review", + "sync_conflicts_title": "Sync conflicts", + "sync_conflicts_empty": "Nothing pending — local and server agree.", + "sync_conflicts_discard_local": "Discard local", + "sync_conflicts_discard_remote": "Discard remote", + "sync_conflicts_back": "Back to settings", + "settings_about": "About", + "settings_about_version": "v{version} · {commit} · {date}", "sync_offline": "You're offline — changes will sync when you reconnect", "sync_syncing": "Syncing…", "undo": "Undo", diff --git a/apps/web/messages/es.json b/apps/web/messages/es.json index ac860cf..1df8cf4 100644 --- a/apps/web/messages/es.json +++ b/apps/web/messages/es.json @@ -164,6 +164,22 @@ "edit": "Editar", "add": "Añadir", "done": "Listo", + "pwa_update_available": "Nueva versión disponible", + "pwa_update_reload": "Recargar", + "pwa_offline_ready": "Lista para usar offline", + "pwa_offline_chip": "Sin conexión", + "pwa_offline_tooltip": "Sin conexión — los cambios se sincronizarán al volver", + "pwa_pending_ops": "{count} cambio pendiente", + "pwa_pending_ops_plural": "{count} cambios pendientes", + "pwa_sync_conflicts_banner": "Hay conflictos de sincronización", + "pwa_sync_conflicts_review": "Revisar", + "sync_conflicts_title": "Conflictos de sincronización", + "sync_conflicts_empty": "Nada pendiente — local y servidor coinciden.", + "sync_conflicts_discard_local": "Descartar local", + "sync_conflicts_discard_remote": "Descartar remoto", + "sync_conflicts_back": "Volver a ajustes", + "settings_about": "Acerca de", + "settings_about_version": "v{version} · {commit} · {date}", "sync_offline": "Sin conexión — los cambios se sincronizarán al reconectar", "sync_syncing": "Sincronizando…", "undo": "Deshacer", diff --git a/apps/web/src/global.d.ts b/apps/web/src/global.d.ts new file mode 100644 index 0000000..5e33aec --- /dev/null +++ b/apps/web/src/global.d.ts @@ -0,0 +1,40 @@ +/** + * Ambient declarations for app-wide constants and virtual modules. + * + * - `virtual:pwa-register/svelte` is provided at build time by + * `vite-plugin-pwa`'s Svelte helper (Fase 14.1). vite-plugin-pwa ships + * `svelte.d.ts` for this but TypeScript only picks it up if the path + * is referenced; declaring the module here inlines the (small) surface + * without coupling to the package's internal layout. + * + * - `__APP_VERSION__`, `__APP_COMMIT__`, `__BUILD_TIME__` (Fase 14.3) + * are replaced literally by Vite at build time via the `define` + * option in `vite.config.ts`. They're consumed exclusively through + * `$lib/version.ts` — never inline — so tree-shaking can't drop + * the build-time substitution. + */ + +declare module 'virtual:pwa-register/svelte' { + import type { Writable } from 'svelte/store'; + + export interface RegisterSWOptions { + immediate?: boolean; + onNeedRefresh?: () => void; + onOfflineReady?: () => void; + onRegisteredSW?: ( + swScriptUrl: string, + registration: ServiceWorkerRegistration | undefined + ) => void; + onRegisterError?: (error: unknown) => void; + } + + export function useRegisterSW(options?: RegisterSWOptions): { + needRefresh: Writable; + offlineReady: Writable; + updateServiceWorker: (reloadPage?: boolean) => Promise; + }; +} + +declare const __APP_VERSION__: string; +declare const __APP_COMMIT__: string; +declare const __BUILD_TIME__: string; diff --git a/apps/web/src/lib/components/UpdateToast.svelte b/apps/web/src/lib/components/UpdateToast.svelte new file mode 100644 index 0000000..c5c95e3 --- /dev/null +++ b/apps/web/src/lib/components/UpdateToast.svelte @@ -0,0 +1,92 @@ + + +{#if $needRefresh} +
+
+ {m.pwa_update_available()} + +
+
+{:else if showOfflineReady} +
+
+ {m.pwa_offline_ready()} +
+
+{/if} diff --git a/apps/web/src/routes/+layout.svelte b/apps/web/src/routes/+layout.svelte index 55af756..50c5b81 100644 --- a/apps/web/src/routes/+layout.svelte +++ b/apps/web/src/routes/+layout.svelte @@ -1,6 +1,7 @@