diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index ff4c08c8..c0c7930e 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -103,6 +103,20 @@ export default tseslint.config( 'simple-import-sort/exports': 'error', }, }, + { + // Service workers run in a ServiceWorkerGlobalScope, not a window. Matched by a + // cwd-independent glob so this applies whether eslint runs from web/ or the repo root. + files: ['**/public/service-worker.js'], + languageOptions: { + globals: { + self: 'readonly', + clients: 'readonly', + caches: 'readonly', + skipWaiting: 'readonly', + registration: 'readonly', + }, + }, + }, { ignores: [ 'public/mtg/*', @@ -111,7 +125,6 @@ export default tseslint.config( 'eslint.config.mjs', 'out/*', 'coverage/*', - 'public/service-worker.js', 'pages_unused/*', 'tailwind.config.js', ], diff --git a/web/public/service-worker.js b/web/public/service-worker.js index 473259e4..dedc7de0 100644 --- a/web/public/service-worker.js +++ b/web/public/service-worker.js @@ -46,6 +46,9 @@ self.addEventListener('push', (event) => { icon: '/icons/icon-192x192.png', badge: '/icons/badge-72x72.png', data: data.url || '/', + // Notifications sharing a tag replace each other, so a conversation only ever + // occupies one slot instead of stacking one entry per message. + tag: data.collapseKey || undefined, } event.waitUntil(self.registration.showNotification(title, options)) })