fix(pokedex): open detail cards without a per-card round trip

Since the grid transport change, opening a Pokémon fetched
/api/pokedexes/[id]/entries/[entryId]. That request re-scanned the whole
dex to authorise one entry, on top of an auth call and an ownership
query, and production runs its functions in a different region from the
database — so each card took seconds to fill in. The page's 60-second
reconcile also wiped the detail cache, making cards refetch about once a
minute.

Details are catalog text plus personal notes, so the dex is now read once
per account/dex in the background at the interactive/idle boundary and
fills that cache; a card opens straight from memory. That read reuses the
existing combined-data endpoint, which gains an opt-out for its count
query since a caller taking every row already knows the total.

The per-entry endpoint stays as the fallback for a card opened before the
background read lands, and now checks membership for the single entry
instead of materialising the dex. Catch status still comes from the live
grid row and any queued write, so a cached detail cannot show stale
progress.

The packed grid transport and virtualised rendering are unchanged.
This commit is contained in:
Josh Creek
2026-09-18 15:27:53 +01:00
parent 669d0cdeaa
commit c917bba54a
14 changed files with 442 additions and 50 deletions
+6
View File
@@ -71,6 +71,9 @@ try {
`Scroll anchor moved: ${anchorTop} to ${resizedTop}`
);
await page.setViewportSize({ width: 1350, height: 940 });
// Let the resize settle first: restoring the box anchor scrolls the page, and a late restore
// would undo the scroll below.
await page.waitForTimeout(200);
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await page.waitForSelector('[data-entry-index="1024"]');
assert.ok((await page.locator('[data-entry-index]').count()) < 300);
@@ -117,6 +120,9 @@ try {
await page.waitForSelector('[data-grid-interactive]');
assert.ok((await page.locator('body').innerText()).includes('Showing 439 of 439'));
await page.goBack();
// Block the background detail read so the cache stays empty: these checks are about the offline
// snapshot answering a click, not about details the browser already holds.
await page.route(`**/api/pokedexes/${national.id}/combined-data*`, (route) => route.abort());
await page
.locator('.card')
.filter({ hasText: national.name })