From 8e377361d05f62b759825943927b947e893f0511 Mon Sep 17 00:00:00 2001 From: Evie Gauthier Date: Mon, 4 May 2026 10:23:42 -0400 Subject: [PATCH 1/3] fix(sw): add userId to pushSessionToSW calls, add heartbeat + foreground resync - Pass userId in both pushSessionToSW calls in ClientRoot (was missing) - On tab foreground, immediately resync session to SW - 10-minute interval heartbeat keeps persisted session fresh (prevents iOS from invalidating session while app is backgrounded) --- src/app/pages/client/ClientRoot.tsx | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/app/pages/client/ClientRoot.tsx b/src/app/pages/client/ClientRoot.tsx index 70ba9221e..72fa5ef3f 100644 --- a/src/app/pages/client/ClientRoot.tsx +++ b/src/app/pages/client/ClientRoot.tsx @@ -205,7 +205,7 @@ export function ClientRoot({ children }: ClientRootProps) { log.log('initClient for', activeSession.userId); const newMx = await initClient(activeSession); loadedUserIdRef.current = activeSession.userId; - pushSessionToSW(activeSession.baseUrl, activeSession.accessToken); + pushSessionToSW(activeSession.baseUrl, activeSession.accessToken, activeSession.userId); return newMx; }, [activeSession, activeSessionId, setActiveSessionId]) ); @@ -234,7 +234,7 @@ export function ClientRoot({ children }: ClientRootProps) { activeSession.userId, '— reloading client' ); - pushSessionToSW(activeSession.baseUrl, activeSession.accessToken); + pushSessionToSW(activeSession.baseUrl, activeSession.accessToken, activeSession.userId); if (mx?.clientRunning) { stopClient(mx); } @@ -259,6 +259,28 @@ export function ClientRoot({ children }: ClientRootProps) { useLogoutListener(mx); useAppVisibility(mx); + // Keep the SW session warm so media fetches and push notifications work + // reliably after iOS kills and restarts the SW in the background. + // - Immediate resync whenever the tab comes back to the foreground. + // - Periodic heartbeat (10 min) keeps the persisted session up to date + // while the app is running. + const swSessionBaseUrl = activeSession?.baseUrl; + const swSessionAccessToken = activeSession?.accessToken; + const swSessionUserId = activeSession?.userId; + useEffect(() => { + if (!swSessionBaseUrl || !swSessionAccessToken) return undefined; + const resync = () => pushSessionToSW(swSessionBaseUrl, swSessionAccessToken, swSessionUserId); + const handleVisibility = () => { + if (document.visibilityState === 'visible') resync(); + }; + document.addEventListener('visibilitychange', handleVisibility); + const timer = setInterval(resync, 10 * 60 * 1000); + return () => { + document.removeEventListener('visibilitychange', handleVisibility); + clearInterval(timer); + }; + }, [swSessionBaseUrl, swSessionAccessToken, swSessionUserId]); + useEffect( () => () => { if (mx?.clientRunning) { From 727755b66bb74dcd875b0e2d09135d2be08db0b1 Mon Sep 17 00:00:00 2001 From: Evie Gauthier Date: Tue, 5 May 2026 07:56:45 -0400 Subject: [PATCH 2/3] fix(push): rename app_display_name from Cinny to Sable in pusher registration --- src/app/features/settings/notifications/PushNotifications.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/features/settings/notifications/PushNotifications.tsx b/src/app/features/settings/notifications/PushNotifications.tsx index 7f510b444..be9e627fb 100644 --- a/src/app/features/settings/notifications/PushNotifications.tsx +++ b/src/app/features/settings/notifications/PushNotifications.tsx @@ -57,7 +57,7 @@ export async function enablePushNotifications( kind: 'http' as const, app_id: clientConfig.pushNotificationDetails?.webPushAppID, pushkey: keys.p256dh, - app_display_name: 'Cinny', + app_display_name: 'Sable', device_display_name: 'This Browser', lang: navigator.language || 'en', data: { @@ -104,7 +104,7 @@ export async function enablePushNotifications( kind: 'http' as const, app_id: clientConfig.pushNotificationDetails?.webPushAppID, pushkey: keys.p256dh, - app_display_name: 'Cinny', + app_display_name: 'Sable', device_display_name: (await mx.getDevice(mx.getDeviceId() ?? '')).display_name ?? 'Unknown Device', lang: navigator.language || 'en', From c16591a82fe9a93f0d45466539f48ff88f72df76 Mon Sep 17 00:00:00 2001 From: Evie Gauthier Date: Mon, 11 May 2026 09:06:22 -0400 Subject: [PATCH 3/3] fix(sw): unregister service workers in clearLoginData for clean reload on mobile --- src/client/initMatrix.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/client/initMatrix.ts b/src/client/initMatrix.ts index 9e0496ee3..b35c8a4a7 100644 --- a/src/client/initMatrix.ts +++ b/src/client/initMatrix.ts @@ -802,5 +802,13 @@ export const clearLoginData = async () => { if (name) window.indexedDB.deleteDatabase(name); }); window.localStorage.clear(); + + // Unregister all service workers so the next load starts fresh. + // Especially important on iOS/mobile where stale SWs can persist. + if ('serviceWorker' in navigator) { + const registrations = await navigator.serviceWorker.getRegistrations(); + await Promise.all(registrations.map((r) => r.unregister())); + } + window.location.reload(); };