From 4ff90abdee8633202bf81383562153050129ea58 Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Fri, 28 Mar 2025 13:14:51 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B(service-worker)=20force=20reload?= =?UTF-8?q?=20new=20service=20worker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When multiple tabs are open, the new service worker can stay in the "waiting" state and not be activated until the other tabs with the old service worker are closed. We fix this by forcing the other tabs to reload the page when a new service worker is detected. All tabs will then be reloaded and the new service worker will be activated. --- .../__tests__/useSWRegister.test.tsx | 6 +++++ .../service-worker/hooks/useSWRegister.tsx | 24 +++++++++++++++++++ .../features/service-worker/service-worker.ts | 7 ++++++ 3 files changed, 37 insertions(+) diff --git a/src/frontend/apps/impress/src/features/service-worker/__tests__/useSWRegister.test.tsx b/src/frontend/apps/impress/src/features/service-worker/__tests__/useSWRegister.test.tsx index 36f4ad4f..1698907c 100644 --- a/src/frontend/apps/impress/src/features/service-worker/__tests__/useSWRegister.test.tsx +++ b/src/frontend/apps/impress/src/features/service-worker/__tests__/useSWRegister.test.tsx @@ -21,9 +21,11 @@ describe('useSWRegister', () => { reject('error'); }), ); + Object.defineProperty(navigator, 'serviceWorker', { value: { register: registerSpy, + addEventListener: jest.fn(), }, writable: true, }); @@ -31,6 +33,10 @@ describe('useSWRegister', () => { render(); expect(registerSpy).toHaveBeenCalledWith('/service-worker.js?v=123456'); + expect(navigator.serviceWorker.addEventListener).toHaveBeenCalledWith( + 'controllerchange', + expect.any(Function), + ); }); it('checks service-worker is not register', () => { diff --git a/src/frontend/apps/impress/src/features/service-worker/hooks/useSWRegister.tsx b/src/frontend/apps/impress/src/features/service-worker/hooks/useSWRegister.tsx index eadeecc7..da897cff 100644 --- a/src/frontend/apps/impress/src/features/service-worker/hooks/useSWRegister.tsx +++ b/src/frontend/apps/impress/src/features/service-worker/hooks/useSWRegister.tsx @@ -8,9 +8,33 @@ export const useSWRegister = () => { ) { navigator.serviceWorker .register(`/service-worker.js?v=${process.env.NEXT_PUBLIC_BUILD_ID}`) + .then((registration) => { + registration.onupdatefound = () => { + const newWorker = registration.installing; + if (!newWorker) { + return; + } + + newWorker.onstatechange = () => { + if ( + newWorker.state === 'installed' && + navigator.serviceWorker.controller + ) { + newWorker.postMessage({ type: 'SKIP_WAITING' }); + } + }; + }; + }) .catch((err) => { console.error('Service worker registration failed:', err); }); + + const currentController = navigator.serviceWorker.controller; + navigator.serviceWorker.addEventListener('controllerchange', () => { + if (currentController) { + window.location.reload(); + } + }); } }, []); }; diff --git a/src/frontend/apps/impress/src/features/service-worker/service-worker.ts b/src/frontend/apps/impress/src/features/service-worker/service-worker.ts index 77a4f257..b8950beb 100644 --- a/src/frontend/apps/impress/src/features/service-worker/service-worker.ts +++ b/src/frontend/apps/impress/src/features/service-worker/service-worker.ts @@ -65,6 +65,13 @@ self.addEventListener('install', function (event) { event.waitUntil(self.skipWaiting()); }); +self.addEventListener('message', (event) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (event.data?.type === 'SKIP_WAITING') { + void self.skipWaiting(); + } +}); + self.addEventListener('activate', function (event) { const cacheAllow = SW_VERSION;