feat(web): show live departures per platform in stop search results#22
Merged
Conversation
Multi-platform stops (e.g. Anděl) are the hard case in search: the result card named the platforms but never said which one to stand on. Each multi-platform result now has a disclosure toggle that expands an inline, per-platform list showing the next vehicle leaving from each platform — route, headsign, and a live countdown — so the user can pick the right platform before adding it. - lib/platforms.ts: buildPlatformPicks joins index platforms with an optional live board by raw platform code (so per-line metro codes match), keeping index order. No geo — search has no per-platform walk time, so countdowns render neutral-tier with no reachability coloring. - search.tsx: SearchView owns an accordion expandedNode and subscribes to that one node's whole-node board on demand via useDepartures (one result open at a time → a single extra subscription); ResultCard renders the live PlatformRow list, replacing the old static "nást. A" list. - icons.tsx: ChevronIcon disclosure glyph. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017UFBVqE6WdTKpVybhkQvST
|
🚀 Preview deployed: https://preview-22.tablo.run |
The disclosure toggle was redundant — search results already listed each platform inline before, so hiding them behind a tap was a regression. Show the per-platform live departures inline for every multi-platform result instead. SearchView now subscribes to the whole-node board of each visible multi-platform result (capped at MAX_SELECTORS; the board route is unmounted on /search, so the full budget is free) and threads a node→departures map down. Results past the cap, or before their board arrives, render the platform list without countdowns. Removes the accordion expandedNode state and the ChevronIcon. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017UFBVqE6WdTKpVybhkQvST
…oads
departuresByNode.get() returned undefined both while a subscribed board
was in flight and for nodes past the cap, so loading platforms fell
through to the "no departures" empty state. Encode the three states
explicitly ("loading" | departures | absent): rows render a pulsing
skeleton while loading, "no departures" only once the board has arrived,
and nothing extra for un-subscribed nodes past the cap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017UFBVqE6WdTKpVybhkQvST
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 & why
Multi-platform stops (e.g. Anděl) are the hard case in search: a result card names the platforms but never says which one to stand on. This adds live public-transport data to search results so the choice is informed — each multi-platform result lists its platforms inline, each showing the next vehicle leaving from it (route, headsign, live countdown), so you can pick the right platform before adding it.
This carries over just the live-data idea from #16's spatial picker, without any geo: no map, no Leaflet dependency, no per-platform coordinates, and no change to the
StopPlatformcontract.How it's built
lib/platforms.ts(new): purebuildPlatformPicksjoins index platforms with an optional live board by rawplatform === code(NOTplatformKey, which collapses metro to one key and wouldn't match the index's per-line metro codes). Index order (by code) is preserved. No geo: search has no per-platform walk time, so countdowns render neutral-tier with no reachability coloring.search.tsx:SearchViewcomputes the visible result entries, then subscribes viauseDeparturesto the whole-node board of every multi-platform result, capped atMAX_SELECTORS(20) — the board route is unmounted on/search, so the whole budget is free. It threads anode → (DepartureVM[] | "loading")map down.ResultCardrenders each platform's livePlatformRow(vehicle icon, route chip, headsign, countdown, add toggle) inline — no tap required.PlatformRowstates: a pulsing skeleton while the subscribed board is in flight; the live departure once it arrives; a quiet "no departures" only after arrival when a platform has nothing upcoming; and just the code + Add for results past the subscription cap (un-fetched).Behaviour notes
/searchis open, so the preview subscriptions are the only live socket.Verification
bun run typecheck— clean (the remaining TS11 messages are pre-existing Effect-LS info diagnostics in the untouched worker package).bun run test— 82 passing, incl. a newplatforms.test.ts(index-order listing, soonest-departure join, metro raw-code join).bun run lint/bun run fmt:check— clean.bun run build:web— succeeds; no Leaflet chunk (no map dependency added).🤖 Generated with Claude Code
https://claude.ai/code/session_017UFBVqE6WdTKpVybhkQvST