feat(appearance): accent color, corners, wide layout and game art background

This commit is contained in:
lobo committed 2026-09-24 15:06:16 +02:00
1 parent 91d5672421
commit 2c360c9421
28 files changed
+934 -46

No files matched your search

+26 -8
View File
@@ -1,7 +1,7 @@
import {useEffect, useMemo, useState} from 'react';
import {Link, useNavigate, useParams} from 'react-router-dom';
import {useTranslation} from 'react-i18next';
import {ArrowLeft, Clock, ExternalLink, ShieldOff, Trophy} from 'lucide-react';
import {ArrowLeft, Clock, ExternalLink, Image as ImageIcon, ImageOff, ShieldOff, Trophy} from 'lucide-react';
import {Badge} from '@/components/ui/badge';
import {Button} from '@/components/ui/button';
@@ -15,6 +15,9 @@ import PlayingHint from '../components/PlayingHint.jsx';
import TimeToBeat from '../components/TimeToBeat.jsx';
import {formatPlaytime} from '../utils/games.js';
import useSessionExpired from '../hooks/useSessionExpired.js';
import useSaveAppearance from '../hooks/useSaveAppearance.js';
import {usePreferences} from '../context/PreferencesContext.jsx';
import PageContainer from '../components/PageContainer.jsx';
const formatUnlockedAt = (instant, locale) => {
if (!instant) return '';
@@ -28,6 +31,8 @@ const formatUnlockedAt = (instant, locale) => {
export default function GameDetail() {
const {t, i18n} = useTranslation();
const {appid} = useParams();
const {appearance} = usePreferences();
const saveAppearance = useSaveAppearance();
const navigate = useNavigate();
const expireSession = useSessionExpired();
@@ -93,6 +98,8 @@ export default function GameDetail() {
});
}, [data]);
const isBackground = !!data && appearance.backgroundAppid === data.appid;
const stats = data ? [
[t('GAME_DETAIL.UNLOCKED'), data.achievementsUnlocked],
[t('GAME_DETAIL.REMAINING'), data.achievementsRemaining],
@@ -100,7 +107,7 @@ export default function GameDetail() {
] : [];
return (
<div className="w-full max-w-5xl mx-auto p-4 md:p-8 space-y-6">
<PageContainer className="p-4 md:p-8 space-y-6">
<Button variant="ghost" size="sm" className="-ml-2" onClick={() => navigate(-1)}>
<ArrowLeft/>{t('GAME_DETAIL.BACK')}
</Button>
@@ -139,11 +146,22 @@ export default function GameDetail() {
<TimeToBeat time={data.timeToBeat}/>
</div>
</div>
<Button asChild variant="outline" size="sm" className="self-start">
<a href={`https://store.steampowered.com/app/${data.appid}`} target="_blank" rel="noopener noreferrer">
{t('GAME_DETAIL.OPEN_IN_STEAM')}<ExternalLink/>
</a>
</Button>
<div className="flex flex-wrap gap-2">
<Button asChild variant="outline" size="sm">
<a href={`https://store.steampowered.com/app/${data.appid}`} target="_blank" rel="noopener noreferrer">
{t('GAME_DETAIL.OPEN_IN_STEAM')}<ExternalLink/>
</a>
</Button>
{isBackground ? (
<Button variant="outline" size="sm" onClick={() => saveAppearance({backgroundAppid: null})}>
<ImageOff/>{t('GAME_DETAIL.REMOVE_BACKGROUND')}
</Button>
) : (
<Button variant="outline" size="sm" onClick={() => saveAppearance({backgroundAppid: data.appid})}>
<ImageIcon/>{t('GAME_DETAIL.SET_BACKGROUND')}
</Button>
)}
</div>
</div>
</Card>
@@ -227,6 +245,6 @@ export default function GameDetail() {
)}
</>
)}
</div>
</PageContainer>
);
}