import { useEffect, useState } from 'react'; import { Bell, Loader2, ShieldCheck, TriangleAlert } from 'lucide-react'; import { Switch } from '@/components/ui/switch'; import { Button } from '@/components/ui/button'; import { useNotificationPrefs } from '@/hooks/useNotificationPrefs'; import { NOTIFICATION_CATEGORIES } from '@/lib/notificationPrefs'; import { getNotificationPermission, requestNotificationPermission, showOsNotification, type OsPermission, } from '@/lib/osNotify'; /** * Notification settings — the opt-in surface for OS notifications. * * Turns on nothing until the user asks, and explains the one honest limitation * up front: notifications are delivered by the device itself while the app is * running (e.g. a background browser tab). A fully closed app needs a push * server, which SkateSpots deliberately does not run. */ export function NotificationSettingsCard() { const [prefs, setPrefs] = useNotificationPrefs(); const [permission, setPermission] = useState('loading'); const [busy, setBusy] = useState(false); useEffect(() => { let cancelled = false; void getNotificationPermission().then((result) => { if (!cancelled) setPermission(result); }); return () => { cancelled = true; }; }, []); const enable = async () => { setBusy(true); try { const result = await requestNotificationPermission(); setPermission(result); if (result === 'granted') { setPrefs((prev) => ({ ...prev, enabled: true })); } } finally { setBusy(false); } }; const toggleEnabled = (next: boolean) => { if (!next) { setPrefs((prev) => ({ ...prev, enabled: false })); return; } if (permission === 'granted') { setPrefs((prev) => ({ ...prev, enabled: true })); return; } void enable(); }; const setCategory = (key: (typeof NOTIFICATION_CATEGORIES)[number]['key'], value: boolean) => { setPrefs((prev) => ({ ...prev, [key]: value })); }; const sendTest = () => { void showOsNotification({ id: `test:${Date.now()}`, title: 'SkateSpots notifications are on 🛹', body: 'This is how a DM, zap or crew invite will look.', path: '/', }); }; const unsupported = permission === 'unsupported'; const denied = permission === 'denied'; const granted = permission === 'granted'; return (
{/* Master switch */}

Enable notifications

Buzz this device when something needs you.

{permission === 'loading' && (

Checking permission…

)} {unsupported && (

This device doesn't support notifications here. Rebuild the Android app to enable them on your phone.

)} {denied && (

Notifications are blocked for this site. Allow them in your browser/OS settings, then try again.

)}
{/* Per-category toggles */}

Notify me about

{NOTIFICATION_CATEGORIES.map(({ key, label, hint }) => (

{label}

{hint}

setCategory(key, value)} />
))}
{/* Test + privacy note */}

Notifications are built on your device from data the app already has. Nothing extra is sent anywhere, and direct-message alerts never decrypt your messages — so Amber is never asked to open one. While the app is fully closed Android and iOS can't reach it, so those alerts arrive next time you open SkateSpots.

); }