Inspired by some evangelical Progressive Web App (PWA) posts, we wanted to see what PWA goodness we could bring to our Rails 5, Turbolinks 5, Websocket-using web project. So this post is about what steps we have taken thus far.
In Part 1, we added service workers and achieved the “Installable”, “Discoverable” and “App-like” and “Connectivity-independent” improvements.
Now in Part 2 we look at achieving “Re-engageable”: User re-engagement with displayed push notifications via service workers, even when a tab is not open.
Add push notifications
We followed the steps outlined in the excellent https://rossta.net/blog/using-the-web-push-api-with-vapid.html but based the code on https://github.com/rossta/serviceworker-rails-sandbox
Objectives:
Set up the following suite of capabilities :
- Requesting permission to send notifications/push messages.
- Registering and activating a service worker to handle the Push/Channel Messages.
- Subscribing/unsubscribing (to/from) the push sevice.
- Sending a push message from the server.
- Sending messages to and from the SW via a message channel.
- Receiving a push message in the SW via the onpush handler and firing a notification and sending a channel message as a result.
The result is - it works in production in both Firefox and Chrome, on desktop and Android phone.
Here are the steps we took:
-
To gemfile add:
gem 'webpush'
, and generate VAPID keys in a Ruby console:$ require 'webpush' $ vapid_key = Webpush.generate_key $ vapid_key.public_key $ vapid_key.private_key
keep these values safe somewhere, add VAPID_PUBLIC_KEY & VAPID_PRIVATE_KEY with their values to AWS & configuration.
-
Declaring manifest.json -> already done
-
Installing a service worker -> already done
-
Subscribing to push notifications: To app/views/layouts/_head_meta.html.erb add:
window.vapidPublicKey = new Uint8Array(<%= @decodedVapidPublicKey %>);
Add push-messaging-and-notifications.js with content:
// When serviceWorker is supported, installed, and activated, // subscribe the pushManager property with the vapidPublicKey navigator.serviceWorker.ready.then((serviceWorkerRegistration) => { serviceWorkerRegistration.pushManager .subscribe({ userVisibleOnly: true, applicationServerKey: window.vapidPublicKey }); });
-
Triggering a web push notification To push-messaging-and-notifications.js add:
$('.webpush-button').on('click', (e) => { navigator.serviceWorker.ready .then((serviceWorkerRegistration) => { serviceWorkerRegistration.pushManager.getSubscription() .then((subscription) => { $.post('/push', { subscription: subscription.toJSON(), message: 'You clicked a button!' }); }); }); });
Add webpush_job & webpush_client :
post '/push' do Webpush.payload_send( ...
-
Receiving the push event: to service-worker-addon.js add:
self.addEventListener("push", (event) => { ... Get User Permission for Notifications: To push-messaging-and-notifications.js add: // Let's check if the browser supports notifications if (!("Notification" in window)) { console.error("This browser does not support desktop notification"); } // Let's check whether notification permissions have already been granted else if (Notification.permission === "granted") { console.log("Permission to receive notifications has been granted"); } // Otherwise, we need to ask the user for permission else if (Notification.permission !== 'denied') { Notification.requestPermission(function (permission) { // If the user accepts, let's create a notification if (permission === "granted") { console.log("Permission to receive notifications has been granted"); } }); }