Public Access
Broadcasts go to every active user, so the settings switch and its API call are gone. Admin broadcast texts no longer mention an opt-out.
90 lines
4.3 KiB
React
90 lines
4.3 KiB
React
import {useTranslation} from 'react-i18next';
|
|
import {SlidersHorizontal} from 'lucide-react';
|
|
import {toast} from 'sonner';
|
|
|
|
import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card';
|
|
import {Label} from '@/components/ui/label';
|
|
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/components/ui/select';
|
|
import {Switch} from '@/components/ui/switch';
|
|
import {useCurrentUserContext} from '../context/CurrentUserContext.jsx';
|
|
import {usePreferences} from '../context/PreferencesContext.jsx';
|
|
import usePersistSettings from '../hooks/usePersistSettings.js';
|
|
import useSaveResponse from '../hooks/useSaveResponse.js';
|
|
import {FAVORITE_PLATFORMS, saveFavoritePlatform} from '../utils/platforms.js';
|
|
import {LANGUAGES, saveSettings} from '../utils/settings.js';
|
|
|
|
// Radix Select has no empty value.
|
|
const NO_FAVORITE = 'NONE';
|
|
|
|
export default function PreferencesCard() {
|
|
const {t, i18n} = useTranslation();
|
|
const {user: profile, updateUser: updateProfile} = useCurrentUserContext();
|
|
const {isDarkMode, setDarkMode, setLanguage} = usePreferences();
|
|
const persistSettings = usePersistSettings();
|
|
const runSave = useSaveResponse();
|
|
|
|
const handleDarkThemeToggle = async () => {
|
|
const next = !isDarkMode;
|
|
setDarkMode(next); // apply on the fly
|
|
await persistSettings(() => saveSettings({useDarkTheme: next}));
|
|
};
|
|
|
|
const handleLanguageChange = async (next) => {
|
|
await persistSettings(() => setLanguage(next));
|
|
};
|
|
|
|
const handleFavoritePlatformChange = async (value) => {
|
|
const platform = value === NO_FAVORITE ? null : value;
|
|
if (await runSave(() => saveFavoritePlatform(platform))) {
|
|
updateProfile((p) => ({...p, settings: {...p.settings, favoritePlatform: platform}}));
|
|
toast.success(t('SETTINGS.SAVED'));
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2"><SlidersHorizontal className="size-4 text-primary"/>{t('SETTINGS.PREFERENCES')}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
<div className="flex items-center justify-between gap-4">
|
|
<Label htmlFor="useDarkTheme">{t('SETTINGS.DARK_THEME')}</Label>
|
|
<Switch id="useDarkTheme" checked={isDarkMode} onCheckedChange={handleDarkThemeToggle}/>
|
|
</div>
|
|
<div className="flex items-center justify-between gap-4">
|
|
<Label htmlFor="language">{t('SETTINGS.LANGUAGE')}</Label>
|
|
<Select value={i18n.language} onValueChange={handleLanguageChange}>
|
|
<SelectTrigger id="language" className="w-40">
|
|
<SelectValue/>
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{LANGUAGES.map((lang) => (
|
|
<SelectItem key={lang} value={lang}>
|
|
{t(`SETTINGS.LANGUAGE_OPTIONS.${lang.toUpperCase()}`)}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="flex items-center justify-between gap-4">
|
|
<div className="space-y-1">
|
|
<Label htmlFor="favoritePlatform">{t('SETTINGS.FAVORITE_PLATFORM')}</Label>
|
|
<p className="text-xs text-muted-foreground">{t('SETTINGS.FAVORITE_PLATFORM_HINT')}</p>
|
|
</div>
|
|
<Select value={profile?.settings?.favoritePlatform ?? NO_FAVORITE} onValueChange={handleFavoritePlatformChange}>
|
|
<SelectTrigger id="favoritePlatform" className="w-48">
|
|
<SelectValue/>
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value={NO_FAVORITE}>{t('SETTINGS.FAVORITE_PLATFORM_NONE')}</SelectItem>
|
|
{FAVORITE_PLATFORMS.map((platform) => (
|
|
<SelectItem key={platform.value} value={platform.value}>{platform.label}</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|