/** * Abonnement Web Push. Le front ne fait que : demander la permission, s'abonner (clé VAPID du back), * transmettre la subscription au back. Le back décide quand/quoi envoyer. */ function urlB64ToUint8Array(base64String: string): Uint8Array { const padding = '='.repeat((4 - (base64String.length % 4)) % 4) const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/') const raw = atob(base64) const arr = new Uint8Array(raw.length) for (let i = 0; i < raw.length; i++) arr[i] = raw.charCodeAt(i) return arr } export function usePush() { const { api } = useApi() function isSupported(): boolean { return import.meta.client && 'serviceWorker' in navigator && 'PushManager' in window && 'Notification' in window } async function enable(vapidPublicKey: string) { if (!isSupported()) throw new Error('Notifications non supportées sur cet appareil.') const perm = await Notification.requestPermission() if (perm !== 'granted') throw new Error('Permission refusée.') const reg = await navigator.serviceWorker.ready const existing = await reg.pushManager.getSubscription() const sub = existing ?? await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlB64ToUint8Array(vapidPublicKey) }) const json = sub.toJSON() as { endpoint?: string, keys?: { p256dh: string, auth: string } } await api('/api/push/subscribe', { method: 'POST', body: { endpoint: json.endpoint, keys: json.keys } }) } async function disable() { if (!isSupported()) return const reg = await navigator.serviceWorker.ready const sub = await reg.pushManager.getSubscription() if (sub) { await api('/api/push/unsubscribe', { method: 'POST', body: { endpoint: sub.endpoint } }).catch(() => {}) await sub.unsubscribe() } } async function sendTest() { await api('/api/push/test', { method: 'POST' }) } return { isSupported, enable, disable, sendTest } }