import { useState, useEffect } from 'react'; import { Authentication } from '../../../scripts/pocketbase/Authentication'; import { Update } from '../../../scripts/pocketbase/Update'; import { Collections } from '../../../schemas/pocketbase/schema'; import { toast } from 'react-hot-toast'; // Default display preferences const DEFAULT_DISPLAY_PREFERENCES = { theme: 'dark', fontSize: 'medium' }; // Default accessibility settings const DEFAULT_ACCESSIBILITY_SETTINGS = { colorBlindMode: false, reducedMotion: false }; export default function DisplaySettings() { const auth = Authentication.getInstance(); const update = Update.getInstance(); const [theme, setTheme] = useState(DEFAULT_DISPLAY_PREFERENCES.theme); const [fontSize, setFontSize] = useState(DEFAULT_DISPLAY_PREFERENCES.fontSize); const [colorBlindMode, setColorBlindMode] = useState(DEFAULT_ACCESSIBILITY_SETTINGS.colorBlindMode); const [reducedMotion, setReducedMotion] = useState(DEFAULT_ACCESSIBILITY_SETTINGS.reducedMotion); const [saving, setSaving] = useState(false); // Load saved preferences on component mount useEffect(() => { const loadPreferences = async () => { try { // First check localStorage for immediate UI updates const savedTheme = localStorage.getItem('theme') || DEFAULT_DISPLAY_PREFERENCES.theme; // Ensure theme is either light or dark const validTheme = ['light', 'dark'].includes(savedTheme) ? savedTheme : DEFAULT_DISPLAY_PREFERENCES.theme; const savedFontSize = localStorage.getItem('fontSize') || DEFAULT_DISPLAY_PREFERENCES.fontSize; const savedColorBlindMode = localStorage.getItem('colorBlindMode') === 'true'; const savedReducedMotion = localStorage.getItem('reducedMotion') === 'true'; setTheme(validTheme); setFontSize(savedFontSize); setColorBlindMode(savedColorBlindMode); setReducedMotion(savedReducedMotion); // Apply theme to document document.documentElement.setAttribute('data-theme', validTheme); // Apply font size applyFontSize(savedFontSize); // Apply accessibility settings if (savedColorBlindMode) { document.documentElement.classList.add('color-blind-mode'); } if (savedReducedMotion) { document.documentElement.classList.add('reduced-motion'); } // Then check if user has saved preferences in their profile const user = auth.getCurrentUser(); if (user) { let needsDisplayPrefsUpdate = false; let needsAccessibilityUpdate = false; // Check and handle display preferences if (user.display_preferences && typeof user.display_preferences === 'string' && user.display_preferences.trim() !== '') { try { const userPrefs = JSON.parse(user.display_preferences); // Only update if values exist and are different from localStorage if (userPrefs.theme && ['light', 'dark'].includes(userPrefs.theme) && userPrefs.theme !== validTheme) { setTheme(userPrefs.theme); localStorage.setItem('theme', userPrefs.theme); document.documentElement.setAttribute('data-theme', userPrefs.theme); } else if (!['light', 'dark'].includes(userPrefs.theme)) { // If theme is not valid, mark for update needsDisplayPrefsUpdate = true; } if (userPrefs.fontSize && userPrefs.fontSize !== savedFontSize) { setFontSize(userPrefs.fontSize); localStorage.setItem('fontSize', userPrefs.fontSize); applyFontSize(userPrefs.fontSize); } } catch (e) { console.error('Error parsing display preferences:', e); needsDisplayPrefsUpdate = true; } } else { needsDisplayPrefsUpdate = true; } // Check and handle accessibility settings if (user.accessibility_settings && typeof user.accessibility_settings === 'string' && user.accessibility_settings.trim() !== '') { try { const accessibilityPrefs = JSON.parse(user.accessibility_settings); if (typeof accessibilityPrefs.colorBlindMode === 'boolean' && accessibilityPrefs.colorBlindMode !== savedColorBlindMode) { setColorBlindMode(accessibilityPrefs.colorBlindMode); localStorage.setItem('colorBlindMode', accessibilityPrefs.colorBlindMode.toString()); if (accessibilityPrefs.colorBlindMode) { document.documentElement.classList.add('color-blind-mode'); } else { document.documentElement.classList.remove('color-blind-mode'); } } if (typeof accessibilityPrefs.reducedMotion === 'boolean' && accessibilityPrefs.reducedMotion !== savedReducedMotion) { setReducedMotion(accessibilityPrefs.reducedMotion); localStorage.setItem('reducedMotion', accessibilityPrefs.reducedMotion.toString()); if (accessibilityPrefs.reducedMotion) { document.documentElement.classList.add('reduced-motion'); } else { document.documentElement.classList.remove('reduced-motion'); } } } catch (e) { console.error('Error parsing accessibility settings:', e); needsAccessibilityUpdate = true; } } else { needsAccessibilityUpdate = true; } // Initialize default settings if needed if (needsDisplayPrefsUpdate || needsAccessibilityUpdate) { await initializeDefaultSettings(user.id, needsDisplayPrefsUpdate, needsAccessibilityUpdate); } } } catch (error) { console.error('Error loading preferences:', error); toast.error('Failed to load display preferences'); } }; loadPreferences(); }, []); // Initialize default settings if not set const initializeDefaultSettings = async (userId: string, updateDisplayPrefs: boolean, updateAccessibility: boolean) => { try { const updateData: any = {}; if (updateDisplayPrefs) { updateData.display_preferences = JSON.stringify({ theme, fontSize }); } if (updateAccessibility) { updateData.accessibility_settings = JSON.stringify({ colorBlindMode, reducedMotion }); } if (Object.keys(updateData).length > 0) { await update.updateFields(Collections.USERS, userId, updateData); console.log('Initialized default display and accessibility settings'); } } catch (error) { console.error('Error initializing default settings:', error); } }; // Apply font size to document const applyFontSize = (size: string) => { const htmlElement = document.documentElement; // Remove existing font size classes htmlElement.classList.remove('text-sm', 'text-base', 'text-lg', 'text-xl'); // Add new font size class switch (size) { case 'small': htmlElement.classList.add('text-sm'); break; case 'medium': htmlElement.classList.add('text-base'); break; case 'large': htmlElement.classList.add('text-lg'); break; case 'extra-large': htmlElement.classList.add('text-xl'); break; } }; // Handle theme change const handleThemeChange = (e: React.ChangeEvent) => { const newTheme = e.target.value; setTheme(newTheme); // Apply theme to document document.documentElement.setAttribute('data-theme', newTheme); // Save to localStorage localStorage.setItem('theme', newTheme); }; // Handle font size change const handleFontSizeChange = (e: React.ChangeEvent) => { const newSize = e.target.value; setFontSize(newSize); // Apply font size applyFontSize(newSize); // Save to localStorage localStorage.setItem('fontSize', newSize); }; // Handle color blind mode toggle const handleColorBlindModeChange = (e: React.ChangeEvent) => { const enabled = e.target.checked; setColorBlindMode(enabled); // Apply to document if (enabled) { document.documentElement.classList.add('color-blind-mode'); } else { document.documentElement.classList.remove('color-blind-mode'); } // Save to localStorage localStorage.setItem('colorBlindMode', enabled.toString()); }; // Handle reduced motion toggle const handleReducedMotionChange = (e: React.ChangeEvent) => { const enabled = e.target.checked; setReducedMotion(enabled); // Apply to document if (enabled) { document.documentElement.classList.add('reduced-motion'); } else { document.documentElement.classList.remove('reduced-motion'); } // Save to localStorage localStorage.setItem('reducedMotion', enabled.toString()); }; // Handle form submission const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setSaving(true); try { const user = auth.getCurrentUser(); if (!user) throw new Error('User not authenticated'); // Save display preferences to user record const displayPreferences = { theme, fontSize }; // Save accessibility settings to user record const accessibilitySettings = { colorBlindMode, reducedMotion }; // Update user record await update.updateFields( Collections.USERS, user.id, { display_preferences: JSON.stringify(displayPreferences), accessibility_settings: JSON.stringify(accessibilitySettings) } ); // Show success message toast.success('Display settings saved successfully!'); } catch (error) { console.error('Error saving display settings:', error); toast.error('Failed to save display settings to your profile'); } finally { setSaving(false); } }; return (
{/* Theme Settings */}

Theme

{/* Font Size Settings */}

Font Size

{/* Accessibility Settings */}

Accessibility

These settings are saved to your browser and your IEEE UCSD account. They will be applied whenever you log in.

); }