📜 ⬆️ ⬇️

Service Workers. Web Push and where they live



Welcome to the school CODEdeystvosti and magic!

In today's lesson we will learn how to use the little-known Web Push + Service Workers (SW) tandem. I’ll open the curtain for you: I’ll tell you about the way to keep the Muggle audience thanks to Web Push technology and how this can be useful for editing websites and other Internet services.

The text continues the article on the basic application of SW. From the comments and questions, I realized that people are interested in the direction of SW + Web Push. Let's take a look at what it is and how to use this magic pair in practice.
')

What is push notification?


You receive alerts to your email: go to your email client and look for incoming emails. In this case, it is a pull technology, that is, you go to the site and “pull” data from it when you need it.

In the case of push notifications, the resource pushes new data to you yourself. At the same time, you immediately get the latest data, because this technology does not have a specific data verification period, they come online. Using push is not limited to receiving notifications. So, through the push-technology, you can synchronize data when updating.

Push notifications are small pop-ups. They can appear on the screen, where there is an alert area or there is the possibility of displaying the received data. Push notifications work even when the user has left the site. This means that you can deliver messages about new materials and new events to draw the user's attention to your application.



Do not forget about support in browsers. So in IE, Edge and Safari your Push-notifications will not work.



Preparations for the magic bundle SW and Web-push


As always, we will need to write our own SW to work with Web push:


All that needs to be done is to connect index.js in index.html, in which the sw.js file will be registered.

In the server.js file, I will only indicate the endpoint (entry point to the server application API) for registering push notifications.

Sample server.js code
//    web-push,      //      push. //     // . https://tools.ietf.org/html/draft-ietf-webpush-protocol //  https://tools.ietf.org/html/draft-ietf-webpush-encryption. var webPush = require('web-push'); //  GCM_API_KEY      // https://developers.google.com/cloud-messaging/ webPush.setGCMAPIKey(process.env.GCM_API_KEY || null); //        route'  express.js module.exports = function(app, route) { app.post(route + 'register', function(req, res) { res.sendStatus(201); }); app.post(route + 'sendNotification', function(req, res) { setTimeout(function() { //     payload,     'auth'  'p256dh'. webPush.sendNotification({ endpoint: req.body.endpoint, TTL: req.body.ttl, keys: { p256dh: req.body.key, auth: req.body.authSecret } }, req.body.payload) .then(function() { res.sendStatus(201); }) .catch(function(error) { res.sendStatus(500); console.log(error); }); }, req.query.delay * 1000); }); }; 


In the article we will consider options for sending push-notifications and how to use them. Let's get acquainted with magic outside of Hogwarts together.

Push payload


This simplest magic spell shows how to send and receive strings, but data can be extracted from push messages in various formats: strings, ArrayBuffer buffer, BLOB object in JSON.

Mode of application

If you just need push notifications, this example is for you. The message can deliver not only text, but also payload - enriched data for the application. The code below demonstrates how you can deliver payload for your application.

For the demonstration, we use data from the text field that will be sent to the server and then displayed as push notifications via SW.

