Skip to content

feat(web): list the games I played lately on the Games page - #190

Merged
Bakhomious merged 3 commits into
mainfrom
step/games-page
Oct 30, 2026
Merged

Bakhomious merged 3 commits into
mainfrom
step/games-page

Conversation

@Bakhomious

@Bakhomious Bakhomious commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Brief

Goal: the Games page shows the games I played in the last 14 days, from GET /api/games/recent, each time it shows: for each game, the day I last played it, its title, its platforms, its trophies and its playtime. When the latest fetch failed, the list is from an earlier one and gets a Stale chip (decision 67). The Games card's description becomes §7's. The tenth of games-exophase's eleven steps (decision 307).

Scope

  • apps/web/src/games/games.ts: the snapshot's types, loadGames, and the row's labels: the day ("Today", "Yesterday", "19 Oct"), the platforms ("PS4 · PS5"), the trophies ("12 of 40 trophies · 30%") and the playtime ("12h 30m").
  • apps/web/src/games/GamesPage.svelte: the page head, then either "Exophase isn't set up." in the page's status line (decision 206) or the games in a polled section (decision 205), "Recently played".
  • shell/Section.svelte and shell/PolledSection.svelte: an opt-in Stale chip after the section's count, shown when the list is from an earlier search.
  • shell/page.ts and App.svelte: the games page at /games and the paths under it.
  • shell/apps.ts: the Games description becomes "Recent games and stats from Exophase" (§7, decision 66).
  • Tests: unit tests for each part, e2e/games.spec.ts with demo games, and the Games line in e2e/page-status.spec.ts.
  • Plan: the §0 row, §7's Games page and section head, §8.2, decision 307.

Out of scope

Acceptance criteria

  1. /games and the paths under it show the Games page:
    • its title is "Games · Bakhyard";
    • its h1 is "Games", with no summary;
    • the nav marks Games, and the tab bar marks More, as before.
  2. With games, a section "Recently played" shows the count as its note ("1 game", "3 games"). Its card has a row per game, in the service's order. No row is a link. Each row shows:
    • label: the day the game was last played, in the device's time zone ("Today", "Yesterday", "19 Oct"), in a <time> element that carries the instant;
    • title: the game's title;
    • first detail line: the platforms, joined by " · ";
    • second detail line: "12 of 40 trophies · 30%", with "trophy" for a total of 1 and the percentage rounded down; left out when the game has no trophies;
    • end: the playtime ("12h 30m", "45m", "3h"); left out when it's unknown.
  3. With no games, the card says "No games played in the last 14 days.", and the head has no count.
  4. When the latest fetch failed:
    • The section's muted line says "The last search failed: ."
    • If the section has games from an earlier fetch, the line goes on, "These games are from 21 October at 09:38.", and an outlined Stale chip follows the count in the section's head.
    • If no fetch has succeeded yet, the line shows alone: no card, no count and no chip.
  5. A 404 shows "Exophase isn't set up." in the page's status line, with no section. Any other failure to load shows "Couldn't load the games." under the section's head.
  6. The page loads the games each time it shows.
  7. The other pages' sections don't change: no chip, and the same DOM.
  8. The Games card in "Your apps" and on More says "Recent games and stats from Exophase".
  9. On a 320 px screen, a long title, a long platform name or a long failure wraps, and the page doesn't scroll sideways.
  10. mise run check is green.

What changed

  • apps/web/src/games/:
    • games.ts: the snapshot's types, loadGames, foundGames and the row's labels;
    • GamesPage.svelte: the page;
    • the tests for both.
  • shell/Section.svelte: an optional badge, a chip after the section's note.
  • shell/PolledSection.svelte: markStale. When a page sets it, a list kept from an earlier search, after the latest failed, gets the Stale badge. Only the Games page sets it.
  • shell/page.ts and App.svelte: the games page at /games and the paths under it, titled "Games · Bakhyard".
  • shell/apps.ts: the Games card's description (decision 66).
  • e2e:
    • games.spec.ts: 8 tests on desktop and phone;
    • demo.ts: four made-up games;
    • page-status.spec.ts: the Games line.
  • docs/plan.md:
    • the §0 row;
    • §7's Games page, and the section head's chip;
    • §8.2;
    • decision 307.

