score:1

Accepted answer

To answer my own question: I've used IndexedDB (can't use localStorage as it is synchronous) to communicate between SW and PWA, though I'm not too happy about it.

This is roughly how my service worker code looks (I'm using idb library):

self.addEventListener('notificationclick', function(event) {
    const notif = event.notification;
    notif.close();
    if (notif.data) {
        let db;
        let p = idb.openDB('my-store', 1, {
            upgrade(db) {
                db.createObjectStore(OBJSTORENAME, {
                    keyPath: 'id'
                });
            }
        }).then(function(idb) {
            db = idb;
            return db.clear(OBJSTORENAME);
        }).then(function(rv) {            
            return db.put(OBJSTORENAME, notif.data);
        }).then(function(res) {
            clients.openWindow('/');
        }).catch(function(err) {
            console.log("Error spawning notif", err);
        });
        event.waitUntil(p);
    }
});

and then, in the root of my react app ie in my AppNavBar component I always check if there is something to show:

componentWillMount() {
    let self = this;
    let db;
    idb.openDB('my-store', 1)
    .then(function (idb) {
        db = idb;            
        return db.getAll(OBJSTORENAME);
    }).then(function (items) {            
        if (items && items.length) {
            axios.get(`/some-additional-info-optional/${items[0].id}`).then(res => {
                if (res.data && res.data.success) {
                    self.props.history.push({
                        pathname: '/details',
                        state: {
                            selectedObject: res.data.data[0]
                        }
                    });
                }
            });
            db.clear(OBJSTORENAME)
            .then()
            .catch(err => {
                console.log("error clearing ", OBJSTORENAME);
            });
        }
    }).catch(function (err) {
        console.log("Error", err);
    });
}

Have been toying with clients.openWindow('/?id=123'); and clients.openWindow('/#123'); but that was behaving strangely, sometimes the app would stall, so I reverted to the IndexedDB approach. (clients.postMessage could also be the way to go though I'm not sure how to plug that into the react framework)

HTH someone else, and I'm still looking for a better solution.

score:1

You can listen for click event for the Notification which you show to the user. And in the handler, you can open the URL for the corresponding person which comes from your server with push event.

notification.onclick = function(event) {
  event.preventDefault(); 

  // suppose you have an url property in the data
  if (event.notification.data.url) {

      self.clients.openWindow(event.notification.data.url);
  }
}

Check these links:

score:1

I had a similar need in my project. Using your's postMessage tip, I was able to get an event on my component every time a user clicks on service worker notification, and then route the user to the desired path.

service-worker.js

self.addEventListener("notificationclick", async event => {
    const notification = event.notification;

    notification.close();

    event.waitUntil(
        self.clients.matchAll({ type: "window" }).then(clientsArr => {
            if (clientsArr[0]) {
                clientsArr[0].focus();
                clientsArr[0].postMessage({
                    type: "NOTIFICATION_CLICK",
                    ticketId: notification.tag,
                });
            }
        })
    );
});

On your react component, add a new listener:

useEffect(() => {
    if ("serviceWorker" in navigator) {
        navigator.serviceWorker.addEventListener("message", message => {
            if (message.data.type === "NOTIFICATION_CLICK") {
                history.push(`/tickets/${message.data.ticketId}`);
            }
        });
    }
}, [history]);

Related Query

More Query from same tag