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?

Source: https://tinyurl.com/y526jzel
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
Try this out: https://tests.peter.sh/notification-generator/
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.
For the smarty pants: https://tools.ietf.org/html/draft-ietf-webpush-protocol-12
Push API: https://www.w3.org/TR/push-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
VAPID Specification: https://tools.ietf.org/html/draft-thomson-webpush-vapid-02
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

Source: https://tinyurl.com/u9a6x2l
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
Push Notifications
- 260