mirror of
https://github.com/tiennm99/vngeoguessr.git
synced 2026-10-11 03:13:56 +00:00
docs: record fresh session ids, the daily pick rules and the new components
This commit is contained in:
1 parent
4d25a82448
commit
b8b4ffbbcb
3 files changed
+24
-9
No files matched your search
+2
-1
@@ -51,7 +51,8 @@ are the shape the code has and new code should keep.
|
||||
board is written, and the fan-out after it settles per level rather than
|
||||
all-or-nothing, because nothing after the claim can be retried.
|
||||
- **One module per browser-storage concern**, all reading through
|
||||
`src/lib/storage.js` (never throws, notifies watchers) and rendered through
|
||||
`src/lib/storage.js` (never throws, keeps a write the browser refuses in
|
||||
memory for the visit, notifies watchers) and rendered through
|
||||
`useStoredValue` in `src/lib/use-stored-value.js`. No component seeds state
|
||||
from storage in an effect; the `react-hooks/set-state-in-effect` rule is an
|
||||
error.
|
||||
|
||||
+10
-6
@@ -78,8 +78,9 @@
|
||||
the name prompt and by `/api/guess`; a colon is excluded because the distance
|
||||
boards pack `username:distance:timestamp` into one member. Guess coordinates
|
||||
must be finite numbers, checked before the session is consumed so a malformed
|
||||
submit costs nothing. A `sessionId` that is not a UUID is replaced on
|
||||
`/api/new-game` and rejected on `/api/skip`; only server-minted ids reach the
|
||||
submit costs nothing. `/api/new-game` mints a fresh session id every round
|
||||
and ignores any the client offers; a `sessionId` that is not a UUID is
|
||||
rejected on `/api/guess` and `/api/skip`. Only server-minted ids reach the
|
||||
keyspace
|
||||
- **Server-resolved region**: the district a panorama sits in is decided at
|
||||
session creation and never sent to the client; a `regionCode` in the guess
|
||||
@@ -95,9 +96,9 @@
|
||||
`invalid-guess`, `invalid-username`, `invalid-request`) and the result dialog
|
||||
words each one differently, so an expired round is not reported as a failed
|
||||
write
|
||||
- **Skipped rounds get a fresh id**: the skip request deletes the old session
|
||||
without being awaited, so the next round never reuses that id -- a late
|
||||
delete used to land after the new round's write and kill it
|
||||
- **Every round gets a fresh id**: a reused id let a late skip delete, or a
|
||||
guess claiming the previous round, land on the new round's session and kill
|
||||
it
|
||||
- **Server-side Calculations**: all distance and scoring computed server-side
|
||||
using Turf.js
|
||||
|
||||
@@ -188,7 +189,10 @@ renaming it would orphan every score already recorded under it.
|
||||
for 48 hours, so the Postgres draw happens once a day. The image URL is
|
||||
resolved from Mapillary on every request, as every round does, so a signed
|
||||
URL that stops working never breaks the day; a pick deleted upstream is
|
||||
forgotten and the next seeded candidate takes over
|
||||
forgotten and the next seeded candidate takes over. A timeout or 5xx is not
|
||||
proof of deletion and keeps the pick (the request fails with a 502), so a
|
||||
Mapillary blip cannot give one day two panoramas. The pick is written with
|
||||
`SET NX`, so two instances that draw differently still serve the first one
|
||||
- **Days roll over at midnight Vietnam time** (`src/lib/daily-calendar.js`),
|
||||
numbered from 2026-09-20 as #1
|
||||
- **Scored, counted, never credited**: `/api/daily` opens an ordinary session
|
||||
|
||||
@@ -80,7 +80,13 @@ Next.js 16 App Router structure:
|
||||
- `FirstRoundHint.js` - One-time how-to-play banner, rendered in flow into
|
||||
the panorama pane's top row via `PanoramaViewer`'s `topBarSlot`
|
||||
- `DonateQRModal.js` - Donation QR code modal
|
||||
- `ThemeToggle.js` - Light/dark switch
|
||||
- `ThemeToggle.js` - Light/dark switch; only writes the stored choice
|
||||
- `ThemeSync.js` - Applies the stored theme (and OS flips while it is
|
||||
'system') to the document. Renders nothing; mounted once in the root layout
|
||||
- `ShareButton.js` - Share-sheet/clipboard button with its outcome label and
|
||||
screen-reader status, used by the result dialog and the daily card
|
||||
- `PlayLink.js` - A game link whose plain click the home page can intercept
|
||||
for the name prompt
|
||||
- `SoundToggle.js` - Music and sound-effect switches; `compact` renders one
|
||||
mute-everything button for the game header below `sm`
|
||||
- `MusicPlayer.js` - The background loop. Renders nothing and is mounted in the
|
||||
@@ -140,6 +146,8 @@ Neon Postgres, which is what the app queries at runtime.
|
||||
- `mapillary.js` - Mapillary lookup by image id
|
||||
- `player-id.js` - **Server-side only.** The anonymous `vng_pid` cookie that
|
||||
identifies a browser for repeat-avoidance, and nothing else
|
||||
- `cookies.js` - Cookie-header parsing for plain `Request`s, shared by
|
||||
`player-id.js` and `debug-access.js`
|
||||
- `pano-history.js` - **Server-side only.** The last 50 panoramas a player was
|
||||
shown, in Redis with a rolling 3-day expiry
|
||||
- `session.js` - Redis-based session management with 30-min expiry
|
||||
@@ -192,7 +200,9 @@ and `debug/pano` routes have no dedicated test file; their underlying
|
||||
`wait-for-srh.js` are the shared harness that lets the same files run against
|
||||
either the in-memory fake or a real Redis. `fake-neon.js`, `mock-neon.js` and
|
||||
`pano-fixtures.js` are the equivalent for the panorama store: PGlite behind the
|
||||
Neon SDK boundary, loaded with small synthetic rows.
|
||||
Neon SDK boundary, loaded with small synthetic rows. `mapillary-stub.js`
|
||||
stubs the Mapillary Graph API around every test of a file that calls
|
||||
`stubMapillary()`.
|
||||
|
||||
`tests/e2e/` holds the Playwright smoke specs (`*.spec.js`, so vitest never
|
||||
collects them): the homepage picker, the username modal, one full round, and
|
||||
|
||||
Reference in new issue
Block a user