8

I'm creating a PWA using angular 7, but I'm struggling with the push notifications.

I was following the tutorials as this and this using SWPush and Vapid keypairs. I was able to subscribe and unsubscribe and my server part works as well, but unfortunately the tutorials do not cover showing the actual notification in the app.

As I understood it, the notifications should pop up automatically without calling it in the code, right?. You just subscribe and the ngsw does the rest. Unfortunately they don't show up in my case. I can show them only by manually calling them in the code like this:

 this.swPush.messages.subscribe(
  (notification: any) => {
    console.log("received push message", notification);

    let options = {
      body: notification.body,
      icon: "assets/icon/favicon.png",
      actions: <any>notification.actions,
      data: notification.data,
      vibrate: [100, 50, 10, 20, 20]
    };
    this.showNotification(notification.title, options)
  },

  err => {
    console.error(err);
  }
);

[...]

showNotification(title: string, options: NotificationOptions) {
    navigator.serviceWorker.getRegistration().then(reg => {
      reg.showNotification(title, options).then(res => {
        console.log("showed notification", res)
      }, err => {
        console.error(err)
      });
   });
}

But this seems to work only when the app/page is in foreground. Otherwise I get a notification like "The site has been updated in the background". Obviously I am doing something wrong here.But what?

  1. What do I have to do to show notifications when the app is in the background?

  2. Is there a way to handle click events on the notifications?

The ng documentation is really sparse here.

Chris
  • 4,238
  • 4
  • 28
  • 49
  • 1
    You still have one more documentation to visit, have you tried [this documentation](https://developers.google.com/web/fundamentals/push-notifications/) regarding web fundamentals? – MαπμQμαπkγVπ.0 Nov 19 '18 at 10:59
  • Thanks, yes I've seen it and in the end this is what my code above is doing. However it seems not to work when the app is in background :-/ – Chris Nov 19 '18 at 15:02

2 Answers2

10

After lots of struggle I found something and will share it for others:

Digging into the ngsw-worker.js that is created by running build --prod there is this function:

onPush(msg) {
    // Push notifications without data have no effect.
    if (!msg.data) {
        return;
    }
    // Handle the push and keep the SW alive until it's handled.
    msg.waitUntil(this.handlePush(msg.data.json()));
}
...
handlePush(data) {
    return __awaiter$5(this, void 0, void 0, function* () {
            yield this.broadcast({
                type: 'PUSH',
                data,
            });

            if (!data.notification || !data.notification.title) {
                return;
            }

            const desc = data.notification;

            let options = {};
            NOTIFICATION_OPTION_NAMES.filter(name => desc.hasOwnProperty(name))
                .forEach(name => options[name] = desc[name]);
            yield this.scope.registration.showNotification(desc['title'], options);
    });
}

As it looks like angular subscribes and shows the notification for you, it is not necessary to subscribe and show it in your own code. I had an issue with my sending library (https://github.com/laravel-notification-channels/webpush) that was sending the payload like this:

payload = {"title":"test","actions":[{"title":"View App","action":"view_app"}],"body":"test","icon":"\/icon.png","data":{"id":"21a3804e-6d71-4a56-b513-535709c37c0f"}}

but angular expects obviously something like:

payload = {
    "notification":{"title":"test","actions":[{"title":"View App","action":"view_app"}],"body":"test","icon":"\/icon.png","data":{"id":"21a3804e-6d71-4a56-b513-535709c37c0f"}}
}

which caused this line in ngsw-worker.js to fail:

if (!data.notification || !data.notification.title) {
    return;
}

Therefor, no notification was shown, but the browser reported an update in the background. Hope this helps someone.

Chris
  • 4,238
  • 4
  • 28
  • 49
  • @Silviu: I modified the server side to send the payload in the form ngsw wants to have it. If you use the same setup (laravel/webpush) I can tell you what I did exactly. – Chris Oct 30 '19 at 17:04
  • I understand. We use OneSignal and I cannot figure out how I can apply your solution to my case.. Thanks – Silviu Oct 31 '19 at 18:32
  • @Silviu If you cannot define the payload bc. of onesignal, you could try to implement a custom sw additionally and overwrite the push notification stuff. Check out this on how to create an additional serviceworker. https://stackoverflow.com/questions/55256689/check-service-worker-installation-progress-from-component-angular/56067366#56067366 Then try to copy the push notification handling from the script created by angular (once you run angular build) and modify it according to your payload. Installing a second serviceworker script works well, the rest I did not test. – Chris Nov 01 '19 at 16:04
4

Is there a way to handle click events on the notifications?

Try this:

this.swPush.notificationClicks.subscribe(data => {
    // handle click
})