-
-
Notifications
You must be signed in to change notification settings - Fork 61
Description
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:
- An app-specific implementation of a service worker with loads of added stuff (https://stackoverflow.com/questions/75988769/vite-pwa-plugin-how-to-add-webpush-notifications)
- The "Getting Started" guide on the docs, which only has a passing mention of push notifications (https://vite-pwa-org.netlify.app/guide/)
- Reddit thread where it's clear nobody really knows what to do (https://www.reddit.com/r/vuejs/comments/12ghvak/pwa_with_vite/)
- Part of an implementation, that doesn't show what a basic service worker would look like (Webpush support in service workers vite-plugin-pwa#84)
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:
- A definite answer in the docs that clearly states that to get push notifications working you need a custom service worker
- 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.