7

Is there a way to remove a Web Push Notification once shown? To remove it from the notification list on the device - or mark it as "read"?

I assume this can't be done from the server, and I've been hunting around for a Javascript API - but I haven't found anything.

(The wider problem that I'm trying to crack is how to synchronise notifications on multiple screens/browsers/devices.)

Daniel Freeman
  • 383
  • 4
  • 11

3 Answers3

4

Even though it's not possble to revoke push messages, it is possible to close one, using:

// app.js
navigator.serviceWorker.ready.then(reg => {
  reg.getNotifications().then(notifications => {
    for (let i = 0; i < notifications.length; i += 1) {
      notifications[i].close();
    }
  });
});

You could even send a new WebPush message from the back-end with a special action in the payload, for example data: { action: 'close', id: 123 } to trigger messages to be closed. The 'push' eventListener can relay a 'message' event to all clients to close the given (or all) open notifications.

// sw.js
self.addEventListener('push', event => {
  let opts = event.data.json();

  if (opts.data && opts.data.action) {
    event.waitUntil(clients.matchAll().then(windowClients => {
      for (let i = 0; i < windowClients.length; i += 1) {
        windowClients[i].postMessage(opts.data);
      }
    });
  }
});

// app.js
navigator.serviceWorker.addEventListener('message', event => {
  if (event.data && event.data.action && event.data.action === 'close') {
    closeNotification(event.data.id);
  }
});

function closeNotification(id) {
  navigator.serviceWorker.ready.then(reg => {
    reg.getNotifications().then(n => {
      for (let i = 0; i < n.length; i += 1) {
        if (n[i].data && n[i].data.id && n[i].data.id === id) {
          n[i].close();
        }
      }
    });
  });
}

The biggest downside is that this method only works for active / open clients. I haven't found a way around that.

Used sources:

royarisse
  • 331
  • 2
  • 9
  • 1
    This is not a viable solution, since silent push is not allowed: i.e. when you send a push message you must show a notification - and a message that simply closes other notifications, without showing anything, would be considered silent push. – collimarco Feb 17 '21 at 10:54
  • @collimarco what makes you think silent push is not allowed and must always show a notification? My understanding is responding to a notification payload is handled by your app, your not forced to do anything with it, if you don't want to. As long as you use `event.waitUntil()` etc appropriately? – Gary Green Apr 07 '21 at 20:30
  • @GaryGreen Check out the standard: `PushManager.subscribe` has a `userVisibleOnly` option set to true that is required – collimarco Apr 07 '21 at 21:10
1

I think might be impossible. As you can show and hide a notification. And this can be a workaround for default browser action as described here

Chrome will only show the "This site has been updated in the background." notification when a push message is received and the push event in the service worker does not show a notification after the promise passed to event.waitUntil() has finished.

baduker
  • 19,152
  • 9
  • 33
  • 56
Kitsor
  • 11
  • 1
1

The closes solution that I found was closing the notification after it was shown. Below is the code solution that I have.

event.waitUntil(
    self.registration.showNotification(title, options)
    .then(function (data) {
        self.registration.getNotifications()
        .then(function (notifications) {
            setTimeout(function () {
                //console.log(notifications);
                notifications.forEach(notification => notification.close())
            }, 10);
        })
    })
);
  • 1
    Service workers cannot reliably use timeouts or intervals as the browser will shut service workers to conserve system resources. A small timeout may work but I wouldn't rely on it and especially not for longer times. – Scott Oct 03 '19 at 19:37
  • @Gavril why did you wait at all? can we just close the notifications immediately? – Surfer on the fall Jun 20 '20 at 06:20
  • `setTimeout(function {}, duration);` is defined with the duration in milliseconds. – Serge Stroobandt Apr 06 '21 at 20:51