feat(library): show set total playtime on dashboard cards

Most played and currently playing cards show the total playtime of
the game set. Use the own minutes when the API sends no total.
This commit is contained in:
lobo committed 2026-10-09 14:35:07 +02:00
1 parent f1a6bc0443
commit 9adc606c57
5 files changed
+44 -6

No files matched your search

+1 -1
View File
@@ -40,7 +40,7 @@ export default function CurrentlyPlayingCard({games, syncing, wide}) {
<FitText text={g.name ?? `App ${g.appid}`}/>
</div>
<div className="text-xs text-muted-foreground tabular-nums">
{formatPlaytime(g.playtimeMinutes)}
{formatPlaytime(g.totalPlaytimeMinutes ?? g.playtimeMinutes)}
</div>
</Link>
</li>
@@ -0,0 +1,18 @@
// @vitest-environment jsdom
import {afterEach, describe, expect, it, vi} from 'vitest';
import {cleanup, render, screen} from '@testing-library/react';
import {MemoryRouter} from 'react-router-dom';
import CurrentlyPlayingCard from './CurrentlyPlayingCard.jsx';
vi.mock('react-i18next', () => import('../test/i18n-mock.js'));
describe('CurrentlyPlayingCard', () => {
afterEach(cleanup);
it('shows the set total of a game, not the minutes of its own row', () => {
render(<MemoryRouter><CurrentlyPlayingCard games={[{appid: 620, name: 'Portal 2', playtimeMinutes: 100,
totalPlaytimeMinutes: 600, status: 'PLAYING'}]}/></MemoryRouter>);
expect(screen.getByText('10h')).toBeTruthy();
});
});
+6 -4
View File
@@ -12,15 +12,17 @@ import {formatPlaytime} from '../utils/games.js';
import {coverEffect} from '../utils/rewards.js';
import {playChart} from '../utils/statCharts.js';
const total = (g) => g.totalPlaytimeMinutes ?? g.playtimeMinutes;
/**
* @param chart LIST, BARS, PIE or DONUT
* @param onChart `(form)`: shows the picker
*/
export default function MostPlayedCard({games, syncing, equipped, chart = 'LIST', onChart}) {
const {t} = useTranslation();
const mapped = games.map((g) => ({appid: g.appid, name: g.name, minutes: g.playtimeMinutes}));
const mapped = games.map((g) => ({appid: g.appid, name: g.name, minutes: total(g)}));
const form = playChart(chart, mapped);
const maxMinutes = games[0]?.playtimeMinutes || 1;
const maxMinutes = mapped[0]?.minutes || 1;
return (
<Card className="gap-0 py-0">
<div className="flex flex-wrap items-center justify-between gap-x-3 px-5 py-4 border-b">
@@ -62,12 +64,12 @@ export default function MostPlayedCard({games, syncing, equipped, chart = 'LIST'
<div className="mt-2 h-1.5 rounded-full bg-muted">
<div
className="h-full rounded-full bg-primary"
style={{width: `${Math.max(2, (g.playtimeMinutes / maxMinutes) * 100)}%`}}
style={{width: `${Math.max(2, (total(g) / maxMinutes) * 100)}%`}}
/>
</div>
</div>
<span className="w-12 sm:w-14 text-right text-sm font-medium text-muted-foreground tabular-nums">
{formatPlaytime(g.playtimeMinutes)}
{formatPlaytime(total(g))}
</span>
</Link>
</li>
+18
View File
@@ -0,0 +1,18 @@
// @vitest-environment jsdom
import {afterEach, describe, expect, it, vi} from 'vitest';
import {cleanup, render, screen} from '@testing-library/react';
import {MemoryRouter} from 'react-router-dom';
import MostPlayedCard from './MostPlayedCard.jsx';
vi.mock('react-i18next', () => import('../test/i18n-mock.js'));
describe('MostPlayedCard', () => {
afterEach(cleanup);
it('shows the set total of a game, not the minutes of its own row', () => {
render(<MemoryRouter><MostPlayedCard games={[{appid: 620, name: 'Portal 2', playtimeMinutes: 100,
totalPlaytimeMinutes: 600, status: 'PLAYING'}]}/></MemoryRouter>);
expect(screen.getByText('10h')).toBeTruthy();
});
});