Rails 5 Progressive Web App - part 2

December 17, 2016

PWA

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:

  1. 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.

  2. Declaring manifest.json -> already done

  3. Installing a service worker -> already done

  4. 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
      });
    });
    
  1. 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(
    ...
    
  2. 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");
        }
      });
    }
    

© 2020 Keith P | Follow on Twitter | Git