Skip to content

Instantly share code, notes, and snippets.

@KTibow
Created April 12, 2023 11:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KTibow/77a4a69591b3b38327ca37f39a95aac5 to your computer and use it in GitHub Desktop.
Save KTibow/77a4a69591b3b38327ca37f39a95aac5 to your computer and use it in GitHub Desktop.
My system for web notifications
<script>
import { PUBLIC_NOTIF_KEY } from "$env/static/public";
let setupState = false;
async function setup() {
setupState = "loading-0";
await Notification.requestPermission();
const registration = await navigator.serviceWorker.ready;
setupState = "loading-1";
let sub;
try {
sub = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: Uint8Array.from(
atob(PUBLIC_NOTIF_KEY.replace(/-/g, "+").replace(/_/g, "/"))
.split("")
.map((x) => x.charCodeAt())
),
});
} catch (e) {
console.error(e);
setupState = "errored";
const sub = await registration.pushManager.getSubscription();
await sub.unsubscribe();
return;
}
setupState = "loading-2";
await fetch("/savePush", { method: "POST", body: JSON.stringify(sub) });
setupState = false;
}
</script>
<button class="w-full bg-stone-800 py-2 px-4" on:click={setup}>Get the notification</button>
{#if setupState == "loading-0"}
<p class="mt-2 text-stone-300">Setting up for notifications</p>
{:else if setupState == "loading-1"}
<p class="mt-2 text-stone-300">Getting the notification bridge</p>
{:else if setupState == "loading-2"}
<p class="mt-2 text-stone-300">Communicating with the server</p>
{:else if setupState == "errored"}
<p class="mt-2 text-red-300">Configuring notification failed, try again</p>
{/if}
import { ApplicationServerKeys, generatePushHTTPRequest, setWebCrypto } from "webpush-webcrypto";
import { PUBLIC_NOTIF_KEY } from "$env/static/public";
import { PRIVATE_NOTIF_KEY } from "$env/static/private";
setWebCrypto(crypto);
let keys;
export function unflattenTarget(target) {
return { endpoint: target.endpoint, keys: { p256dh: target.p256dh, auth: target.auth } };
}
export async function sendNotification(target, payload) {
if (!keys)
keys = await ApplicationServerKeys.fromJSON({
publicKey: PUBLIC_NOTIF_KEY,
privateKey: PRIVATE_NOTIF_KEY,
});
const { headers, body, endpoint } = await generatePushHTTPRequest({
applicationServerKeys: keys,
payload: JSON.stringify(payload),
target,
ttl: 60 * 60 * 24,
});
return await fetch(endpoint, {
method: "POST",
headers,
body,
});
}
/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />
/// <reference lib="webworker" />
/** @type {ServiceWorkerGlobalScope} sw */
const sw = self;
sw.addEventListener("install", () => sw.skipWaiting());
sw.addEventListener("push", async (event) => {
const data = await event.data.json();
// logic here, must always send notification
});
sw.addEventListener("notificationclick", (event) => {
// logic here
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment