"use client"; import { useState, useCallback, useEffect, useRef } from 'react'; import { useLocalStorage } from '@/hooks/use-local-storage'; import { useToast } from '@/hooks/use-toast'; import { usePresence } from '@/hooks/use-presence'; import type { User, DataMode, FontStyle, Language, PrivacySettings } from '@/lib/types'; import { formatDistanceToNow as formatDistanceToNowFn } from 'date-fns'; import { ar as arLocale, enUS as enLocale } from 'date-fns/locale'; import enTranslations from '@/lib/locales/en.json'; import arTranslations from '@/lib/locales/ar.json'; import { useFirebase } from '@/contexts/firebase-context'; import { getMessaging, getToken } from "firebase/messaging"; import { doc, updateDoc } from 'firebase/firestore'; const SYNC_INTERVAL = 30; const defaultPrivacySettings: PrivacySettings = { showOnline: true, showTyping: true, readReceipts: true, whoCanAdd: 'everyone', whoCanViewProfile: 'everyone', }; const translations = { en: enTranslations, ar: arTranslations }; const locales = { ar: arLocale, en: enLocale }; interface UseSettingsProps { currentUser: User | null; } export const useSettingsCore = ({ currentUser }: UseSettingsProps) => { const { app, db, rtdb } = useFirebase(); const { toast } = useToast(); const soundsRef = useRef | null>(null); const [fontStyle, setFontStyle] = useLocalStorage('fontStyle', 'default'); const [language, setLanguage] = useLocalStorage('appLanguage', 'en'); const [isLanguageLoading, setIsLanguageLoading] = useState(true); const [isSoundEnabled, setIsSoundEnabled] = useLocalStorage('soundEnabled', false); const [dataMode, setDataMode] = useLocalStorage('dataMode', 'normal'); const [privacySettings, setPrivacySettings] = useLocalStorage('privacySettings', defaultPrivacySettings); const [accentColor, setAccentColor] = useLocalStorage('accentColor', '221.2 83.2% 53.3%'); const [syncCountdown, setSyncCountdown] = useState(SYNC_INTERVAL); const [notificationPermission, setNotificationPermission] = useState('default'); const [fcmToken, setFcmToken] = useState(null); usePresence({ currentUser, rtdb, privacySettings }); const addToast = useCallback((message: string, options?: { variant?: 'default' | 'destructive' }) => { toast({ title: message, variant: options?.variant }); }, [toast]); const playSound = useCallback((sound: 'send' | 'receive' | 'touch' | 'typing') => { if (!isSoundEnabled || !soundsRef.current) return; const s = soundsRef.current[sound]; if (s) { s.currentTime = 0; s.play().catch(e => console.error("Sound play failed:", e)); } }, [isSoundEnabled]); const stopSound = useCallback((sound: 'typing') => { if (!soundsRef.current) return; const s = soundsRef.current[sound]; if (s) { s.pause(); s.currentTime = 0; } }, []); const toggleSound = () => setIsSoundEnabled(prev => !prev); useEffect(() => { if (typeof window !== 'undefined' && 'Notification' in window) { setNotificationPermission(Notification.permission); } soundsRef.current = { send: new Audio('/sounds/send.mp3'), receive: new Audio('/sounds/receive.mp3'), touch: new Audio('/sounds/touch.mp3'), typing: new Audio('/sounds/typing.mp3'), }; Object.values(soundsRef.current).forEach(sound => { if(sound) { sound.preload = 'auto'; sound.volume = 0.7; } }); if(soundsRef.current.typing) { soundsRef.current.typing.loop = true; soundsRef.current.typing.volume = 0.3; } const storedLang = localStorage.getItem('appLanguage'); const lang = storedLang && (storedLang === 'en' || storedLang === 'ar') ? storedLang : (typeof navigator !== 'undefined' && navigator.language.split('-')[0] === 'ar' ? 'ar' : 'en'); setLanguage(lang); setIsLanguageLoading(false); }, [setLanguage]); const requestNotificationPermission = useCallback(async () => { if (typeof window === 'undefined' || !('Notification' in window) || !currentUser) { addToast("This browser does not support desktop notification or user is not logged in.", { variant: 'destructive' }); return; } try { const permission = await Notification.requestPermission(); setNotificationPermission(permission); if (permission === 'granted') { const messaging = getMessaging(app); // Get registration token. Initially this makes a network call, once retrieved // subsequent calls to getToken will return from cache. const currentToken = await getToken(messaging, { vapidKey: "BD6ckIt46u2iNjS-1MZ09WoWg2HP5WkZelZr8EK4cTtoU4i_JbEAJq4EsprfpaMs8JBRtCZiph3lFGSW7TsmjVM" }); if (currentToken) { setFcmToken(currentToken); await updateDoc(doc(db, 'users', currentUser.uid), { fcmToken: currentToken }); addToast("Notifications enabled and token registered!", { variant: 'default' }); } else { addToast("Could not get notification token.", { variant: 'destructive' }); } } else if (permission === 'denied') { addToast("Notifications blocked. You can enable them from browser settings.", { variant: 'destructive' }); } } catch (error) { console.error("Error during notification permission request:", error); addToast("Failed to request notification permission.", { variant: 'destructive' }); } }, [addToast, app, currentUser, db]); const t = useCallback((key: string, options?: Record) => { const translationsTyped = translations as Record>; let text = translationsTyped[language]?.[key] || translations['en']?.[key] || key; if (options) { Object.keys(options).forEach(k => { text = text.replace(new RegExp(`{{${k}}}`, 'g'), String(options[k])); }); } return text; }, [language]); const formatDistanceToNow = useCallback((date: number | Date, options?: { addSuffix?: boolean }) => { return formatDistanceToNowFn(date, { ...options, locale: locales[language] }); }, [language]); const updatePrivacySettings = useCallback(async (newSettings: Partial) => { setPrivacySettings(prev => ({ ...prev, ...newSettings })); }, [setPrivacySettings]); useEffect(() => { if (!isLanguageLoading) { document.documentElement.lang = language; document.documentElement.dir = language === 'ar' ? 'rtl' : 'ltr'; document.documentElement.style.setProperty('--primary', accentColor); document.documentElement.style.setProperty('--ring', accentColor); // Safer body class manipulation document.body.classList.remove('font-body', 'font-casual-body'); document.body.classList.add(fontStyle === 'casual' ? 'font-casual-body' : 'font-body'); } }, [language, fontStyle, isLanguageLoading, accentColor]); useEffect(() => { if (dataMode !== 'ultra') return; const timer = setInterval(() => { setSyncCountdown(prev => (prev <= 1 ? SYNC_INTERVAL : prev - 1)); }, 1000); return () => clearInterval(timer); }, [dataMode]); return { dataMode, setDataMode, isSoundEnabled, toggleSound, privacySettings, updatePrivacySettings, fontStyle, setFontStyle, language, setLanguage, accentColor, setAccentColor, isLanguageLoading, syncCountdown, notificationPermission, requestNotificationPermission, fcmToken, setFcmToken, playSound, stopSound, t, formatDistanceToNow, addToast }; }