import React from 'react'; import { getAdminPushConfig } from '../../lib/runtime-config'; import { registerAdminPushSubscription, unregisterAdminPushSubscription } from '../../api'; import { getAdminDeviceId } from '../../lib/device'; type PushSubscriptionState = { supported: boolean; permission: NotificationPermission; subscribed: boolean; loading: boolean; error: string | null; enable: () => Promise; disable: () => Promise; refresh: () => Promise; }; export function useAdminPushSubscription(): PushSubscriptionState { const pushConfig = React.useMemo(() => getAdminPushConfig(), []); const supported = React.useMemo(() => { return typeof window !== 'undefined' && typeof navigator !== 'undefined' && typeof Notification !== 'undefined' && 'serviceWorker' in navigator && 'PushManager' in window && pushConfig.enabled; }, [pushConfig.enabled]); const [permission, setPermission] = React.useState(() => { if (typeof Notification === 'undefined') { return 'default'; } return Notification.permission; }); const [subscription, setSubscription] = React.useState(null); const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState(null); const refresh = React.useCallback(async () => { if (!supported) { return; } try { const registration = await navigator.serviceWorker.ready; const current = await registration.pushManager.getSubscription(); setSubscription(current); setPermission(Notification.permission); } catch (err) { console.warn('Unable to refresh admin push subscription', err); setSubscription(null); } }, [supported]); React.useEffect(() => { if (!supported) { return; } void refresh(); const handleMessage = (event: MessageEvent) => { if (event.data?.type === 'push-subscription-change') { void refresh(); } }; navigator.serviceWorker?.addEventListener('message', handleMessage); return () => { navigator.serviceWorker?.removeEventListener('message', handleMessage); }; }, [refresh, supported]); const enable = React.useCallback(async () => { if (!supported) { setError('Push-Benachrichtigungen werden auf diesem Gerät nicht unterstützt.'); return; } setLoading(true); setError(null); try { const permissionResult = await Notification.requestPermission(); setPermission(permissionResult); if (permissionResult !== 'granted') { throw new Error('Bitte erlaube Benachrichtigungen, um Push zu aktivieren.'); } const registration = await navigator.serviceWorker.ready; const existing = await registration.pushManager.getSubscription(); if (existing) { await registerAdminPushSubscription(existing, getAdminDeviceId()); setSubscription(existing); return; } if (!pushConfig.vapidPublicKey) { throw new Error('Push-Konfiguration ist nicht vollständig.'); } const newSubscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(pushConfig.vapidPublicKey).buffer as ArrayBuffer, }); await registerAdminPushSubscription(newSubscription, getAdminDeviceId()); setSubscription(newSubscription); } catch (err) { const message = err instanceof Error ? err.message : 'Push konnte nicht aktiviert werden.'; setError(message); console.error(err); await refresh(); } finally { setLoading(false); } }, [pushConfig.vapidPublicKey, refresh, supported]); const disable = React.useCallback(async () => { if (!supported || !subscription) { return; } setLoading(true); setError(null); try { await unregisterAdminPushSubscription(subscription.endpoint); await subscription.unsubscribe(); setSubscription(null); } catch (err) { const message = err instanceof Error ? err.message : 'Push konnte nicht deaktiviert werden.'; setError(message); console.error(err); } finally { setLoading(false); } }, [subscription, supported]); return { supported, permission, subscribed: Boolean(subscription), loading, error, enable, disable, refresh, }; } function urlBase64ToUint8Array(base64String: string): Uint8Array { const padding = '='.repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/'); const rawData = typeof window !== 'undefined' ? window.atob(base64) : Buffer.from(base64, 'base64').toString('binary'); const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; i += 1) { outputArray[i] = rawData.charCodeAt(i); } return outputArray; }