mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 07:26:30 +00:00
fix(roadtrip): a place tapped on the phone stage map opens its stop
The bar over the road trip map names a place, but a tap on it switched to the whole day's chain and the place never opened. It now opens that stop's sheet, the one its row in the chain opens. Its name, clock and tap come off one row (stageEnd), so the bar no longer pairs the destination with the automatic day end's clock (Kyoto Station 22:00 while the head card said 12:40). The header's list switch stays the way back to the chain. A pin on the stage went through the plan tab's marker click, which moves the place selection and brought up the plan tab's place card. On the road trip tab a pin now opens its stop on the stage card, or the first routed visit in the all days view, and only a place no routed day stops at still gets the place card. The handler keeps one identity for the life of the map. "Show on map" in the stop sheet and the up next card move the camera through focusRoadtripPoint instead of the selection, picking the stop's card first when it is not on screen. A pending focus now lets go once the day moves, so later stages frame their own drive again. Stage pins come from the trip store instead of the plan tab's map list. That list carries the plan tab's day declutter and place filters, so after the all days switch and back most pins of the next stage were missing, and the unplanned filter or hidden service stops removed stops the chain draws.
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
destinationCount,
|
||||
firstStopOfPlace,
|
||||
pickWarning,
|
||||
roadtripRows,
|
||||
stageClocks,
|
||||
stageEnd,
|
||||
stageOf,
|
||||
upNextStop,
|
||||
type RoadtripRow,
|
||||
@@ -12,7 +14,7 @@ import {
|
||||
import type { ScheduleEntry, ScheduleWarning } from './roadtripModel'
|
||||
import type { RoadtripDay, RoadtripStop, RouteSegment } from '@trek/shared/roadtrip'
|
||||
|
||||
// FE-RTROW-001 to FE-RTROW-033
|
||||
// FE-RTROW-001 to FE-RTROW-037
|
||||
|
||||
function stop(name: string, over: Partial<RoadtripStop> = {}): RoadtripStop {
|
||||
return {
|
||||
@@ -393,3 +395,51 @@ describe('stageClocks', () => {
|
||||
expect(stageClocks(roadtripRows(unsplit))).toEqual({ start: '18:30', arrive: '10:00' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('stageEnd', () => {
|
||||
it('FE-RTROW-034: names the last stop the chain draws with its own arrival, not the automatic day end after it', () => {
|
||||
// The reported bar: Kyoto Station reached at 12:40 sat beside 22:00, where the window closed.
|
||||
const d = day([stop('Bremen'), stop('Aral', { stopType: 'fuel' }), stop('Kyoto Station'), night()], {
|
||||
schedule: { entries: [entry('09:00'), entry('10:30'), entry('12:40'), entry('22:00')], warnings: [] },
|
||||
})
|
||||
const end = stageEnd(roadtripRows(d))
|
||||
expect(end?.stop.name).toBe('Kyoto Station')
|
||||
expect(end?.time).toBe('12:40')
|
||||
|
||||
// A service stop is still a stop the drive ends at, so it is the one named.
|
||||
const fuelLast = timedDay(['09:00', '11:00'], [stop('A'), stop('Aral', { stopType: 'fuel' })])
|
||||
expect(stageEnd(roadtripRows(fuelLast))?.stop.name).toBe('Aral')
|
||||
})
|
||||
|
||||
it('FE-RTROW-035: a stage drawn with nothing but its night markers, and no rows at all, end nowhere', () => {
|
||||
expect(stageEnd(roadtripRows(day([night('start'), night('end')])))).toBeNull()
|
||||
expect(stageEnd([])).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('firstStopOfPlace', () => {
|
||||
it('FE-RTROW-036: on one card, a place visited twice answers with its first visit and a night on it never does', () => {
|
||||
// A loop day: out of the hotel in the morning, back to it at night.
|
||||
const loop = day([
|
||||
stop('Nacht', { placeId: 300, assignmentId: 90, automaticNight: { phase: 'start', fromDayNumber: 1 } }),
|
||||
stop('Hotel', { placeId: 300, assignmentId: 1 }),
|
||||
stop('Museum', { placeId: 301, assignmentId: 2 }),
|
||||
stop('Hotel', { placeId: 300, assignmentId: 3 }),
|
||||
])
|
||||
expect(firstStopOfPlace([loop], 300)?.assignmentId).toBe(1)
|
||||
expect(firstStopOfPlace([loop], 301)?.assignmentId).toBe(2)
|
||||
})
|
||||
|
||||
it('FE-RTROW-037: across days it keeps day order, a card handed alone ignores the days before it, and no stop gives null', () => {
|
||||
const monday = day([stop('Town', { placeId: 301, assignmentId: 11 })], { dayId: 7 })
|
||||
const tuesday = day([stop('Town', { placeId: 301, assignmentId: 21 }), stop('Lake', { placeId: 302, assignmentId: 22 })], { dayId: 8 })
|
||||
expect(firstStopOfPlace([monday, tuesday], 301)?.assignmentId).toBe(11)
|
||||
expect(firstStopOfPlace([tuesday], 301)?.assignmentId).toBe(21)
|
||||
expect(firstStopOfPlace([monday, tuesday], 302)?.assignmentId).toBe(22)
|
||||
|
||||
expect(firstStopOfPlace([monday, tuesday], 999)).toBeNull()
|
||||
expect(firstStopOfPlace([], 301)).toBeNull()
|
||||
// Only an automatic night sits on this position, which is not a stop anybody chose.
|
||||
expect(firstStopOfPlace([day([stop('Nacht', { placeId: 303, automaticNight: { phase: 'end', fromDayNumber: 1 } })])], 303)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -197,6 +197,48 @@ export function stageClocks(rows: readonly RoadtripRow[]): { start: string | nul
|
||||
return { start, arrive }
|
||||
}
|
||||
|
||||
/**
|
||||
* The stop a stage ends on: the last one the chain draws, a service stop included.
|
||||
*
|
||||
* The map half names it in its bar and opens it on a tap, so the name, the clock beside it
|
||||
* and the sheet the tap brings up all come off this one row and cannot name three different
|
||||
* things. The clock used to be read off the schedule on its own, which found the automatic
|
||||
* day end whenever the window closed after the last stop: a station reached at 12:40 sat
|
||||
* beside 22:00, the moment the day ran out rather than anything that happens there.
|
||||
*
|
||||
* Null for a stage without a single stop, one drawn with nothing but its night markers.
|
||||
* There is nothing for a tap to open then, and the bar says so by not being a button.
|
||||
*/
|
||||
export function stageEnd(rows: readonly RoadtripRow[]): StopRow | null {
|
||||
for (let i = rows.length - 1; i >= 0; i--) {
|
||||
const row = rows[i]
|
||||
if (row.kind === 'stop') return row
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* The stop a place's pin stands for on the road trip map: its first visit in the days given.
|
||||
*
|
||||
* A pin is drawn once per place, while a place can be several stops, like the hotel a loop
|
||||
* day leaves in the morning and comes back to at night, or a town the drive passes on two
|
||||
* days. Over a stage the caller hands in that one card, so a pin opens the visit on the
|
||||
* stage being looked at, however many days before it stop at the same place. Over the
|
||||
* whole drive it hands in every routed day, and the first visit in day order is the one a
|
||||
* traveller reading the drive from its start reaches first.
|
||||
*
|
||||
* An automatic night can sit on a place's position, but nobody chose to stop there, so it
|
||||
* never answers. Null when none of the days stops at the place, which is a pin the road
|
||||
* trip cannot explain and the caller hands to the plan's own place inspector instead.
|
||||
*/
|
||||
export function firstStopOfPlace(days: readonly RoadtripDay[], placeId: number): RoadtripStop | null {
|
||||
for (const day of days) {
|
||||
const stop = day.stops.find(s => !s.automaticNight && s.placeId === placeId)
|
||||
if (stop) return stop
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* The next destination the plan still owes you, measured against the clock.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user