PWA with Angular Part 2: Web Push Notifications
I delete node_modules for money
Ajit Kumar Singh
Doing Software Stuff
💶
Installing React skills ......
Previously ..
Notification API
Push API
Let do it
Please don't
Service worker what?
Source: https://tinyurl.com/y526jzel
Script that sits between the web page and the network
Service worker facts
Service Worker Lifecycle
👉
self.addEventListener('fetch', event => event.respondWith(
// Various caching strategies possible here
));Push
self.addEventListener('push', event => {
// Handle notification logic
});Know your limits!
Service Worker facts
ng add @angular/pwa
“one-size-fits-all” service worker
enabled only on production builds!
Why all this in today's talk?
Both are built on top of the Service Worker API
Push Notifications are assembled using two APIs:
No she doesn't, we know !
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);
});
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
}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
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()
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()
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
}
})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
#IEasUsual #WtfSafari
if ('PushManager' in window) {
// Push is supported on this browser
}Introducing Push Server: The middleman
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
The subscription flow
2
1
3
4
5
You can then subscribe to the browser's push service
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
}
}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;
};
}Unique to each subscription
To encrypt your message. So Push Server can not read it.
About that whole encryption thing...
That application server 🔑 thing..
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')
}
})To summarise
Source: https://tinyurl.com/u9a6x2l
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
}SwPush @ your service
Source code is pathway to ...
Watch out !
{
"notification":{
"title": <REQUIRED>
// Your notification payload goes here
}
}notificationcloseWhen User Experience is bad
Developers
When someone uses different framework
🔥💩 ≠ User Experience