121 lines
3.2 KiB
TypeScript
121 lines
3.2 KiB
TypeScript
|
"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>;
|
||
|
}
|