Regression-of: 5b8d2d5c
The previous SW eviction commit wiped caches and called clients.claim()
on activate, but that only changes which SW handles future fetches — it
does not re-render a tab that already finished loading the stale bundle.
In practice, returning users had to manually close and reopen the tab
before seeing the new build.
Fix: after clients.claim(), iterate self.clients.matchAll({ type: 'window' })
and call client.navigate(client.url) on each one. Since this SW has no
fetch handler, the navigation falls through to the network and the tab
re-renders against the fresh index.html + hashed bundle.
Caveats:
- Users mid-interaction (typing a post, scrolling) lose their unsaved
state. Acceptable trade — the alternative is they stay on a broken
cached bundle indefinitely.
- Fires exactly once per user (only on the install -> activate transition
for a byte-different /sw.js). No reload loop.
Also corrected the misleading comment on the main.tsx registration: that
registration is forward-looking insurance for future cache busts, not the
mechanism that evicts the old SW. The browser's own SW update check is
what re-fetches /sw.js out-of-band; our in-page JS never runs on a tab
the old precache SW is controlling.
A previous version of Agora deployed at agora.spot shipped a precaching
service worker that is still controlling returning browsers and serving
them stale HTML/JS — they never see new deploys.
The fix has three parts:
1. public/sw.js — on activate, delete every Cache Storage entry the old
SW left behind. This SW has no fetch handler, so once it takes over
nothing re-populates the cache.
2. src/main.tsx — register /sw.js unconditionally on every web page load.
Previously only usePushNotifications registered it, which meant users
who never visited NotificationSettings stayed pinned to the old SW
forever. Native (Capacitor) skips this — there is no stale SW on the
filesystem origin.
3. .gitlab-ci.yml — the deploy-web rsync was excluding sw.js from the
first pass and never re-adding it to the second pass, so deploys
silently never updated sw.js. Now it ships in the second pass
alongside index.html (after hashed assets land).
Replace the browser Notification constructor (tab-must-be-open) with nostr-push
Web Push notifications that work even when the browser is closed.
- Add service worker (public/sw.js) for receiving and displaying push events
- Add nostr-push RPC client (src/lib/nostrPush.ts) using an ephemeral device
keypair for signing — the user's Nostr signer is never prompted
- Add usePushNotifications hook managing the full lifecycle: SW registration,
VAPID key fetch, pushManager.subscribe, and nostr-push registration
- Replace the web path in useNativeNotifications with push registration that
auto-enables/disables based on the notificationsEnabled encrypted setting
- Android native Capacitor path is unchanged
- Requires VITE_NOSTR_PUSH_PUBKEY env var (hex pubkey of the nostr-push server)