PWA with Angular Part 2: Web Push Notifications

#26

Who am I ?

I delete node_modules for money

Ajit Kumar Singh

Doing Software Stuff 

💶

Installing React skills ......

OUTLINE

Previously ..

Notification API

Push API

Let         do it

Please don't

PREVIOUSLY

PREVIOUSLY

Service worker what?

Script that sits between the web page and the network

Service worker facts

PREVIOUSLY

  • HTTPS or localhost only
  • runs on separate thread from the UI
  • does not require an open web page to work
  • has a life cycle

Service Worker Lifecycle

👉

self.addEventListener('fetch', event => event.respondWith(
    // Various caching strategies possible here
));

Push

self.addEventListener('push', event => {
     // Handle notification logic 
});

PREVIOUSLY

Know your limits!

  • Limited 🍎 👁️OS support
  • Can ONLY do what          technology allow

PREVIOUSLY

        Service Worker facts

ng add @angular/pwa

“one-size-fits-all” service worker

enabled only on production builds!

PREVIOUSLY

Why all this in today's talk?

PREVIOUSLY

Both are built on top of the Service Worker API

Push Notifications are assembled using two APIs:

  • Notifications API
  • Push API

NOTIFICATION API

No she doesn't, we know !

NOTIFICATION API

if ('Notification' in window) {
     // Notification is supported on this browser
}

#IEasUsual #WhyAppleWhy

Can I spam you, please?

Notification.requestPermission( status => {
  
  // value of permission can be 'granted', 'default', 'denied'
  
  console.log("Permission status:", status);
  
});

NOTIFICATION API

1. Ask for user permission to show notification

 ( Notification API's requestPermission method ) 

default

granted

denied

Can I spam you, please?

if (Notification.permission === "granted") {
 // do our magic 
} else if (Notification.permission === "denied") {
 // user showed middle finger. Can't reprompt.
} else {
 // show a prompt to the user
}

NOTIFICATION API

2. If granted: Brilliant! You can show notifications locally.

 ( Handle this ! Today's overconfidence is tomorrow's JIRA ticket ) 

3. If denied: There is nothing more you can do here.

To break it further

NOTIFICATION API

  • Invocation
  • Interaction

 clicking or closing the notification

displaying, appear, styling and vibration stuff

Notifications API could be split into two core parts:

Invocation

async function showNotification() {
  
  if (Notification.permission === "granted") {
    
    const registration = await navigator.serviceWorker.getRegistration()

    await registration.showNotification("Nice title")
  }
}

showNotification()

NOTIFICATION API

showNotification(title: string, options?: NotificationOptions): Promise<void>

Currently registered service worker

Invocation

async function showNotification() {
  if (Notification.permission === "granted") {
    const registration = await navigator.serviceWorker.getRegistration()
    const options = {
      body: "Useless notification",
      icon: "https://tiny.cc/pgozkz",
      // Only Notification.maxActions possible
      actions: [
        {
          action: "pizza",
          title: "Pizza?"
        }
      ]
    };
    await registration.showNotification("Nice title", options)
  }
}

showNotification()

NOTIFICATION API

Interaction

self.addEventListener('notificationclick', event => {
  const notification = event.notification
  const action = event.action;

  if (action === 'pizza') {
    // order more pizza logic
  }
})

NOTIFICATION API

Just some event listeners in the Service Worker file:

self.addEventListener('notificationclose', event => {
  // User dismissed the notification
})

When user clicked one of the action

When user just closed the notification

PUSH API

#IEasUsual #WtfSafari

PUSH API

if ('PushManager' in window) {
    // Push is supported on this browser
 }

PUSH API

Introducing Push Server: The middleman

  • Every browser is free to have it's own Push Service
  • You have no control over it !
  • But, all push services will follow the same API.

How Push Server fits in picture?

2

1

3

PUSH API

The subscription flow

2

1

3

4

5

PUSH API

You can then subscribe to the browser's push service

PUSH API

async function subscribeForNotifications() {
  
  if (Notification.permission === "granted") {
    
    // your magic line to subscribe user to push service
  }
}

subscribeForNotifications()

You need to save the result of this subscription to your server

After permission is granted...

Subscribing to Push Server

async function doSubscription() {
  const registration = await navigator.serviceWorker.getRegistration();
  if (registration) {
    const subscribeOptions = {
      userVisibleOnly: true, // #FlagsForFuture, currently can only be true
      applicationServerKey: base64ToUint8Array('<APPLICATION SERVER KEY>') // Ask your Server
    };
    const subscription = await registration.pushManager.subscribe(subscribeOptions);
    // Some POST call to save the subscription object on server
  }
}

PUSH API

That base64ToUint8Array thing , you WILL copy paste it: https://gist.github.com/malko/ff77f0af005f684c44639e4061fa8019

A lazy overview of PushSubscription

export interface PushSubscription {
  
  endpoint: string;  
  
  keys: {
       
    p256dh: string;
       
    auth: string;
       
  };
  
}

PUSH API

Unique to each subscription 

To encrypt your message. So Push Server can not read it.

About that whole encryption thing...

PUSH API

That application server 🔑 thing..

PUSH API

  • Also know as VAPID key.
  • VAPID: a Web Push protocol to talk to Push Server
  • VAPID stands for Voluntary Application Server Identification for Web Push.
  • Every wanna be Web Push library must implement it.
  • NodeJs implementation is in library called web-push

The Push Event

self.addEventListener('push', event => {
    if (event.data) {
        // Can also be text(), blob(), arrayBuffer()
        const notification = event.data.json()
        const promise = self.registration.showNotification('Hello, World.', notification)
        event.waitUntil(promise); // Browser please do not kill me till I am done
    } else {
        console.log('Push event but no data')
    }
})

PUSH API

To summarise 

PUSH API

ANGULAR

NO MEME

For laugh just watch me code 😭

export class SwPush {

  requestSubscription(options: { serverPublicKey: string; }): Promise<PushSubscription>

  readonly subscription: Observable<PushSubscription | null>
    
  unsubscribe(): Promise<void>

  readonly notificationClicks: Observable<{
    action: string;
    notification: NotificationOptions & {
      title: string;
    }
  }>

  readonly messages: Observable<object>

  get isEnabled(): boolean

}

ANGULAR

SwPush @ your service

Source code is pathway to ...

ANGULAR

Watch out !

ANGULAR

  • You get what you get
  •                                 interaction is missing
  • JSON only payload
  • Payload must be
{
   "notification":{
     "title": <REQUIRED>
     // Your notification payload goes here
   }
}
notificationclose

When User Experience is bad

Developers

When someone uses different framework

PLEASE D    N'T!

PLEASE D    N'T!

  • Don’t just request permission on first load ( like I did       )
  • Don’t send notifications without the proper context
  • Don’t overdo it

🔥💩 User Experience

🙏THANKS 🙏

Push Notifications

By Ajit Kumar Singh