diff --git a/app/component/itinerary/navigator/NaviCardContainer.js b/app/component/itinerary/navigator/NaviCardContainer.js index 97c7ac1b94..31c2282d08 100644 --- a/app/component/itinerary/navigator/NaviCardContainer.js +++ b/app/component/itinerary/navigator/NaviCardContainer.js @@ -11,6 +11,7 @@ import { getAdditionalMessages, getItineraryAlerts, getTransitLegState, + itinerarySearchPath, LEGTYPE, DESTINATION_RADIUS, } from './NaviUtils'; @@ -92,6 +93,17 @@ function NaviCardContainer( config, ); + const makeNewItinerarySearch = () => { + const path = itinerarySearchPath( + time, + currentLeg, + nextLeg, + position, + match.params.to, + ); + router.push(path); + }; + useEffect(() => { updateClient(getNaviTopics(), context); }, []); @@ -116,8 +128,7 @@ function NaviCardContainer( origin, intl, messages, - match.params, - router, + makeNewItinerarySearch, ), ); @@ -170,11 +181,10 @@ function NaviCardContainer( // handle initial focus when not tracking if (currentLeg) { focusToLeg(currentLeg); - destCountRef.current = 0; } else if (time < legTime(firstLeg.start)) { focusToLeg(firstLeg); } else { - focusToLeg(lastLeg); + focusToLeg(nextLeg || lastLeg); } focusRef.current = true; } diff --git a/app/component/itinerary/navigator/NaviUtils.js b/app/component/itinerary/navigator/NaviUtils.js index 9c25e670aa..d258d9a5ba 100644 --- a/app/component/itinerary/navigator/NaviUtils.js +++ b/app/component/itinerary/navigator/NaviUtils.js @@ -7,6 +7,7 @@ import { timeStr, epochToIso } from '../../../util/timeUtils'; import { getFaresFromLegs } from '../../../util/fareUtils'; import { ExtendedRouteTypes } from '../../../constants'; import { getItineraryPagePath } from '../../../util/path'; +import { locationToUri } from '../../../util/otpStrings'; const TRANSFER_SLACK = 60000; const DISPLAY_MESSAGE_THRESHOLD = 120 * 1000; // 2 minutes @@ -279,7 +280,10 @@ export const getTransitLegState = (leg, intl, messages, time) => { const departure = leg.trip.stoptimesForDate[0]; const departed = 1000 * (departure.serviceDay + departure.scheduledDeparture); - if (time - departed < DISPLAY_MESSAGE_THRESHOLD) { + if ( + time - departed < DISPLAY_MESSAGE_THRESHOLD && + time + DISPLAY_MESSAGE_THRESHOLD > legTime(leg.start) + ) { // vehicle just departed, maybe no realtime yet severity = 'INFO'; } else { @@ -330,6 +334,43 @@ export const getTransitLegState = (leg, intl, messages, time) => { return [{ severity, content, id: legId, expiresOn: legTime(start) }]; }; +export function itinerarySearchPath(time, leg, nextLeg, position, to) { + let from; + if (leg?.transitLeg) { + from = leg.intermediatePlaces.find( + p => legTime(p.arrival) > time + TRANSFER_SLACK, + ); + if (!from) { + from = leg.to; + } + } else { + from = position || leg?.to || nextLeg?.from; + } + const location = { ...from, ...from.stop }; + + return getItineraryPagePath(locationToUri(location), to); +} + +function withNewSearchBtn(children, searchCallback) { + return ( +
+ {children} + + +
+ ); +} + +function alertId(alert) { + return `${alert.effectiveStartDate}-${alert.alertDescriptionText}`; +} + export const getItineraryAlerts = ( legs, time, @@ -337,21 +378,16 @@ export const getItineraryAlerts = ( origin, intl, messages, - location, - router, + itinerarySearchCallback, ) => { - const canceled = legs.filter( - leg => leg.realtimeState === 'CANCELED' && legTime(leg.start) > time, - ); - let content; const alerts = legs.flatMap(leg => { return leg.alerts .filter(alert => { - const { first } = getFirstLastLegs(legs); - const startTime = legTime(first.start) / 1000; - if (messages.get(alert.id)) { + if (messages.get(alertId(alert))?.closed) { return false; } + const { first } = getFirstLastLegs(legs); + const startTime = legTime(first.start) / 1000; // show only alerts that are active when // the journey starts if (startTime < alert.effectiveStartDate) { @@ -372,30 +408,22 @@ export const getItineraryAlerts = ( {alert.alertHeaderText} ), - id: `${alert.effectiveStartDate}-${alert.alertDescriptionText}`, + id: alertId(alert), })); }); - const abortTrip = ; - const withShowRoutesBtn = children => ( -
- {children} - -
+ + const canceled = legs.filter( + leg => leg.realtimeState === 'CANCELED' && legTime(leg.start) > time, ); - if (canceled) { + if (canceled.length) { // show routes button only for first canceled leg. canceled.forEach((leg, i) => { const { legId, mode, route } = leg; const lMode = getLocalizedMode(mode, intl); const routeName = `${lMode} ${route.shortName}`; + const m = ( ); // we want to show the show routes button only for the first canceled leg. - if (i === 0) { - content = withShowRoutesBtn( -
- {m} - {abortTrip} -
, + const content = + i === 0 ? ( + withNewSearchBtn({ m }, itinerarySearchCallback) + ) : ( +
{m}
); - } else { - content =
{m}
; - } + if (!messages.get(`canceled-${legId}`)) { alerts.push({ severity: 'ALERT', @@ -423,36 +448,33 @@ export const getItineraryAlerts = ( }); } }); - } - - const transferProblems = findTransferProblems(legs, time, position, origin); - if (transferProblems.length) { - let prob = transferProblems.find(p => p.severity === 'ALERT'); - if (!prob) { - // just take first - [prob] = transferProblems; - } - const transferId = `transfer-${prob.fromLeg.legId}-${prob.toLeg.legId}}`; - const alert = messages.get(transferId); - if (!alert || alert.severity !== prob.severity) { - content = withShowRoutesBtn( -
- - {abortTrip} -
, - ); - alerts.push({ - severity: prob.severity, - content, - id: transferId, - hideClose: prob.severity === 'ALERT', - }); + } else { + const transferProblems = findTransferProblems(legs, time, position, origin); + if (transferProblems.length) { + let prob = transferProblems.find(p => p.severity === 'ALERT'); + if (!prob) { + // just take first + [prob] = transferProblems; + } + const transferId = `transfer-${prob.fromLeg.legId}-${prob.toLeg.legId}}`; + const alert = messages.get(transferId); + if (!alert || alert.severity !== prob.severity) { + alerts.push({ + severity: prob.severity, + content: withNewSearchBtn( + , + itinerarySearchCallback, + ), + id: transferId, + hideClose: prob.severity === 'ALERT', + }); + } } } return alerts; diff --git a/app/component/itinerary/navigator/hooks/useRealtimeLegs.js b/app/component/itinerary/navigator/hooks/useRealtimeLegs.js index a3a4623b36..383239318d 100644 --- a/app/component/itinerary/navigator/hooks/useRealtimeLegs.js +++ b/app/component/itinerary/navigator/hooks/useRealtimeLegs.js @@ -91,8 +91,13 @@ function matchLegEnds(legs) { } } -function getLegsOfInterest(initialLegs, time, previousFinishedLeg) { - if (!initialLegs?.length) { +function getLegsOfInterest( + realTimeLegs, + time, + previousFinishedLeg, + itineraryStarted, +) { + if (!realTimeLegs?.length) { return { firstLeg: undefined, lastLeg: undefined, @@ -100,8 +105,7 @@ function getLegsOfInterest(initialLegs, time, previousFinishedLeg) { nextLeg: undefined, }; } - - const legs = initialLegs.reduce((acc, curr, i, arr) => { + const legs = realTimeLegs.reduce((acc, curr, i, arr) => { acc.push(curr); const next = arr[i + 1]; @@ -130,7 +134,8 @@ function getLegsOfInterest(initialLegs, time, previousFinishedLeg) { isAnyLegPropertyIdentical(currentLeg, previousFinishedLeg, [ 'legId', 'legGeometry.points', - ]) + ]) && + itineraryStarted // prev and current are both undefined before itinerary starts ) { previousLeg = currentLeg; currentLeg = nextLeg; @@ -141,7 +146,7 @@ function getLegsOfInterest(initialLegs, time, previousFinishedLeg) { lastLeg: legs[legs.length - 1], previousLeg, currentLeg, - nextLeg: initialLegs.find(({ start }) => legTime(start) >= nextStart), + nextLeg: realTimeLegs.find(({ start }) => legTime(start) >= nextStart), }; } @@ -149,6 +154,7 @@ const useRealtimeLegs = (relayEnvironment, initialLegs = []) => { const [realTimeLegs, setRealTimeLegs] = useState(); const [time, setTime] = useState(Date.now()); const previousFinishedLeg = useRef(undefined); + const itineraryStarted = useRef(false); const origin = useMemo( () => GeodeticToEcef(initialLegs[0].from.lat, initialLegs[0].from.lon), @@ -235,10 +241,17 @@ const useRealtimeLegs = (relayEnvironment, initialLegs = []) => { }, [fetchAndSetRealtimeLegs]); const { firstLeg, lastLeg, currentLeg, nextLeg, previousLeg } = - getLegsOfInterest(realTimeLegs, time, previousFinishedLeg.current); + getLegsOfInterest( + realTimeLegs, + time, + previousFinishedLeg.current, + itineraryStarted.current, + ); previousFinishedLeg.current = previousLeg; - + if (currentLeg) { + itineraryStarted.current = true; + } // return wait legs as undefined as they are not a global concept return { realTimeLegs, diff --git a/app/component/itinerary/navigator/navigator.scss b/app/component/itinerary/navigator/navigator.scss index 482711ceaa..5f09e1c6f8 100644 --- a/app/component/itinerary/navigator/navigator.scss +++ b/app/component/itinerary/navigator/navigator.scss @@ -457,23 +457,16 @@ $fixed-width-padding: 16px; } } - .alt-btn { - display: flex; - flex-direction: column; - width: 100%; - - .show-options { - padding: var(--space-s, 16px) var(--space-xs, 8px) var(--space-s, 16px) - var(--space-s, 16px); - background: #0074bf; - color: #fff; - border-radius: 999px; // var(--radius-radius-medium, 8px); - margin-top: var(--space-xxs); - - /* box-shadow-card-s-strong */ - box-shadow: 0 2px 4px 0 - var(--color-shadow-strong, rgba(51, 51, 51, 0.2)); - } + .new-itinerary-search { + padding: var(--space-s, 16px) var(--space-xs, 8px) var(--space-s, 16px) + var(--space-s, 16px); + background: #0074bf; + color: #fff; + border-radius: 999px; // var(--radius-radius-medium, 8px); + margin-top: var(--space-xxs); + + /* box-shadow-card-s-strong */ + box-shadow: 0 2px 4px 0 var(--color-shadow-strong, rgba(51, 51, 51, 0.2)); } &.slide-out-right { diff --git a/app/component/itinerary/queries/LegQuery.js b/app/component/itinerary/queries/LegQuery.js index be6e8e4ceb..f70c7fbbed 100644 --- a/app/component/itinerary/queries/LegQuery.js +++ b/app/component/itinerary/queries/LegQuery.js @@ -30,6 +30,12 @@ const legQuery = graphql` time } } + stop { + gtfsId + lat + lon + name + } } to { vehicleRentalStation { diff --git a/app/util/otpStrings.js b/app/util/otpStrings.js index ed1e0ec639..50921edaae 100644 --- a/app/util/otpStrings.js +++ b/app/util/otpStrings.js @@ -52,7 +52,7 @@ export function locationToUri(location) { if (!location.lat) { return '-'; } - let address = location.address || ''; + let address = location.address || location.name || ''; if (location.gtfsId) { address = `${address}**${location.gtfsId}`; }