'use client'; import { useEffect, useState } from 'react'; import api from '@/lib/api'; import { VolumeIcon, VolumeOffIcon, SparklesIcon } from '@/components/ui/Icons'; import { playStepComplete } from '@/lib/sounds'; import { hapticTap } from '@/lib/haptics'; import PushNotificationToggle from '@/components/notifications/PushNotificationToggle'; interface Preferences { sound_enabled: boolean; haptic_enabled: boolean; show_launch_screen: boolean; celebration_style: string; } interface NotifSettings { discord_user_id: string; discord_enabled: boolean; ntfy_topic: string; ntfy_enabled: boolean; } export default function SettingsPage() { const [prefs, setPrefs] = useState({ sound_enabled: false, haptic_enabled: true, show_launch_screen: true, celebration_style: 'standard', }); const [notif, setNotif] = useState({ discord_user_id: '', discord_enabled: false, ntfy_topic: '', ntfy_enabled: false, }); const [isLoading, setIsLoading] = useState(true); const [saved, setSaved] = useState(false); useEffect(() => { Promise.all([ api.preferences.get().then((data: Preferences) => setPrefs(data)), api.notifications.getSettings().then((data) => setNotif({ discord_user_id: data.discord_user_id, discord_enabled: data.discord_enabled, ntfy_topic: data.ntfy_topic, ntfy_enabled: data.ntfy_enabled, })), ]) .catch(() => {}) .finally(() => setIsLoading(false)); }, []); const flashSaved = () => { setSaved(true); setTimeout(() => setSaved(false), 1500); }; const updatePref = async (key: keyof Preferences, value: boolean | string) => { const updated = { ...prefs, [key]: value }; setPrefs(updated); try { await api.preferences.update({ [key]: value }); flashSaved(); } catch { setPrefs(prefs); } }; const updateNotif = async (updates: Partial) => { const prev = { ...notif }; const updated = { ...notif, ...updates }; setNotif(updated); try { await api.notifications.updateSettings(updates); flashSaved(); } catch { setNotif(prev); } }; if (isLoading) { return (
); } return (

Settings

{saved && ( Saved )}
{/* Session Experience */}

Session Experience

{/* Sound */}
{prefs.sound_enabled ? ( ) : ( )}

Sound effects

Subtle audio cues on step completion

{/* Haptics */}

Haptic feedback

Gentle vibration on actions

{/* Launch Screen */}

Pre-routine launch screen

Environment check and emotion bridge

{/* Notifications */}

Notifications

{/* Push Notifications */} {/* ntfy */}

ntfy

Push notifications via ntfy.sh

{notif.ntfy_enabled && ( setNotif({ ...notif, ntfy_topic: e.target.value })} onBlur={() => updateNotif({ ntfy_topic: notif.ntfy_topic })} className="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent text-gray-900 placeholder-gray-400" /> )}
{/* Discord */}

Discord

Get DMs from the Synculous bot

{notif.discord_enabled && ( setNotif({ ...notif, discord_user_id: e.target.value })} onBlur={() => updateNotif({ discord_user_id: notif.discord_user_id })} className="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent text-gray-900 placeholder-gray-400" /> )}
{/* Celebration Style */}

Celebration Style

{[ { value: 'standard', label: 'Standard', desc: 'Full animated celebration with stats and rewards' }, { value: 'quick', label: 'Quick', desc: 'Brief confirmation, then back to dashboard' }, { value: 'none', label: 'None', desc: 'No celebration screen, return immediately' }, ].map(option => ( ))}
); }