"use client"; import { useEffect, useState } from 'react'; import { useTranslations } from 'next-intl'; import { useSettingsStore } from '@/stores/settings-store'; import { SettingsSection, SettingItem, ToggleSwitch, Select } from './settings-section'; import { playNotificationSound, NOTIFICATION_SOUNDS } from '@/lib/notification-sound'; import type { NotificationSoundChoice } from '@/lib/notification-sound'; import { Button } from '@/components/ui/button'; import { CheckCircle2, Volume2, XCircle } from 'lucide-react'; import { usePolicyStore } from '@/stores/policy-store'; import { useAuthStore } from '@/stores/auth-store'; import { ConfirmDialog } from '@/components/ui/confirm-dialog'; import { useConfirmDialog } from '@/hooks/use-confirm-dialog'; import { DEFAULT_RELAY_BASE_URL, WebPushUnsupportedError, disableWebPush, enableWebPush, isWebPushEnabled, isWebPushSupported, } from '@/lib/web-push'; type PushStatus = | { kind: 'idle' } | { kind: 'busy' } | { kind: 'enabled' } | { kind: 'unsupported' } | { kind: 'error'; message: string }; export function NotificationSettings() { const t = useTranslations('settings.notifications'); const { emailNotificationsEnabled, emailNotificationSound, notificationSoundChoice, calendarNotificationsEnabled, calendarNotificationSound, calendarInvitationParsingEnabled, updateSetting, } = useSettingsStore(); const { isSettingLocked, isSettingHidden } = usePolicyStore(); const client = useAuthStore((s) => s.client); const username = useAuthStore((s) => s.username); const { dialogProps: confirmDialogProps, confirm: confirmDialog } = useConfirmDialog(); const supported = typeof window !== 'undefined' && isWebPushSupported(); const [relayUrl, setRelayUrl] = useState(DEFAULT_RELAY_BASE_URL); const [pushStatus, setPushStatus] = useState( supported ? { kind: 'idle' } : { kind: 'unsupported' }, ); useEffect(() => { if (!supported) return; void (async () => { const enabled = await isWebPushEnabled(); if (enabled) setPushStatus({ kind: 'enabled' }); })(); }, [supported]); const trimmedRelay = relayUrl.trim().replace(/\/+$/, ''); const isValidRelay = /^https?:\/\/.+/i.test(trimmedRelay); const busy = pushStatus.kind === 'busy'; const handleEnablePush = async () => { if (!client) { setPushStatus({ kind: 'error', message: 'Sign in first' }); return; } if (!isValidRelay) { setPushStatus({ kind: 'error', message: 'Enter a valid https:// URL' }); return; } setPushStatus({ kind: 'busy' }); try { await enableWebPush({ client, relayBaseUrl: trimmedRelay, accountLabel: username ?? undefined, }); setPushStatus({ kind: 'enabled' }); } catch (err) { if (err instanceof WebPushUnsupportedError) { setPushStatus({ kind: 'unsupported' }); return; } setPushStatus({ kind: 'error', message: err instanceof Error ? err.message : 'Failed to enable push', }); } }; const handleDisablePush = async () => { if (!client) return; const confirmed = await confirmDialog({ title: t('push.confirm_disable_title'), message: t('push.confirm_disable_message'), confirmText: t('push.disable'), variant: 'destructive', }); if (!confirmed) return; setPushStatus({ kind: 'busy' }); try { await disableWebPush({ client, relayBaseUrl: trimmedRelay }); setPushStatus({ kind: 'idle' }); } catch (err) { setPushStatus({ kind: 'error', message: err instanceof Error ? err.message : 'Failed to disable push', }); } }; const soundOptions = NOTIFICATION_SOUNDS.map((s) => ({ value: s.id, label: t(`sounds.${s.id}`), })); return (

{t('push.relay_desc')}

setRelayUrl(e.target.value)} placeholder={t('push.relay_placeholder')} disabled={busy || pushStatus.kind === 'unsupported'} className="w-full rounded border bg-background px-3 py-2 text-sm disabled:opacity-50" />
{pushStatus.kind === 'enabled' && ( )}
{pushStatus.kind === 'unsupported' && (

{t('push.ios_hint')}

)}