// composables/usePushNotifications.ts import { useLocalStorage, usePermission } from "@vueuse/core"; import { onMounted, ref } from "vue"; import useSubscribeNotification from "./useSubscribeNotification"; interface VapidKeys { publicKey: string; } export const usePushNotifications = () => { const isSupported = ref(false); const permission = usePermission("notifications"); const subscription = useLocalStorage( "push-subscription", null ); const vapid = ref(null); const { mutateAsync: subscribeNotification } = useSubscribeNotification(); const toast = useToast(); // Only run in client-side onMounted(async () => { if (typeof window !== "undefined" && "serviceWorker" in navigator) { isSupported.value = true; vapid.value = await $fetch("/api/vapid"); } }); const subscribe = async () => { if (!isSupported.value || !vapid.value?.publicKey) { throw new Error("Push notifications not supported"); } const swRegistration = await navigator.serviceWorker.ready; const applicationServerKey = vapid.value.publicKey .replace(/-/g, "+") .replace(/_/g, "/"); const convertedKey = Uint8Array.from(atob(applicationServerKey), (c) => c.charCodeAt(0) ); const pushSubscription = await swRegistration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: convertedKey, }); const subscriptionJson = pushSubscription.toJSON(); subscribeNotification({ body: subscriptionJson, }); subscription.value = subscriptionJson; }; return { isSupported, permission, subscribe, subscription, }; };