mirror of
https://github.com/tiennm99/vngeoguessr.git
synced 2026-10-15 04:18:57 +00:00
feat(game): put the region in the URL path
/game?region=TPHCM becomes /game/tphcm, backed by an app/game/[region] dynamic segment. The region is a property of the page, so it belongs in the path: the URL is now shareable and honest, an unknown region is a real 404 instead of silently rendering as "Vietnam", and the page no longer needs useSearchParams or its Suspense boundary. It also makes per-region traffic visible. Vercel's Pages dimension strips query parameters on every plan, so ?region= was invisible; one row per region falls out of correct routing. Slugs are lowercase, and regionSlug/regionFromSlug in lib/regions.js own the conversion. Three call sites build these URLs -- the picker, generateStaticParams, and the legacy redirect -- and a casing mismatch between any two would split one region across two rows, which is the whole point of the move. An unusual casing renders rather than redirecting to the canonical form. A redirect on a prerendered route gets cached as that route's response: on a case-insensitive filesystem the ISR cache folds /game/DNA onto /game/dna, and a cached redirect that has lost its Location header then answers the canonical URL with a 307 to nowhere for the life of the process. Reproduced on Windows against next start; no redirect on the route means no such mechanism anywhere. Legacy ?region= and ?location= redirect from app/game/page.js rather than next.config.mjs, because a config redirect forwards the source query to the destination and would park ?region= on /game/tphcm permanently. The legacy value is encodeURIComponent'd: it reaches a Location header unvalidated, where a raw CRLF makes Node throw a 500 instead of the honest 404, and a raw ../ would be normalised onto another path. A region-less /game now plays the country rather than defaulting to TPHCM, which was never a stated default -- just where the old query chain happened to end. API routes keep their query params. They are fetch calls, not navigations, so a path segment buys nothing there. Also repairs four specs that were already failing on main: three asserted that the landing page shows no username prompt, which the landing-prompt change contradicted, and one expected the build sha and its copy button to be a single element, which splitting them contradicted.
This commit is contained in:
1 parent
90ebed728a
commit
711ef863a6
14 files changed
+409
-71
No files matched your search
+3
-2
@@ -98,8 +98,9 @@ The UI has a Playwright smoke lane: `npm run test:e2e` runs `tests/e2e/*.spec.js
|
||||
in Chromium against a dev server it starts (or reuses) itself. Every `/api/*`
|
||||
call and the panorama image are stubbed at the browser boundary
|
||||
(`tests/e2e/helpers.js`), so it needs no Redis, Neon, Mapillary, or `.env`.
|
||||
It covers the region picker, the username modal, and one full round to the
|
||||
reveal. Anything beyond those flows (real panoramas, real scoring round-trips)
|
||||
It covers the region picker, the username modal, one full round to the
|
||||
reveal, and the `/game/{slug}` routing contract (legacy `?region=` redirects,
|
||||
unknown-region 404s). Anything beyond those flows (real panoramas, real scoring round-trips)
|
||||
is still manual: inform the user when work is complete. Do NOT start
|
||||
development servers - user handles manual testing themselves.
|
||||
|
||||
|
||||
Reference in new issue
Block a user