feat(gui): the service worker tells open tabs when the push subscription changes

This commit is contained in:
Juan Castro committed 2026-10-27 11:06:14 -04:00
1 parent 681b9cd463
commit 7ee0cbfac9
2 files changed
+31

No files matched your search

+15
View File
@@ -20,12 +20,15 @@
// Shows each push as a notification and routes taps to a Puter window.
// Push payload: JSON whose `puter` (or `data.puter`, an object or JSON string)
// is { uid, title, body, icon, group, target: { app, path }, data }.
// On `pushsubscriptionchange` it posts { type: 'puter-push-subscription-changed' }
// to this origin's windows, so a signed-in page can register again.
const DEFAULT_TITLE = 'New notification';
const DEFAULT_ICON = '/favicons/android-icon-192x192.png';
const SHOW_MESSAGE = 'puter-show-notification';
const CLICK_MESSAGE = 'puter-notification-click';
const CLICK_ACK_MESSAGE = 'puter-notification-click-ack';
const SUBSCRIPTION_CHANGED_MESSAGE = 'puter-push-subscription-changed';
const CLICK_PARAM = 'puter.notification';
// Browsers allow openWindow only shortly after the click.
const CLICK_ACK_BUDGET_MS = 600;
@@ -189,6 +192,14 @@ async function showPageMessage (scope, message) {
await showMessage(scope.registration, message, scope.location.origin);
}
async function announceSubscriptionChange (scope) {
const origin = scope.location.origin;
const windowClients = await scope.clients.matchAll({ type: 'window', includeUncontrolled: true });
for ( const client of windowClients ) {
if ( sameOriginUrl(client.url, origin) ) client.postMessage({ type: SUBSCRIPTION_CHANGED_MESSAGE });
}
}
self.addEventListener('install', () => {
self.skipWaiting();
});
@@ -216,3 +227,7 @@ self.addEventListener('notificationclick', (event) => {
event.notification.close();
event.waitUntil(routeClick(self, clickFromData(event.notification.data), startedAt));
});
self.addEventListener('pushsubscriptionchange', (event) => {
event.waitUntil(announceSubscriptionChange(self));
});
+16
View File
@@ -303,6 +303,22 @@ describe('service worker notificationclick', () => {
});
});
describe('service worker pushsubscriptionchange', () => {
it('tells every window on this origin, and no other', async () => {
const desktop = windowClient(`${ORIGIN}/desktop`);
const hidden = windowClient(`${ORIGIN}/`);
const other = windowClient('https://elsewhere.example/');
const worker = loadWorker({ windows: [desktop, hidden, other] });
await worker.fire('pushsubscriptionchange', {});
expect(worker.scope.clients.matchAll).toHaveBeenCalledWith({ type: 'window', includeUncontrolled: true });
for ( const win of [desktop, hidden] ) {
expect(win.postMessage).toHaveBeenCalledWith({ type: 'puter-push-subscription-changed' });
}
expect(other.postMessage).not.toHaveBeenCalled();
expect(worker.shown).toEqual([]);
});
});
describe('service worker lifecycle', () => {
it('takes over at once and adds no fetch handler', async () => {
const worker = loadWorker();