Skip to content

Commit

Permalink
Merge pull request #5216 from HSLdevcom/reroute-from-stop
Browse files Browse the repository at this point in the history
Proper rerouting logic and misc improvements
  • Loading branch information
partisaani authored Dec 30, 2024
2 parents 00f5859 + 3960390 commit fe48e46
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 93 deletions.
18 changes: 14 additions & 4 deletions app/component/itinerary/navigator/NaviCardContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
getAdditionalMessages,
getItineraryAlerts,
getTransitLegState,
itinerarySearchPath,
LEGTYPE,
DESTINATION_RADIUS,
} from './NaviUtils';
Expand Down Expand Up @@ -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);
}, []);
Expand All @@ -116,8 +128,7 @@ function NaviCardContainer(
origin,
intl,
messages,
match.params,
router,
makeNewItinerarySearch,
),
);

Expand Down Expand Up @@ -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;
}
Expand Down
148 changes: 85 additions & 63 deletions app/component/itinerary/navigator/NaviUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -330,28 +334,60 @@ 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 (
<div className="navi-alert-content">
{children}
<FormattedMessage id="navigation-abort-trip" />
<button
className="new-itinerary-search"
type="button"
onClick={searchCallback}
>
<FormattedMessage id="settings-dropdown-open-label" />
</button>
</div>
);
}

function alertId(alert) {
return `${alert.effectiveStartDate}-${alert.alertDescriptionText}`;
}

export const getItineraryAlerts = (
legs,
time,
position,
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) {
Expand All @@ -372,47 +408,36 @@ export const getItineraryAlerts = (
<span className="header"> {alert.alertHeaderText}</span>
</div>
),
id: `${alert.effectiveStartDate}-${alert.alertDescriptionText}`,
id: alertId(alert),
}));
});
const abortTrip = <FormattedMessage id="navigation-abort-trip" />;
const withShowRoutesBtn = children => (
<div className="alt-btn">
{children}
<button
className="show-options"
type="button"
onClick={() => router.push(getItineraryPagePath('POS', location.to))}
>
<FormattedMessage id="settings-dropdown-open-label" />
</button>
</div>

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 = (
<FormattedMessage
id="navigation-mode-canceled"
values={{ mode: routeName }}
/>
);
// we want to show the show routes button only for the first canceled leg.
if (i === 0) {
content = withShowRoutesBtn(
<div className="navi-alert-content">
{m}
{abortTrip}
</div>,
const content =
i === 0 ? (
withNewSearchBtn({ m }, itinerarySearchCallback)
) : (
<div className="navi-alert-content">{m}</div>
);
} else {
content = <div className="navi-alert-content">{m}</div>;
}

if (!messages.get(`canceled-${legId}`)) {
alerts.push({
severity: 'ALERT',
Expand All @@ -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(
<div className="navi-alert-content">
<FormattedMessage
id="navigation-transfer-problem"
values={{
route1: prob.fromLeg.route.shortName,
route2: prob.toLeg.route.shortName,
}}
/>
{abortTrip}
</div>,
);
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(
<FormattedMessage
id="navigation-transfer-problem"
values={{
route1: prob.fromLeg.route.shortName,
route2: prob.toLeg.route.shortName,
}}
/>,
itinerarySearchCallback,
),
id: transferId,
hideClose: prob.severity === 'ALERT',
});
}
}
}
return alerts;
Expand Down
29 changes: 21 additions & 8 deletions app/component/itinerary/navigator/hooks/useRealtimeLegs.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,21 @@ 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,
currentLeg: undefined,
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];

Expand Down Expand Up @@ -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;
Expand All @@ -141,14 +146,15 @@ 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),
};
}

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),
Expand Down Expand Up @@ -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,
Expand Down
27 changes: 10 additions & 17 deletions app/component/itinerary/navigator/navigator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions app/component/itinerary/queries/LegQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ const legQuery = graphql`
time
}
}
stop {
gtfsId
lat
lon
name
}
}
to {
vehicleRentalStation {
Expand Down
2 changes: 1 addition & 1 deletion app/util/otpStrings.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
}
Expand Down

0 comments on commit fe48e46

Please sign in to comment.