Public Access
Hero and library card show the set total. Fall back to the row's own minutes when the API sends no total.
62 lines
3.0 KiB
React
62 lines
3.0 KiB
React
import {Link} from 'react-router-dom';
|
|
import {useTranslation} from 'react-i18next';
|
|
import {Trophy} from 'lucide-react';
|
|
|
|
import {Card} from '@/components/ui/card';
|
|
import {cn} from '@/lib/utils';
|
|
import PlatformIcons from './PlatformIcons.jsx';
|
|
import {formatPlaytime} from '../utils/games.js';
|
|
import {formatDate} from '../utils/format.js';
|
|
import {platformLabel} from '../utils/platforms.js';
|
|
|
|
/**
|
|
* One row per platform of the game: playtime, last played and the progress of that platform. A row of another edition
|
|
* links to that edition, whose page lists its achievements.
|
|
*/
|
|
export default function PlatformsCard({game}) {
|
|
const {t, i18n} = useTranslation();
|
|
const lines = game.platforms;
|
|
if (!lines?.length) return null;
|
|
|
|
return (
|
|
<Card className="gap-3 p-4">
|
|
<h2 className="text-sm font-medium">{t('PLATFORMS.TITLE')}</h2>
|
|
<ul className="divide-y text-sm">
|
|
{lines.map((line, index) => {
|
|
const label = platformLabel(line) ?? t(`LIBRARY.SOURCE.${line.source}`);
|
|
return (
|
|
<li key={`${line.appid}:${line.source}:${index}`}
|
|
className="flex flex-wrap items-center gap-x-4 gap-y-1 py-2 first:pt-0 last:pb-0">
|
|
<span className="flex min-w-24 items-center gap-2 font-medium">
|
|
<PlatformIcons platforms={[line]}/>
|
|
{line.appid === game.appid
|
|
? <span>{label}</span>
|
|
: <Link to={`/library/${line.appid}`} className="hover:text-primary hover:underline">{label}</Link>}
|
|
</span>
|
|
<span className="tabular-nums">{formatPlaytime(line.playtimeMinutes)}</span>
|
|
<span className="text-muted-foreground">
|
|
{t('PLATFORMS.LAST_PLAYED')}{' '}
|
|
{line.lastPlayedAt
|
|
? formatDate(line.lastPlayedAt, i18n.language, {dateStyle: 'medium'})
|
|
: t('PLATFORMS.NEVER')}
|
|
</span>
|
|
{line.achievementsTotal != null && (
|
|
<span className="inline-flex items-center gap-1 tabular-nums">
|
|
<Trophy className={cn('size-3', line.perfect && 'text-amber-500')}/>
|
|
{line.achievementsUnlocked ?? '?'} / {line.achievementsTotal}
|
|
</span>
|
|
)}
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
{lines.length > 1 && (
|
|
<p className="flex items-center justify-between border-t pt-2 text-sm font-medium">
|
|
<span>{t('PLATFORMS.TOTAL')}</span>
|
|
<span className="tabular-nums">{formatPlaytime(game.totalPlaytimeMinutes)}</span>
|
|
</p>
|
|
)}
|
|
</Card>
|
|
);
|
|
}
|