import { useCallback, useEffect, useState } from "react"; import { pushApi } from "../api/push"; /** * Converts a base64url-encoded VAPID public key to a Uint8Array * as required by pushManager.subscribe({ applicationServerKey }). */ function urlBase64ToUint8Array(base64String: string): Uint8Array { const padding = "=".repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/"); const rawData = atob(base64); const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i); } return outputArray; } const isSupported = typeof navigator !== "undefined" && "serviceWorker" in navigator && "PushManager" in window && "Notification" in window; export function usePushNotifications(): { isSupported: boolean; permission: NotificationPermission | "unsupported"; subscribe: () => Promise; unsubscribe: () => Promise; } { const [permission, setPermission] = useState( isSupported ? Notification.permission : "unsupported", ); // Poll for permission changes (e.g., user revokes permission in browser settings) useEffect(() => { if (!isSupported) return; const interval = setInterval(() => { const current = Notification.permission; setPermission((prev) => (prev !== current ? current : prev)); }, 2000); return () => clearInterval(interval); }, []); const subscribe = useCallback(async () => { if (!isSupported) return; const perm = await Notification.requestPermission(); setPermission(perm); if (perm !== "granted") return; const { publicKey } = await pushApi.getVapidPublicKey(); if (!publicKey) return; const reg = await navigator.serviceWorker.ready; const sub = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(publicKey).buffer as ArrayBuffer, }); await pushApi.subscribe(sub.toJSON() as ReturnType); }, []); const unsubscribe = useCallback(async () => { if (!isSupported) return; const reg = await navigator.serviceWorker.ready; const sub = await reg.pushManager.getSubscription(); if (!sub) return; await sub.unsubscribe(); await pushApi.unsubscribe(sub.endpoint); }, []); return { isSupported, permission, subscribe, unsubscribe }; }