index.js
 var endpoint; var key; var authSecret; navigator.serviceWorker.register('service-worker.js') .then(function(registration) { //  PushManager,      -. return registration.pushManager.getSubscription() .then(function(subscription) { //      . if (subscription) { return subscription; } //   ,  . // userVisibleOnly -   ,   push- //     , //      . return registration.pushManager.subscribe({ userVisibleOnly: true }); }); }).then(function(subscription) { //  public key  . var rawKey = subscription.getKey ? subscription.getKey('p256dh') : ''; key = rawKey ? btoa(String.fromCharCode.apply(null, new Uint8Array(rawKey))) : ''; var rawAuthSecret = subscription.getKey ? subscription.getKey('auth') : ''; authSecret = rawAuthSecret ? btoa(String.fromCharCode.apply(null, new Uint8Array(rawAuthSecret))) : ''; endpoint = subscription.endpoint; //        Fetch API fetch('./register', { method: 'post', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ endpoint: subscription.endpoint, key, authSecret, }), }); }); //   . //    ""   , ..      . document.getElementById('doIt').onclick = function() { var payload = document.getElementById('notification-payload').value; var delay = document.getElementById('notification-delay').value; var ttl = document.getElementById('notification-ttl').value; fetch('./sendNotification', { method: 'post', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ endpoint: endpoint, payload: payload, delay: delay, ttl: ttl, key: key, authSecret: authSecret }), }); }; 


service-worker.js
 //     'push' self.addEventListener('push', function(event) { var payload = event.data ? event.data.text() : 'Alohomora'; event.waitUntil( //       . self.registration.showNotification('My first spell', { body: payload, }) ); }); 


Rich notifications


Complicate the previous version and add special effects, it all depends on your desires and fantasy. We will help complete Notification API . The API provides an interface for using "live" push-notifications to the user with an indication of the locale, pattern of vibration , image.

Mode of application

The example is similar to that described above, but allows using the more advanced Notification API to select an image, set a locale and a notification template — that is, make the notification unique.

service-worker.js
 //      Push Payload    // Notitfication API  SW self.addEventListener('push', function(event) { var payload = event.data //   -     ,  // try.. catch    ¯\_(ツ)_/¯ ? JSON.parse(event.data) : { name: 'Expecto patronum!', icon: 'buck.jpg', locale: 'en' }; event.waitUntil( //       . //   : // *  // *   // *  //    ,       // https://notifications.spec.whatwg.org/ self.registration.showNotification('Summoning spell', { lang: payload.locale, body: payload.name, icon: payload.icon, vibrate: [500, 100, 500], }) ); }); 


Push tag


The next spell will demonstrate the accumulation and replacement of the previous spell by a stronger one. Use the notification tag to replace the old notifications with new ones. Allows you to show Muggles only relevant information or minimize several notifications into one.

Mode of application

The option is suitable for those applications where there are chats or notifications about new content (examples: Tproger and Tinder). The code below demonstrates how to manage the notification queue so that previous notifications can be dropped or merged into one notification. This is useful to have a fallback in case we wrote a chat where you can edit messages. The client will see not a ton of notifications with corrections, but only one.

service-worker.js
 var num = 1; self.addEventListener('push', function(event) { event.waitUntil( //       . // ,      . //         // (       ) self.registration.showNotification('Attacking Spell', { body: ++num > 1 ? 'Bombarda Maxima' : 'Bombarda', tag: 'spell', }) ); }); 


Push clients


The time has come for "unforgivable spells." Let me remind you why they are inexcusable:
any excessive use of these spells on muggles is punishable by life imprisonment in Azkaban . Therefore, the main thing - do not bother!

When a muggle clicks on a notification generated from a push event, it will focus it on the application's tab or even reopen it if it has been closed.

Mode of application

Below is the code for the three use cases for delivering notifications depending on the state of the application.

It can recognize when you are on a page or need to switch to an open tab or re-open a tab.

The method will help you to increase the conversion of the application if you want to return the Muggle. Just do not go too far, Muggles may not like the excessive attention of the application.

The most classic examples of use:


In all these cases, when you click on the push client, our application will open or it will be focused on the open tab.

service-worker.js
 self.addEventListener('install', function(event) { event.waitUntil(self.skipWaiting()); }); self.addEventListener('activate', function(event) { event.waitUntil(self.clients.claim()); }); self.addEventListener('push', function(event) { event.waitUntil( //     SW self.clients.matchAll().then(function(clientList) { // ,       . var focused = clientList.some(function(client) { return client.focused; }); var notificationMessage; if (focused) { notificationMessage = 'Imperio! You\'re still here, thanks!'; } else if (clientList.length > 0) { notificationMessage = 'Imperio! You haven\'t closed the page, ' + 'click here to focus it!'; } else { notificationMessage = 'Imperio! You have closed the page, ' + 'click here to re-open it!'; } //     «Unforgiveable Curses» //        SW // (  : // * 1,  ; // * 2,  - ,   ; // * 3,  ). return self.registration.showNotification('Unforgiveable Curses', { body: notificationMessage, }); }) ); }); //    'notificationclick'. self.addEventListener('notificationclick', function(event) { event.waitUntil( //    SW. self.clients.matchAll().then(function(clientList) { //      ,  . if (clientList.length > 0) { return clientList[0].focus(); } //      . return self.clients.openWindow('our/url/page'); }) ); }); 


Push subscription


It's time to take control of our Muggle mind. Muggles call this “telepathy” or mind reading, but we will do it differently. Let's learn how to put our information and make it tied to our application. This example shows how to use push notifications with subscription management, allowing users to subscribe to and keep in touch with the application. We try to remember about Azkabane!

Mode of application

After SW is registered, the client checks whether he is subscribed to the notification service. Depending on this, the text of the button is set.

After a successful subscription ( index.js :: pushManager.subscribe ), the client sends a post-request to the application server to register the subscription.

The server periodically sends a notification using the web-push library to all registered endpoints. If the endpoint is no longer registered (subscription has expired or canceled), the current subscription is removed from the list of subscriptions.

After a successful unsubscribe ( index.js :: pushSubscription.unsubscribe ), the client sends a post-request to the application server to cancel the registration of the subscription. The server no longer sends notifications. SW also monitors pushsubscriptionchange and resubscribes events .

A subscription can be canceled by the user off-page in the browser settings or UI notifications. In this case, the backend will stop sending notifications, but the front-end will not know about it. For magic to work, it is important to periodically check whether registration is active in the notification service.

index.js
 //     . //      . var subscriptionButton = document.getElementById('subscriptionButton'); //       ,   , //   Promise. function getSubscription() { return navigator.serviceWorker.ready .then(function(registration) { return registration.pushManager.getSubscription(); }); } if ('serviceWorker' in navigator) { navigator.serviceWorker.register('service-worker.js') .then(function() { console.log('SW registered'); subscriptionButton.removeAttribute('disabled'); }); getSubscription() .then(function(subscription) { if (subscription) { console.log('Already invaded', subscription.endpoint); setUnsubscribeButton(); } else { setSubscribeButton(); } }); } //  «registration»  SW    //    `registration.pushManager.subscribe`. //    ,  POST-. function subscribe() { navigator.serviceWorker.ready.then(function(registration) { return registration.pushManager.subscribe({ userVisibleOnly: true }); }).then(function(subscription) { console.log('Legilimens!', subscription.endpoint); return fetch('register', { method: 'post', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ endpoint: subscription.endpoint }) }); }).then(setUnsubscribeButton); } //     SW, //   (`subscription.unsubscribe ()`)  //       POST- //    push-. function unsubscribe() { getSubscription().then(function(subscription) { return subscription.unsubscribe() .then(function() { console.log('Unsubscribed', subscription.endpoint); return fetch('unregister', { method: 'post', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ endpoint: subscription.endpoint }) }); }); }).then(setSubscribeButton); } //   ( ).   . function setSubscribeButton() { subscriptionButton.onclick = subscribe; subscriptionButton.textContent = 'Open mind!'; } function setUnsubscribeButton() { subscriptionButton.onclick = unsubscribe; subscriptionButton.textContent = 'Protego!'; } 


service-worker.js
 //   'push'. self.addEventListener('push', function(event) { event.waitUntil(self.registration.showNotification('Your mind', { body: 'Wizard invaded to your mind!' })); }); //   'pushsubscriptionchange',  , //    . //        , //  POST-. //        ID  token //   . self.addEventListener('pushsubscriptionchange', function(event) { console.log('Spell expired'); event.waitUntil( self.registration.pushManager.subscribe({ userVisibleOnly: true }) .then(function(subscription) { console.log('Another invade! Legilimens!', subscription.endpoint); return fetch('register', { method: 'post', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ endpoint: subscription.endpoint }) }); }) ); }); 


server.js
 var webPush = require('web-push'); var subscriptions = []; var pushInterval = 10; webPush.setGCMAPIKey(process.env.GCM_API_KEY || null); //   push-. //      `subscriptions`, //  push-        . function sendNotification(endpoint) { webPush.sendNotification({ endpoint: endpoint }).then(function() { }).catch(function() { subscriptions.splice(subscriptions.indexOf(endpoint), 1); }); } //          //  . //   ,     `pushInterval`  //   setInterval(function() { subscriptions.forEach(sendNotification); }, pushInterval * 1000); function isSubscribed(endpoint) { return (subscriptions.indexOf(endpoint) >= 0); } module.exports = function(app, route) { app.post(route + 'register', function(req, res) { var endpoint = req.body.endpoint; if (!isSubscribed(endpoint)) { console.log('We invaded into mind ' + endpoint); subscriptions.push(endpoint); } res.type('js').send('{"success":true}'); }); // Unregister a subscription by removing it from the `subscriptions` array app.post(route + 'unregister', function(req, res) { var endpoint = req.body.endpoint; if (isSubscribed(endpoint)) { console.log('It was counterspell from ' + endpoint); subscriptions.splice(subscriptions.indexOf(endpoint), 1); } res.type('js').send('{"success":true}'); }); }; 


One more time on spells


Above, we looked at the magical ways of using SW and Web Push for applications.
This tandem is fraught with many interesting applications.

If you only need to sometimes invite Muggle to your application or inform him about corrections or change of the status of his order, then use Push Payload. You can add a little imagination and use Notification API - then the colors and the icon of your application will be visible to the user in Rich Push.

If you want to seize all the attention of Muggle and make contact with him - examples of Push Client and Push Subscription for you. The main thing - remember about Azkaban, otherwise you will start to lose your audience.

Waiting for your comments and suggestions on the next topic. From myself I would add that I would like to talk and discuss the topic of SW + React / Redux-applications work and methods of acceleration. Would it be helpful?

Source: https://habr.com/ru/post/350034/


All Articles