owltide/composables/push-notification.ts

114 lines
2.7 KiB
TypeScript
Raw Permalink Normal View History

/*
SPDX-FileCopyrightText: © 2025 Hornwitser <code@hornwitser.no>
SPDX-License-Identifier: AGPL-3.0-or-later
*/
function notificationSupported() {
return (
import.meta.client
&& "serviceWorker" in navigator
&& "PushManager" in window
&& "showNotification" in ServiceWorkerRegistration.prototype
);
}
export const usePushNotification = () => {
const subscription = ref<PushSubscription | null>(null);
const supported = ref<boolean | undefined>(undefined);
const runtimeConfig = useRuntimeConfig();
function checkSupport() {
return supported.value = notificationSupported();
}
async function postSubscription(subscription: PushSubscriptionJSON) {
const result = await $fetch("/api/subscribe", {
method: "POST",
body: { subscription },
});
console.log("/api/subscribe returned", result);
}
async function postUnsubscription() {
const result = await $fetch("/api/unsubscribe", {
method: "POST",
});
console.log("/api/unsubscribe returned", result);
}
async function subscribe() {
if (!checkSupport())
return;
let registration;
try {
registration = await navigator.serviceWorker.register("/sw.js");
} catch (err) {
console.error("Failed to register service worker:", err);
return;
}
// Check if we already have a subscription.
if (!subscription.value) {
subscription.value = await registration.pushManager.getSubscription();
}
// Create a new push subscription if none exists.
if (!subscription.value) {
try {
subscription.value = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: runtimeConfig.public.vapidPublicKey,
});
} catch (err) {
console.error("Failed to subscribe:" , err);
return;
}
}
console.log("Subscribing with", subscription.value);
// Tell server about the new subscription.
try {
await postSubscription(subscription.value.toJSON());
} catch (err) {
console.log("Failed to post subscription to server", err)
return;
}
}
async function unsubscribe() {
if (!checkSupport())
return;
// Fetch subscription if it hasn't already been fetched.
if (!subscription.value) {
await getSubscription();
}
if (!subscription.value) {
return;
}
await subscription.value.unsubscribe();
subscription.value = null;
await postUnsubscription();
}
async function getSubscription() {
if (!checkSupport())
return;
const registration = await navigator.serviceWorker.getRegistration("./sw.js");
if (!registration)
return;
return subscription.value = await registration.pushManager.getSubscription();
}
return {
supported,
subscription,
getSubscription,
subscribe,
unsubscribe,
}
}