Repository navigation
feat(tracking): show your game status on every game card - #59
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The movie, game, show and episode reads each carried the same sort and join one-liner, and the card slot tests stubbed a copy of it. One pure idsKey and parseIdsKey in lib/tracking/ids-key.ts now serve them all. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec 0030's surface table says an empty key returns an empty map with no query; the check ran after getOptionalUser, so an empty grid still read the session and a visitor got signed_out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Playing or Finished literal and the want_to_play comparisons were written out across the chip, the card status, the showcase, the card slots and the library card. StartedGameStatus and isStartedGame in lib/tracking/types.ts now carry that rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
readStatus wrapped its result in an object only to tell a visitor or a failed read apart from a game with no row, so callers wrote status.status. readCard returns unknown or known with the card instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RemoveButton defaulted to ml-auto and the started card cancelled it with an empty className. The button now sets no margin, and the one card where the X stands alone passes ml-auto. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec 0030 AC-8 says a card and its library page read the same stored row. gameItem took the status from the tab's GAME_LIBRARY_TABS entry instead, which only matched while the query filtered on it. The tab read now selects status and the card shows row.status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Game cards now show your own state everywhere they appear: on the
/gameslanding, the five/games/{slug}list pages and/search?type=game. When you are signed in, each card shows your score when you have one, a Want to play bookmark you can tap, a release pill on a wanted game that is not out yet, and a read only Playing or Finished chip on a game you have started. The library tabs get the same bookmark and chip.Why
This builds scope feature G8 from spec 0030 (rationale). Until now game cards were plain covers with the IGDB rating, so you had to open a game to see whether it was already yours. The spec mirrors the movie card slots: one grid read per grid, a pure card status function, a server badge slot and controls slot, and one small client bookmark. It adds no table and no Server Action.
Changes
One read per grid (
lib/tracking/,lib/catalog/)getGameGridStates(idsKey)inlib/tracking/game-state.ts, wrapped in Reactcache(). It readsuser_game_stateonce for all the grid's ids, under the user's session.gameIdsKeysorts and dedupes the ids, so every card on a grid shares that one read. It checkspublicEnvProblems()first, and a failed read logs once and falls back to the plain card.gameCardStatus(state, releaseDate, today)inlib/catalog/game-page.ts. It reads straight from the stored status, so a card always names the library tab the game is on (AC-8).Card slots (
components/tracking/)GameCardBadgeshows your cyan score on a Playing or Finished game that has one, and the amber IGDB rating otherwise (also while loading, for a visitor, or after a failed read).GameCardControlsshows the bookmark on a card with no status or with Want to play, and theGameStatusChipon a started game. A Want to play game that is not released yet (UTC day rule) gets theReleasePill, or Date TBA.releasePill={false}skips it on cards that already print the date, so Coming soon never shows two.GameCardBookmarkButtonis optimistic and disabled while its write runs. Unpressed it callssetGameStatus(id, "want_to_play"); pressed it callsremoveGame(id). A failure rolls it back and showsshowGameTrackingError(with the Sign in action back to the grid's page). Everyundogoes toshowGameUndo, so removing offers Undo, and adding over a stale started card shows the score cleared Undo.GameStatusChiphas noserver-onlyimport, so the client library grid can render it too.Wired into every grid (
components/catalog/,components/search/)GameListCardtakesgridGameIdsandreturnPath, wraps the badge and controls in their own Suspense boundaries, and prints the release year as its meta line. The oldmetaprop is gone.CatalogListCardspasses each list page's ids;/searchpasses the results page's ids andsearchHref(params)as the return path.LANDING_SLOTS.gamenow has a badge, controls and a newmetafield (the year). Badge and controls are no longer nullable, soLandingBadgeandLandingControlslose their null checks. This also lights the Coming soon wide cards and the Top rated rows.Library tabs (
components/library/)LibraryGameItemgets astatusfield, set ingameLibraryTab.PlannedIconbutton, namedRemove {name} from Want to play, using the same removal and Undo the grid already owns. Playing and Finished cards show the chip left of the X. The missing game card keeps the X.Smaller pieces
Plan / Want to playandPlanned / Want to play./showcasegets a Game card section with all seven states (visitor, no status, Want to play released, unreleased, TBA, Playing scored, Finished unscored).How to test / verify
You can run these against
pnpm dev:dockerwithNEXT_PUBLIC_GAMES_LIBRARY=on. The full checklist is in verify.md; the UI and command steps are ticked, and three value sourcing checks are still open there (grid ids never mixed across sections, no score badge on a Want to play row, the UTC day boundary in a timezone ahead of UTC).pnpm typecheck && pnpm lint && pnpm test, thenpnpm build./games, every/games/{slug}and/searchshould stay partially prerendered (◐)./search?type=game&q=witcher. The grid should run oneuser_game_statequery for all its cards./watchlist?type=game. Tap it again: "Removed from your games." with Undo.Playingchip, with no button./games/coming-soonthere should still be exactly one date pill./watchlist?type=gamethe card should show the green bookmark; on/upcoming?type=gameand/watched?type=gamethe chip left of the X./games,/games/popularand/search?type=gameshould show IGDB ratings only, no bookmark or chip.Risk & rollout
Low risk. No migrations, no new Server Actions and no new flags. The cards add no
NEXT_PUBLIC_GAMES_LIBRARYgate of their own; the routes keep the gates they have. Shows and movies are touched only throughLANDING_SLOTS(they getmeta: null) and the icon key labels. Rollback is a plain revert.Tradeoffs the spec accepts:
/gamesruns one status query per section (about five), as the movie landing does.Notes for reviewers
releasePillinGameListCardisreleaseDay === undefined, so the coming soon list keeps its visitor pill and the slot skips its own. The landingWideCardalready passedreleasePill={false}.RemoveButtoningame-library-card.tsxgains aclassNameprop, so the X after a chip does not take its ownml-auto(the chip already pushes the row right)./syncshould update thecomponents/AGENTS.mdline saying the game landing entry is null until G8, and the root card status line should namegetGameGridStates. The G8 follow ups in specs 0027, 0028 and 0029 can be ticked.🤖 Generated with Claude Code