Skip to content

Commit

Permalink
Merge pull request #5201 from HSLdevcom/DT-6607
Browse files Browse the repository at this point in the history
DT-6607 Prevent finished leg from reappearing as current
  • Loading branch information
vesameskanen authored Dec 20, 2024
2 parents c087597 + 69dfcb0 commit ec7fa53
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 20 deletions.
3 changes: 2 additions & 1 deletion app/component/itinerary/Itinerary.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
getTotalDistance,
legTime,
legTimeStr,
LegMode,
} from '../../util/legUtils';
import { dateOrEmpty, isTomorrow, timeStr } from '../../util/timeUtils';
import withBreakpoint from '../../util/withBreakpoint';
Expand Down Expand Up @@ -622,7 +623,7 @@ const Itinerary = (
renderModeIcons={renderModeIcons}
duration={waitingTimeinMin}
isTransitLeg={false}
mode="WAIT"
mode={LegMode.Wait}
large={breakpoint === 'large'}
icon={usingOwnCarWholeTrip ? 'icon-icon_wait-car' : undefined}
/>,
Expand Down
7 changes: 5 additions & 2 deletions app/component/itinerary/navigator/NaviInstructions.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ export default function NaviInstructions(
const hs = headsign || nextLeg.trip?.tripHeadsign;
const localizedMode = getLocalizedMode(mode, intl);

const remainingDuration = Math.ceil((legTime(start) - time) / 60000); // ms to minutes
const remainingDuration = Math.max(
Math.ceil((legTime(start) - time) / 60000),
0,
); // ms to minutes, >= 0
const rt = nextLeg.realtimeState === 'UPDATED';
const values = {
duration: withRealTime(rt, remainingDuration),
Expand Down Expand Up @@ -101,7 +104,7 @@ export default function NaviInstructions(
: intl.formatMessage({ id: 'navileg-from-stop' });
const localizedMode = getLocalizedMode(leg.mode, intl);

const remainingDuration = Math.ceil((t - time) / 60000); // ms to minutes
const remainingDuration = Math.max(Math.ceil((t - time) / 60000), 0); // ms to minutes, >= 0
const values = {
stopOrStation,
stop: leg.to.stop.name,
Expand Down
67 changes: 52 additions & 15 deletions app/component/itinerary/navigator/hooks/useRealtimeLegs.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import polyUtil from 'polyline-encoded';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { fetchQuery } from 'react-relay';
import { GeodeticToEcef, GeodeticToEnu } from '../../../../util/geo-utils';
import { legTime } from '../../../../util/legUtils';
import {
isAnyLegPropertyIdentical,
legTime,
LegMode,
} from '../../../../util/legUtils';
import { epochToIso } from '../../../../util/timeUtils';
import { legQuery } from '../../queries/LegQuery';

Expand Down Expand Up @@ -87,8 +91,8 @@ function matchLegEnds(legs) {
}
}

function getLegsOfInterest(legs, time) {
if (!legs?.length) {
function getLegsOfInterest(initialLegs, time, previousFinishedLeg) {
if (!initialLegs?.length) {
return {
firstLeg: undefined,
lastLeg: undefined,
Expand All @@ -97,17 +101,46 @@ function getLegsOfInterest(legs, time) {
};
}

const firstLeg = legs[0];
const lastLeg = legs[legs.length - 1];
const nextLeg = legs.find(({ start }) => legTime(start) > time);
const previousLeg = legs.findLast(({ end }) => legTime(end) < time);
const currentLeg = legs.find(
const legs = initialLegs.reduce((acc, curr, i, arr) => {
acc.push(curr);
const next = arr[i + 1];

// A wait leg is added, if next leg exists but it does not start when current ends
if (next && legTime(curr.end) !== legTime(next.start)) {
acc.push({
id: null,
legGeometry: { points: null },
mode: LegMode.Wait,
start: curr.end,
end: next.start,
});
}

return acc;
}, []);

const nextLegIdx = legs.findIndex(({ start }) => legTime(start) > time);
let currentLeg = legs.find(
({ start, end }) => legTime(start) <= time && legTime(end) >= time,
);
let previousLeg = legs.findLast(({ end }) => legTime(end) < time);
let nextLeg = legs[nextLegIdx];

// Indices are shifted by one if a previously completed leg reappears as current.
if (
isAnyLegPropertyIdentical(currentLeg, previousFinishedLeg, [
'legId',
'legGeometry.points',
])
) {
previousLeg = currentLeg;
currentLeg = nextLeg;
nextLeg = nextLegIdx !== -1 ? legs[nextLegIdx + 1] : undefined;
}

return {
firstLeg,
lastLeg,
firstLeg: legs[0],
lastLeg: legs[legs.length - 1],
previousLeg,
currentLeg,
nextLeg,
Expand All @@ -117,6 +150,7 @@ function getLegsOfInterest(legs, time) {
const useRealtimeLegs = (relayEnvironment, initialLegs = []) => {
const [realTimeLegs, setRealTimeLegs] = useState();
const [time, setTime] = useState(Date.now());
const previousFinishedLeg = useRef(undefined);

const origin = useMemo(
() => GeodeticToEcef(initialLegs[0].from.lat, initialLegs[0].from.lon),
Expand Down Expand Up @@ -200,17 +234,20 @@ const useRealtimeLegs = (relayEnvironment, initialLegs = []) => {
}, [fetchAndSetRealtimeLegs]);

const { firstLeg, lastLeg, currentLeg, nextLeg, previousLeg } =
getLegsOfInterest(realTimeLegs, time);
getLegsOfInterest(realTimeLegs, time, previousFinishedLeg.current);

previousFinishedLeg.current = previousLeg;

// return wait legs as undefined as they are not a global concept
return {
realTimeLegs,
time,
origin,
firstLeg,
lastLeg,
previousLeg,
currentLeg,
nextLeg,
previousLeg: previousLeg?.mode === LegMode.Wait ? undefined : previousLeg,
currentLeg: currentLeg?.mode === LegMode.Wait ? undefined : currentLeg,
nextLeg: nextLeg?.mode === LegMode.Wait ? undefined : nextLeg,
};
};

Expand Down
3 changes: 2 additions & 1 deletion app/component/map/ItineraryLine.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getInterliningLegs,
getLegText,
isCallAgencyLeg,
LegMode,
} from '../../util/legUtils';
import { getRouteMode } from '../../util/modeUtils';
import { configShape, legShape } from '../../util/shapes';
Expand Down Expand Up @@ -62,7 +63,7 @@ class ItineraryLine extends React.Component {
const transitLegs = [];

this.props.legs.forEach((leg, i) => {
if (!leg || leg.mode === 'WAIT') {
if (!leg || leg.mode === LegMode.Wait) {
return;
}
const nextLeg = this.props.legs[i + 1];
Expand Down
44 changes: 43 additions & 1 deletion app/util/legUtils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,48 @@
import cloneDeep from 'lodash/cloneDeep';
import get from 'lodash/get';
import { BIKEAVL_UNKNOWN } from './vehicleRentalUtils';
import { getRouteMode } from './modeUtils';
import { BIKEAVL_UNKNOWN } from './vehicleRentalUtils';

/**
* Gets a (nested) property value from an object
*
* @param {Object.<string, any>} obj object with properties i.e. { foo: 'bar', baz: {qux: 'quux'} }
* @param {string} propertyString string representation of object property i.e. foo, baz.qux
* @returns {Object}
*/
function getNestedValue(obj, propertyString) {
return propertyString.split('.').reduce((acc, part) => acc && acc[part], obj);
}

/**
* Compares if given legs share any of the given properties.
* Can be used to check if two separate leg objects are identical
* Returns true if both legs are null|undefined
*
* @param {Object.<string, any>|undefined} leg1
* @param {Object.<string, any>|undefined} leg2
* @param {string[]} properties list of object fields to compare i.e. ['foo', 'bar.baz']
* @returns {boolean}
*/
export function isAnyLegPropertyIdentical(leg1, leg2, properties) {
if (leg1 === leg2) {
return true;
}

if (!leg1 || !leg2) {
return false;
}

for (let i = 0; i < properties.length; i++) {
const property = properties[i];
const val1 = getNestedValue(leg1, property);
const val2 = getNestedValue(leg2, property);
if (val1 && val2 && val1 === val2) {
return true;
}
}
return false;
}

/**
* Get time as milliseconds since the Unix Epoch
Expand Down Expand Up @@ -81,6 +122,7 @@ export const LegMode = {
Walk: 'WALK',
Car: 'CAR',
Rail: 'RAIL',
Wait: 'WAIT',
};

/**
Expand Down

0 comments on commit ec7fa53

Please sign in to comment.