The live page says "Exophase isn't set up." until the gateway forwards /api/games (#188), which waits for codedogapp/homelab#44 (decision 300).

QA

  • Acceptance criteria, each checked by a test:
    • AC1: page.test.ts and App.pages.test.ts cover the paths and the title. games.spec.ts covers the nav, and the unit test shows the page's text is "Games" alone while it loads.
    • AC2: games.test.ts, GamesPage.test.ts and games.spec.ts.
    • AC3: the empty-list tests.
    • AC4: the stale and first-failure tests. The e2e checks:
      • the chip is outlined;
      • it sits 10 px after the count and centred on its line;
      • the failure line is 20 px above the card.
    • AC5: the 404 and 500 tests, and the Games line in page-status.spec.ts.
    • AC6: the e2e leaves for Home and comes back through the Games card, and the page shows a game it didn't have.
    • AC7: PolledSection.test.ts shows no chip unless asked. A throwaway test rendered main's Section.svelte and this one with four sets of props and no badge: the DOM was the same, once Svelte's hash classes were stripped.
    • AC8: MorePage.test.ts, and the e2e's click on the card.
    • AC9: the 320 px e2e test.
    • AC10: below.
  • Hand mutants:
    • Unit tests: 35 mutants, all killed. Two new tests were needed first:
      • a game played at 05:00 on New Year's Day in Tokyo, while UTC is still on the old year, which kills getUTCMonth and getUTCFullYear;
      • a badge other than Stale, which kills a hard-coded chip.
    • e2e: 4 CSS and markup mutants, all killed: no flex, a 4 px gap, no centring (2 px off), and a filled chip.
  • Edge cases:
    • playedLabel gives the right day across both summer-time changes, in London and in New York (a Node probe of the same code).
    • An empty platforms list leaves an empty detail line with no height.
    • Counts get thousands separators: "1,234 of 5,678 trophies".
  • Gates: mise run check is green:
    • Go lint: 0 issues;
    • govulncheck: clean;
    • Go coverage: 96.6%;
    • Vitest: 1270 tests; web coverage 99.79% of statements, and 100% for src/games;
    • Playwright: 412 passed, 8 skipped.

Review

No blocking findings. Non-blocking:

  1. The e2e checks the chip's centring to within 1 px, since the count's box is its text's, from the font's metrics, which the browser rounds. Without align-items: center, the chip is 2 px off.
  2. The page has its own narrow duration format beside the Time page's formatHours, which writes 45 minutes as "0h 45m" (decision 307).
  3. The page says "trophies" on every platform (decision 307). Saying "achievements" where Exophase does would need each platform's kind from the service.
  4. A lastPlayed later than now, from a device clock that runs slow, shows its date rather than "Today".

Screenshots

With the e2e demo data, the made-up name Alex, and Tokyo time at 09:41 on Wednesday 21 October 2026.

Light Dark
Desktop The Games page on a desktop, light The Games page on a desktop, dark
Phone The Games page on a phone, light The Games page on a phone, dark
Desktop, stale The stale games on a desktop, light The stale games on a desktop, dark
Phone, stale The stale games on a phone, light The stale games on a phone, dark

The Games page shows the games I played in the last 14 days from the
games service, each time it shows: the day I last played each, its
title, its platforms, its trophies and its playtime. A list kept from an
earlier fetch, after the latest failed, gets a Stale chip after its
count (decision 67). Decision 307 records the choices.
@Bakhomious
Bakhomious merged commit ebb0862 into main Oct 30, 2026
2 checks passed
@Bakhomious
Bakhomious deleted the step/games-page branch October 30, 2026 04:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant