Private
Public Access
Fix stale room/message/member data after leaving and returning (#37)
The service worker cached /api/rooms/mine, /api/rooms, .../messages, .../members, and /api/invites/mine with StaleWhileRevalidate: serve the previous cached response immediately, refresh the cache in the background for next time. That means every repeat visit showed content one visit behind -- reopening a room after someone messaged it, or checking a second device, both showed stale data until a manual reload (which finally picked up the now-revalidated cache). Only registers in a production build (vite dev never activates it), which is why this didn't surface during in-browser testing this session for #26/#27/#34. Switched all five routes to NetworkFirst: always prefer a live response, fall back to cache only when the network request itself fails or times out (genuinely offline), keeping the "readable while offline" behavior without the staleness. Verified against a real production build (`vite preview`, the only way the SW actually registers): sent a message from a second session against an already-cached room, and the very next fetch showed it immediately with no staleness. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+23
-6
@@ -3,7 +3,7 @@ import { CacheableResponsePlugin } from 'workbox-cacheable-response'
|
|||||||
import { ExpirationPlugin } from 'workbox-expiration'
|
import { ExpirationPlugin } from 'workbox-expiration'
|
||||||
import { cleanupOutdatedCaches, createHandlerBoundToURL, precacheAndRoute } from 'workbox-precaching'
|
import { cleanupOutdatedCaches, createHandlerBoundToURL, precacheAndRoute } from 'workbox-precaching'
|
||||||
import { NavigationRoute, registerRoute } from 'workbox-routing'
|
import { NavigationRoute, registerRoute } from 'workbox-routing'
|
||||||
import { NetworkOnly, StaleWhileRevalidate } from 'workbox-strategies'
|
import { NetworkFirst, NetworkOnly } from 'workbox-strategies'
|
||||||
|
|
||||||
declare let self: ServiceWorkerGlobalScope
|
declare let self: ServiceWorkerGlobalScope
|
||||||
|
|
||||||
@@ -20,6 +20,18 @@ registerRoute(
|
|||||||
|
|
||||||
const READ_CACHE_EXPIRATION = { maxEntries: 50, maxAgeSeconds: 7 * 24 * 60 * 60 }
|
const READ_CACHE_EXPIRATION = { maxEntries: 50, maxAgeSeconds: 7 * 24 * 60 * 60 }
|
||||||
const cacheableResponse = new CacheableResponsePlugin({ statuses: [0, 200] })
|
const cacheableResponse = new CacheableResponsePlugin({ statuses: [0, 200] })
|
||||||
|
// Always prefer a live network response over the cache -- these routes back
|
||||||
|
// an actively-updating chat, so "stale" isn't an acceptable default the way
|
||||||
|
// it can be for e.g. static assets. StaleWhileRevalidate was tried here
|
||||||
|
// first, but it serves the *previous* cached response immediately and only
|
||||||
|
// refreshes the cache in the background for next time, which means every
|
||||||
|
// repeat visit shows content that's one visit behind until a manual reload
|
||||||
|
// (confirmed as the cause of #37 -- messages/rooms/members looking stale
|
||||||
|
// after leaving and returning to a room, or after another device's update).
|
||||||
|
// NetworkFirst keeps the same "readable while offline" behavior (falls back
|
||||||
|
// to cache only when the network request itself fails or times out) without
|
||||||
|
// that staleness while online.
|
||||||
|
const NETWORK_TIMEOUT_SECONDS = 4
|
||||||
|
|
||||||
// Ported from Phase 3's vite.config.ts `workbox.runtimeCaching` -- that
|
// Ported from Phase 3's vite.config.ts `workbox.runtimeCaching` -- that
|
||||||
// option only applies to the generateSW strategy, so with a hand-written
|
// option only applies to the generateSW strategy, so with a hand-written
|
||||||
@@ -29,36 +41,41 @@ registerRoute(({ url }) => url.pathname.startsWith('/api/auth/'), new NetworkOnl
|
|||||||
|
|
||||||
registerRoute(
|
registerRoute(
|
||||||
({ url }) => url.pathname === '/api/rooms/mine',
|
({ url }) => url.pathname === '/api/rooms/mine',
|
||||||
new StaleWhileRevalidate({
|
new NetworkFirst({
|
||||||
cacheName: 'api-rooms-mine',
|
cacheName: 'api-rooms-mine',
|
||||||
|
networkTimeoutSeconds: NETWORK_TIMEOUT_SECONDS,
|
||||||
plugins: [cacheableResponse, new ExpirationPlugin(READ_CACHE_EXPIRATION)],
|
plugins: [cacheableResponse, new ExpirationPlugin(READ_CACHE_EXPIRATION)],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
registerRoute(
|
registerRoute(
|
||||||
({ url }) => url.pathname === '/api/rooms',
|
({ url }) => url.pathname === '/api/rooms',
|
||||||
new StaleWhileRevalidate({
|
new NetworkFirst({
|
||||||
cacheName: 'api-rooms-open',
|
cacheName: 'api-rooms-open',
|
||||||
|
networkTimeoutSeconds: NETWORK_TIMEOUT_SECONDS,
|
||||||
plugins: [cacheableResponse, new ExpirationPlugin(READ_CACHE_EXPIRATION)],
|
plugins: [cacheableResponse, new ExpirationPlugin(READ_CACHE_EXPIRATION)],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
registerRoute(
|
registerRoute(
|
||||||
({ url }) => /^\/api\/rooms\/[^/]+\/messages$/.test(url.pathname),
|
({ url }) => /^\/api\/rooms\/[^/]+\/messages$/.test(url.pathname),
|
||||||
new StaleWhileRevalidate({
|
new NetworkFirst({
|
||||||
cacheName: 'api-room-messages',
|
cacheName: 'api-room-messages',
|
||||||
|
networkTimeoutSeconds: NETWORK_TIMEOUT_SECONDS,
|
||||||
plugins: [cacheableResponse, new ExpirationPlugin(READ_CACHE_EXPIRATION)],
|
plugins: [cacheableResponse, new ExpirationPlugin(READ_CACHE_EXPIRATION)],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
registerRoute(
|
registerRoute(
|
||||||
({ url }) => /^\/api\/rooms\/[^/]+\/members$/.test(url.pathname),
|
({ url }) => /^\/api\/rooms\/[^/]+\/members$/.test(url.pathname),
|
||||||
new StaleWhileRevalidate({
|
new NetworkFirst({
|
||||||
cacheName: 'api-room-members',
|
cacheName: 'api-room-members',
|
||||||
|
networkTimeoutSeconds: NETWORK_TIMEOUT_SECONDS,
|
||||||
plugins: [cacheableResponse, new ExpirationPlugin(READ_CACHE_EXPIRATION)],
|
plugins: [cacheableResponse, new ExpirationPlugin(READ_CACHE_EXPIRATION)],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
registerRoute(
|
registerRoute(
|
||||||
({ url }) => url.pathname === '/api/invites/mine',
|
({ url }) => url.pathname === '/api/invites/mine',
|
||||||
new StaleWhileRevalidate({
|
new NetworkFirst({
|
||||||
cacheName: 'api-invites-mine',
|
cacheName: 'api-invites-mine',
|
||||||
|
networkTimeoutSeconds: NETWORK_TIMEOUT_SECONDS,
|
||||||
plugins: [cacheableResponse, new ExpirationPlugin(READ_CACHE_EXPIRATION)],
|
plugins: [cacheableResponse, new ExpirationPlugin(READ_CACHE_EXPIRATION)],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user