Skip to content

Add an example to implement Push Notifications? #132

@leo-petrucci

Description

@leo-petrucci

Hey everyone, great job on the plugin, it's been super useful!

While working on my PWA and this plugin I had a lot of confusion around how to get Push Notifications working, I had a hard time finding the information I needed in the docs so I just wanted to share my thoughts here.

1. There's no examples

When Googling "vite pwa push notifications", these are the results I get:

You get the idea.

2. The docs aren't really very informative

This page is the only mention of push notifications in the docs.

This links to a page that I don't think exists anymore in the Workbox docs (It redirects to this homepage https://web.dev/explore/notifications).

And it also links to the Elk app repository, which is a really good implementation, but it's also a huge and extremely complicated app which is sure to overwhelm anyone that doesn't know what they're looking at.

3. Requiring an understanding of Workbox just to implement notifications

I understand that the docs are trying to push people to read the entirety of the Workbox docs to implement notifications, but to me that seems really overkill.

Push Notifications really only require two listeners, it shouldn't require a developer to learn how to use Workbox in its entirety before they can implement it. After all, we don't expect every developer that use vite-pwa to understand what's going on in their service worker when they first install the plugin, so why should it be different for adding notifications?

Push notifications are arguably the # 1 reason people want to make a PWA, so why make it so difficult for people to find the answer they need?

Actionable points

From this there's a couple of things that I think are missing:

  1. A definite answer in the docs that clearly states that to get push notifications working you need a custom service worker
  2. A minimal custom Service Worker example which does two things:
    a. Implements the basic functionality implemented by the service worker generated by the plugin (Offline support, and whatever else)
    b. Has a minimal implementation of receiving and clicking on notifications

I think we can all agree on the fact that we want PWA adoption to increase, I think doing this will improve the developer experience massively and lower the barrier of entry to making PWAs.

I'm in no way an expert with service workers, however this is my current service worker implementation in Typescript (pieced together from Googling and the Elk repository). If nobody else has a better example I'd love if this was added to the docs.

// ./service-worker/sw.ts

/// <reference lib="WebWorker" />
/// <reference types="vite/client" />
import {
  cleanupOutdatedCaches,
  createHandlerBoundToURL,
  precacheAndRoute,
} from "workbox-precaching";
import { NavigationRoute, registerRoute } from "workbox-routing";

declare const self: ServiceWorkerGlobalScope;

self.addEventListener("message", (event) => {
  if (event.data && event.data.type === "SKIP_WAITING") self.skipWaiting();
});

const entries = self.__WB_MANIFEST;

precacheAndRoute(entries);

// clean old assets
cleanupOutdatedCaches();

// only cache pages and external assets on local build + start or in production
if (import.meta.env.PROD) {
  // to allow work offline
  registerRoute(new NavigationRoute(createHandlerBoundToURL("index.html")));
}

self.addEventListener("push", onPush);
self.addEventListener("notificationclick", onNotificationClick);

export function onPush(event: PushEvent) {
  console.log("[Service Worker] Push Received.");

  if (event.data) {
    const { title, ...rest } = event.data.json();

    event.waitUntil(
      self.registration.showNotification(title, {
        ...rest,
      })
    );
  }
}

export function onNotificationClick(event: NotificationEvent) {
  const reactToNotificationClick = new Promise((resolve) => {
    event.notification.close();
    resolve(openUrl(event.notification.data.url));
  });

  event.waitUntil(reactToNotificationClick);
}

function findBestClient(clients: WindowClient[]) {
  const focusedClient = clients.find((client) => client.focused);
  const visibleClient = clients.find(
    (client) => client.visibilityState === "visible"
  );

  return focusedClient || visibleClient || clients[0];
}

async function openUrl(url: string) {
  const clients = await self.clients.matchAll({ type: "window" });
  // Chrome 42-48 does not support navigate
  if (clients.length !== 0 && "navigate" in clients[0]) {
    const client = findBestClient(clients as WindowClient[]);
    await client.navigate(url).then((client) => client?.focus());
  }

  await self.clients.openWindow(url);
}

And this is my Vite config:

      VitePWA({
        strategies: "injectManifest",
        srcDir: "./service-worker",
        filename: "sw.ts",
        scope: "/",
        devOptions: {
          enabled: mode === "development",
        },
        // ... other configs here
      })

For the record, I'm fully willing to write the documentation page myself if given the go-ahead.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions