diff --git a/README.md b/README.md index 275dce3..48b4d6d 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ To Be Played is split into three repositories: - **Your stats** (`/stats`): totals and statuses; the pile in hours, in weeks at your pace of the last 90 days and in years of full-time work, and its oldest unplayed game; most played and most loyal games; top genres and moods; trips to the moon, the longest gaming day, games started but under one hour; finished games per year; games per platform. +- **Click a number** (stats page, dashboard, public profile, landing page, Pile Points): a number, bar, row or year + opens the games behind it in a dialog (a bottom sheet on phones), 40 per page with "Load more". In your own + views the status counts and the pile link to the library with the filter instead. Not clickable: sums (playtime), + averages, rates and the moon trips. The list always uses the same rules as the number. - **Pile Points** (`/points`): points for playing games off your pile (start, finish by time to beat, 100 %, month and streak bonuses; Core checks proof of play), spent in a small shop on an avatar border, a badge, a premium accent, a banner frame, an effect for 100 % games and an animated page or banner background (CSS and SVG in `effects.css`, two canvas engines in `EffectCanvas`; layers follow the mouse and the phone tilt, on iPhone after the switch in the settings), @@ -208,6 +212,19 @@ flowchart LR with `src/test/fetchRoutes.js`, which fails a test on any request without a route. - `src/utils/brokenImages.js` reports pictures that fail to load (`GameCover`, achievement icons on the game detail page) to Core. +- The games behind a number: pages wrap their content in `StatDrillProvider` (`source` own, profile, community or + points), which renders one `StatGamesDialog`. Numbers call `useStatDrill()` / `StatCount`; outside a provider they + are plain text. `src/utils/statGames.js` builds the four endpoint URLs. + +```mermaid +flowchart LR + Number["Number, bar or row"] --> Provider["StatDrillProvider"] + Provider --> Dialog["StatGamesDialog"] + Dialog -->|own| Own["Core /api/library/stats/games"] + Dialog -->|"profile + ticket"| Profile["Core /api/public/profiles/library/stats/games"] + Dialog -->|community| Community["Core /api/stats/public/games"] + Dialog -->|points| Points["Core /api/rewards/.../games"] +``` - `useLibrarySync` polls Core's `/api/library/sync-status` while a Steam sync runs, then loads the data again. - The bookmarklet hands its list over in the URL fragment (base64url JSON), which the browser sends to no server. The import page reads it once and removes it from the URL. `RequireSession` keeps the fragment on the way diff --git a/public/i18n/de.json b/public/i18n/de.json index c90c647..8880dee 100644 --- a/public/i18n/de.json +++ b/public/i18n/de.json @@ -496,7 +496,9 @@ }, "TREND": { "TITLE": "Stapel-Verlauf", - "THIS_MONTH": "Diesen Monat: +{{added}} dazu, −{{done}} vom Stapel", + "THIS_MONTH": "Diesen Monat:", + "THIS_MONTH_ADDED": "+{{added}} neu", + "THIS_MONTH_DONE": "−{{done}} vom Stapel", "STARTS": "Der Verlauf beginnt heute. Morgen gibt es die erste Linie." }, "TOP_GAMES": { @@ -557,6 +559,25 @@ "TACTICAL": "Taktik", "TURN_BASED_STRATEGY_TBS": "Rundenstrategie", "VISUAL_NOVEL": "Visual Novel" + }, + "DRILL": { + "SHOW_ALL": "Alle anzeigen", + "LOAD_MORE": "Mehr laden", + "EMPTY": "Hinter dieser Zahl stehen keine Spiele.", + "ERROR": "Die Spiele konnten nicht geladen werden.", + "NOT_SHARED": "Der Besitzer teilt diese Zahl nicht.", + "EXPIRED": "Diese Ansicht ist abgelaufen. Lade die Seite neu.", + "RELOAD": "Neu laden", + "IN_LIBRARIES_one": "in {{count}} Bibliothek", + "IN_LIBRARIES_other": "in {{count}} Bibliotheken", + "BEFORE_TRACKING": "Vor Beginn des Verlaufs durchgespielt", + "TREND_MOVES": "Stapel-Änderungen der letzten 90 Tage", + "TREND_ADDED": "Diesen Monat zum Stapel hinzugekommen", + "TREND_DONE": "Diesen Monat vom Stapel gegangen", + "NEW_IN_LIBRARY": "Neu", + "OPEN": "Im Steam-Shop öffnen", + "GAMES_one": "{{count}} Spiel", + "GAMES_other": "{{count}} Spiele" } }, "ROLES": { diff --git a/public/i18n/en.json b/public/i18n/en.json index 043bbd7..d9c0d99 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -496,7 +496,9 @@ }, "TREND": { "TITLE": "Pile trend", - "THIS_MONTH": "This month: +{{added}} added, −{{done}} off the pile", + "THIS_MONTH": "This month:", + "THIS_MONTH_ADDED": "+{{added}} added", + "THIS_MONTH_DONE": "−{{done}} off the pile", "STARTS": "The trend starts today. Come back tomorrow for the first line." }, "TOP_GAMES": { @@ -557,6 +559,25 @@ "TACTICAL": "Tactics", "TURN_BASED_STRATEGY_TBS": "Turn-based strategy", "VISUAL_NOVEL": "Visual novel" + }, + "DRILL": { + "SHOW_ALL": "Show all", + "LOAD_MORE": "Load more", + "EMPTY": "No games behind this number.", + "ERROR": "Could not load the games.", + "NOT_SHARED": "The owner does not share this number.", + "EXPIRED": "This view has expired. Reload the page.", + "RELOAD": "Reload", + "IN_LIBRARIES_one": "in {{count}} library", + "IN_LIBRARIES_other": "in {{count}} libraries", + "BEFORE_TRACKING": "Finished before the history started", + "TREND_MOVES": "Pile moves in the last 90 days", + "TREND_ADDED": "Added to the pile this month", + "TREND_DONE": "Off the pile this month", + "NEW_IN_LIBRARY": "New", + "OPEN": "Open in the Steam store", + "GAMES_one": "{{count}} game", + "GAMES_other": "{{count}} games" } }, "ROLES": { diff --git a/src/components/PileOfShameCard.jsx b/src/components/PileOfShameCard.jsx index 27820f5..3856fa1 100644 --- a/src/components/PileOfShameCard.jsx +++ b/src/components/PileOfShameCard.jsx @@ -6,10 +6,12 @@ import {Button} from '@/components/ui/button'; import {Card} from '@/components/ui/card'; import GameCover from './GameCover.jsx'; import PileTrend from './stats/PileTrend.jsx'; +import {StatCount} from './stats/StatDrill.jsx'; import {STATUS_META} from './GameStatusBadge.jsx'; import {backlogShare} from '../utils/backlog.js'; import {GAME_STATUSES} from '../utils/limits.js'; import {formatPlaytime} from '../utils/games.js'; +import {libraryLink} from '../utils/statGames.js'; /** * @param trend the TREND group of GET /api/library/stats; the card shows no trend while it is missing @@ -34,17 +36,25 @@ export default function PileOfShameCard({summary, trend, syncing, onShuffle, shu
- {t('DASHBOARD.NEVER_PLAYED', {count: summary.neverPlayed})}
+
- {t('DASHBOARD.BACKLOG_HOURS', {time: formatPlaytime(summary.backlogMinutes)})}
+
{t('STATS.FINISHED.EMPTY')}
) : ( -
- {t('STATS.FINISHED.BEFORE', {count: finished.finishedBeforeTracking})}
+
- {t('STATS.TREND.THIS_MONTH', {added: thisMonth.added, done: thisMonth.done})}
+ {t('STATS.TREND.THIS_MONTH')}{' '}
+
{t('STATS.PLATFORMS.EMPTY')}
) : ( -{t('MAIN.DESCRIPTION')}
-
-
+
{t('POINTS.EMPTY_HISTORY')}
+ ) : ( +{t('POINTS.EMPTY_HISTORY')}
- ) : ( -{t('STATS.SUBTITLE')}
-{t('STATS.SUBTITLE')}