Implement proof of concept push notifications
This commit is contained in:
parent
e3210afe3a
commit
6007f4caeb
13 changed files with 407 additions and 1 deletions
120
ui/push-notification.tsx
Normal file
120
ui/push-notification.tsx
Normal file
|
@ -0,0 +1,120 @@
|
|||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function notificationUnsupported() {
|
||||
return (
|
||||
!("serviceWorker" in navigator)
|
||||
|| !("PushManager" in window)
|
||||
|| !("showNotification" in ServiceWorkerRegistration.prototype)
|
||||
)
|
||||
}
|
||||
|
||||
async function registerAndSubscribe(
|
||||
vapidPublicKey: string,
|
||||
onSubscribe: (subs: PushSubscription | null ) => void,
|
||||
) {
|
||||
try {
|
||||
await navigator.serviceWorker.register("/sw.js");
|
||||
await subscribe(vapidPublicKey, onSubscribe);
|
||||
} catch (err) {
|
||||
console.error("Failed to register service worker:", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function subscribe(
|
||||
vapidPublicKey: string,
|
||||
onSubscribe: (subs: PushSubscription | null) => void
|
||||
) {
|
||||
try {
|
||||
const registration = await navigator.serviceWorker.ready;
|
||||
const subscription = await registration.pushManager.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: vapidPublicKey,
|
||||
});
|
||||
console.log("Got subscription object", subscription.toJSON());
|
||||
await submitSubscription(subscription);
|
||||
onSubscribe(subscription);
|
||||
} catch (err) {
|
||||
console.error("Failed to subscribe:" , err);
|
||||
}
|
||||
}
|
||||
|
||||
async function unsubscribe(
|
||||
subscription: PushSubscription,
|
||||
onUnsubscribed: () => void,
|
||||
) {
|
||||
const body = JSON.stringify({ subscription });
|
||||
await subscription.unsubscribe();
|
||||
const res = await fetch("/api/unsubscribe", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body,
|
||||
});
|
||||
const result = await res.json();
|
||||
console.log("/api/unsubscribe returned", result);
|
||||
onUnsubscribed();
|
||||
}
|
||||
|
||||
async function submitSubscription(subscription: PushSubscription) {
|
||||
const res = await fetch("/api/subscribe", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ subscription }),
|
||||
});
|
||||
const result = await res.json();
|
||||
console.log("/api/subscribe returned", result);
|
||||
}
|
||||
|
||||
async function getSubscription(
|
||||
onSubscribe: (subs: PushSubscription | null) => void,
|
||||
) {
|
||||
const registration = await navigator.serviceWorker.ready;
|
||||
const subscription = await registration.pushManager.getSubscription();
|
||||
if (subscription) {
|
||||
onSubscribe(subscription);
|
||||
}
|
||||
}
|
||||
|
||||
export function PushNotification(props: { vapidPublicKey: string }) {
|
||||
const [unsupported, setUnsupported] = useState<boolean | undefined>(undefined);
|
||||
const [subscription, setSubscription] = useState<PushSubscription | null>(null);
|
||||
useEffect(() => {
|
||||
const isUnsupported = notificationUnsupported();
|
||||
setUnsupported(isUnsupported);
|
||||
if (isUnsupported) {
|
||||
return;
|
||||
}
|
||||
|
||||
getSubscription(setSubscription);
|
||||
}, [props.vapidPublicKey])
|
||||
|
||||
return <section>
|
||||
Notifications are: <b>{subscription ? "Enabled" : "Disabled"}</b>
|
||||
<br />
|
||||
<button
|
||||
disabled={unsupported}
|
||||
onClick={() => {
|
||||
if (!subscription)
|
||||
registerAndSubscribe(props.vapidPublicKey, setSubscription)
|
||||
else
|
||||
unsubscribe(subscription, () => setSubscription(null))
|
||||
}}
|
||||
>
|
||||
{unsupported === undefined && "Checking for support"}
|
||||
{unsupported === true && "Notifications are not supported :(."}
|
||||
{unsupported === false && subscription ? "Disable notifications" : "Enable notifications"}
|
||||
</button>
|
||||
<details>
|
||||
<summary>Debug</summary>
|
||||
<pre>
|
||||
<code>
|
||||
{JSON.stringify(subscription?.toJSON(), undefined, 4) ?? "No subscription set"}
|
||||
</code>
|
||||
</pre>
|
||||
</details>
|
||||
</section>;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue