Public Access
feat(appearance): accent color, corners, wide layout and game art background
This commit is contained in:
1 parent
91d5672421
commit
2c360c9421
28 files changed
+934
-46
No files matched your search
@@ -4,7 +4,7 @@ import {useTranslation} from 'react-i18next';
|
||||
import {useForm} from 'react-hook-form';
|
||||
import {zodResolver} from '@hookform/resolvers/zod';
|
||||
|
||||
import {Copy, Gamepad2, Loader2, Save, SlidersHorizontal, UserRound} from 'lucide-react';
|
||||
import {Copy, Gamepad2, ImageOff, Loader2, Palette, Save, SlidersHorizontal, UserRound} from 'lucide-react';
|
||||
import {toast} from 'sonner';
|
||||
|
||||
import {Button} from '@/components/ui/button';
|
||||
@@ -18,6 +18,10 @@ import FormField from '../components/FormField.jsx';
|
||||
import {settingsSchema} from '../utils/validation.js';
|
||||
import {usePreferences} from '../context/PreferencesContext.jsx';
|
||||
import useSessionExpired from '../hooks/useSessionExpired.js';
|
||||
import useSaveAppearance from '../hooks/useSaveAppearance.js';
|
||||
import {ACCENTS, CORNERS} from '../utils/appearance.js';
|
||||
import {coverUrl} from '../utils/games.js';
|
||||
import {cn} from '@/lib/utils';
|
||||
|
||||
const BACKEND_ERROR_KEY = {
|
||||
STEAM_ID_ALREADY_IN_USE: 'REGISTRATION.ERROR_STEAM_ID_IN_USE',
|
||||
@@ -39,7 +43,8 @@ export default function SettingsPage() {
|
||||
const {t, i18n} = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const expireSession = useSessionExpired();
|
||||
const {isDarkMode, setDarkMode, setLanguage} = usePreferences();
|
||||
const {isDarkMode, setDarkMode, setLanguage, appearance} = usePreferences();
|
||||
const saveAppearance = useSaveAppearance();
|
||||
|
||||
const [profile, setProfile] = useState(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
@@ -268,6 +273,68 @@ export default function SettingsPage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2"><Palette className="size-4 text-primary"/>{t('SETTINGS.APPEARANCE')}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<Label id="accent-label">{t('SETTINGS.ACCENT')}</Label>
|
||||
<div role="group" aria-labelledby="accent-label" className="flex flex-wrap gap-2">
|
||||
{ACCENTS.map((accent) => (
|
||||
<button key={accent} type="button" data-accent={accent}
|
||||
aria-label={t(`SETTINGS.ACCENT_OPTIONS.${accent.toUpperCase()}`)}
|
||||
title={t(`SETTINGS.ACCENT_OPTIONS.${accent.toUpperCase()}`)}
|
||||
aria-pressed={appearance.accent === accent}
|
||||
onClick={() => saveAppearance({accent})}
|
||||
className={cn('size-8 rounded-full bg-primary ring-offset-2 ring-offset-background transition hover:scale-110',
|
||||
appearance.accent === accent && 'ring-2 ring-foreground')}/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<Label id="corners-label">{t('SETTINGS.CORNERS')}</Label>
|
||||
<div role="group" aria-labelledby="corners-label" className="flex gap-2">
|
||||
{CORNERS.map((corners) => (
|
||||
<Button key={corners} type="button" size="sm" data-corners={corners}
|
||||
variant={appearance.corners === corners ? 'default' : 'outline'}
|
||||
aria-pressed={appearance.corners === corners}
|
||||
onClick={() => saveAppearance({corners})}>
|
||||
{t(`SETTINGS.CORNER_OPTIONS.${corners.toUpperCase()}`)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="wideLayout">{t('SETTINGS.WIDE_LAYOUT')}</Label>
|
||||
<p className="text-xs text-muted-foreground">{t('SETTINGS.WIDE_LAYOUT_HINT')}</p>
|
||||
</div>
|
||||
<Switch id="wideLayout" checked={appearance.wideLayout}
|
||||
onCheckedChange={(wideLayout) => saveAppearance({wideLayout})}/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div className="space-y-1">
|
||||
<Label>{t('SETTINGS.BACKGROUND')}</Label>
|
||||
{!appearance.backgroundAppid && (
|
||||
<p className="text-xs text-muted-foreground">{t('SETTINGS.BACKGROUND_HINT')}</p>
|
||||
)}
|
||||
</div>
|
||||
{appearance.backgroundAppid && (
|
||||
<div className="flex items-center gap-2">
|
||||
<img src={coverUrl(appearance.backgroundAppid)} alt=""
|
||||
onError={(event) => { event.currentTarget.hidden = true; }}
|
||||
className="h-10 w-20 rounded-md object-cover"/>
|
||||
<Button type="button" variant="outline" size="sm"
|
||||
onClick={() => saveAppearance({backgroundAppid: null})}>
|
||||
<ImageOff/>{t('SETTINGS.BACKGROUND_REMOVE')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2"><Gamepad2 className="size-4 text-primary"/>{t('SETTINGS.PROFILE')}</CardTitle>
|
||||
|
||||
Reference in new issue
Block a user