Skip to content

Commit

Permalink
Merge pull request #5242 from HSLdevcom/DT-6586
Browse files Browse the repository at this point in the history
DT-6586 Manual start for Navigator journey
  • Loading branch information
vesameskanen authored Jan 30, 2025
2 parents ac22624 + 2cedfc7 commit 8dc44ca
Show file tree
Hide file tree
Showing 12 changed files with 382 additions and 149 deletions.
169 changes: 83 additions & 86 deletions app/component/itinerary/navigator/NaviCard.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import PropTypes from 'prop-types';
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { isRental } from '../../../util/legUtils';
import React, { useState } from 'react';
import { isAnyLegPropertyIdentical, isRental } from '../../../util/legUtils';
import { getRouteMode } from '../../../util/modeUtils';
import { configShape, legShape } from '../../../util/shapes';
import Icon from '../../Icon';
import NaviCardExtension from './NaviCardExtension';
import NaviInstructions from './NaviInstructions';
import { LEGTYPE } from './NaviUtils';
import usePrevious from './hooks/usePrevious';

const iconMap = {
BICYCLE: 'icon-icon_cyclist',
Expand All @@ -27,104 +27,103 @@ const iconMap = {
};

export default function NaviCard(
{
leg,
nextLeg,
legType,
cardExpanded,
startTime,
time,
position,
tailLength,
},
{ leg, nextLeg, legType, time, position, tailLength },
{ config },
) {
let mainCardContent;
if (legType === LEGTYPE.PENDING) {
mainCardContent = (
<FormattedMessage
id="navigation-journey-start"
values={{ time: startTime }}
/>
);
} else if (legType === LEGTYPE.END) {
mainCardContent = <FormattedMessage id="navigation-journey-end" />;
} else if (!leg && !nextLeg) {
const [cardExpanded, setCardExpanded] = useState(false);
const { isEqual: legChanged } = usePrevious(leg, (prev, current) =>
isAnyLegPropertyIdentical(prev, current, ['legId', 'mode']),
);
const handleClick = () => {
setCardExpanded(!cardExpanded);
};

if (legChanged) {
setCardExpanded(false);
}

if (
(!leg && !nextLeg) ||
legType === LEGTYPE.PENDING ||
legType === LEGTYPE.END
) {
return null;
} else {
let iconColor = 'currentColor';
let iconName;
let instructions = '';
if (legType === LEGTYPE.TRANSIT) {
const m = getRouteMode(leg.route, config);
iconColor = config.colors.iconColors[`mode-${m}`] || leg.route.color;
iconName = iconMap[m.toUpperCase()];
}

instructions = `navileg-in-transit`;
} else if (
legType !== LEGTYPE.WAIT &&
legType !== LEGTYPE.WAIT_IN_VEHICLE &&
isRental(leg, nextLeg)
) {
if (leg.mode === 'WALK' && nextLeg?.mode === 'SCOOTER') {
instructions = `navileg-rent-scooter`;
} else {
instructions = 'rent-cycle-at';
}
iconName = iconMap[leg.mode];
} else if (legType === LEGTYPE.MOVE) {
instructions = `navileg-${leg?.mode.toLowerCase()}`;
iconName = iconMap.WALK;
} else if (legType === LEGTYPE.WAIT) {
iconName = iconMap.WAIT;
} else if (legType === LEGTYPE.WAIT_IN_VEHICLE) {
iconName = iconMap.WAIT_IN_VEHICLE;
let iconColor = 'currentColor';
let iconName;
let instructions = '';

if (legType === LEGTYPE.TRANSIT) {
const m = getRouteMode(leg.route, config);
iconColor = config.colors.iconColors[`mode-${m}`] || leg.route.color;
iconName = iconMap[m.toUpperCase()];

instructions = `navileg-in-transit`;
} else if (
legType !== LEGTYPE.WAIT &&
legType !== LEGTYPE.WAIT_IN_VEHICLE &&
isRental(leg, nextLeg)
) {
if (leg.mode === 'WALK' && nextLeg?.mode === 'SCOOTER') {
instructions = `navileg-rent-scooter`;
} else {
instructions = 'rent-cycle-at';
}
iconName = iconMap[leg.mode];
} else if (legType === LEGTYPE.MOVE) {
instructions = `navileg-${leg?.mode.toLowerCase()}`;
iconName = iconMap.WALK;
} else if (legType === LEGTYPE.WAIT) {
iconName = iconMap.WAIT;
} else if (legType === LEGTYPE.WAIT_IN_VEHICLE) {
iconName = iconMap.WAIT_IN_VEHICLE;
}

mainCardContent = (
<>
<Icon img={iconName} className="mode" color={iconColor} />
<div className={`instructions ${cardExpanded ? 'expanded' : ''}`}>
<NaviInstructions
return (
<button
type="button"
className={`navi-top-card ${cardExpanded ? 'expanded' : ''}`}
onClick={handleClick}
>
<div className="main-card">
<div className="content">
<Icon img={iconName} className="mode" color={iconColor} />
<div className={`instructions ${cardExpanded ? 'expanded' : ''}`}>
<NaviInstructions
leg={leg}
nextLeg={nextLeg}
instructions={instructions}
legType={legType}
time={time}
position={position}
tailLength={tailLength}
/>
</div>
<div type="button" className="navi-top-card-arrow">
<Icon
img="icon-icon_arrow-collapse"
className={`cursor-pointer ${cardExpanded ? 'inverted' : ''}`}
/>
</div>
</div>
{cardExpanded && (
<NaviCardExtension
legType={legType}
leg={leg}
nextLeg={nextLeg}
instructions={instructions}
legType={legType}
time={time}
position={position}
tailLength={tailLength}
/>
</div>
<div type="button" className="navi-top-card-arrow">
<Icon
img="icon-icon_arrow-collapse"
className={`cursor-pointer ${cardExpanded ? 'inverted' : ''}`}
/>
</div>
</>
);
}
return (
<div className="main-card">
<div className="content">{mainCardContent}</div>
{cardExpanded && (
<NaviCardExtension
legType={legType}
leg={leg}
nextLeg={nextLeg}
time={time}
/>
)}
</div>
)}
</div>
</button>
);
}

NaviCard.propTypes = {
leg: legShape,
nextLeg: legShape,
legType: PropTypes.string.isRequired,
cardExpanded: PropTypes.bool,
startTime: PropTypes.string,
time: PropTypes.number.isRequired,
position: PropTypes.shape({
lat: PropTypes.number,
Expand All @@ -133,10 +132,8 @@ NaviCard.propTypes = {
tailLength: PropTypes.number.isRequired,
};
NaviCard.defaultProps = {
cardExpanded: false,
leg: undefined,
nextLeg: undefined,
startTime: '',
position: undefined,
};

Expand Down
60 changes: 29 additions & 31 deletions app/component/itinerary/navigator/NaviCardContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,33 @@ import { matchShape, routerShape } from 'found';
import PropTypes from 'prop-types';
import React, { useEffect, useRef, useState } from 'react';
import { intlShape } from 'react-intl';
import { legTime, legTimeStr } from '../../../util/legUtils';
import {
isAnyLegPropertyIdentical,
legTime,
legTimeStr,
} from '../../../util/legUtils';
import { configShape, legShape } from '../../../util/shapes';
import { getTopics, updateClient } from '../ItineraryPageUtils';
import NaviCard from './NaviCard';
import NaviStack from './NaviStack';
import NaviStarter from './NaviStarter';
import {
DESTINATION_RADIUS,
getAdditionalMessages,
getItineraryAlerts,
getTransitLegState,
itinerarySearchPath,
LEGTYPE,
DESTINATION_RADIUS,
} from './NaviUtils';
import { updateClient, getTopics } from '../ItineraryPageUtils';
import usePrevious from './hooks/usePrevious';

const COUNT_AT_LEG_END = 2; // update cycles within DESTINATION_RADIUS from leg.to
const TOPBAR_PADDING = 8; // pixels
const HIDE_TOPCARD_DURATION = 2000; // milliseconds

function addMessages(incominMessages, newMessages) {
function addMessages(incomingMessages, newMessages) {
newMessages.forEach(m => {
incominMessages.set(m.id, m);
incomingMessages.set(m.id, m);
});
}

Expand All @@ -35,7 +41,7 @@ const getLegType = (
interlineWithPreviousLeg,
) => {
let legType;
if (time < legTime(firstLeg.start)) {
if (!firstLeg.forceStart && time < legTime(firstLeg.start)) {
legType = LEGTYPE.PENDING;
} else if (leg) {
if (!leg.transitLeg) {
Expand Down Expand Up @@ -67,31 +73,29 @@ function NaviCardContainer(
lastLeg,
previousLeg,
isJourneyCompleted,
startItinerary,
},
context,
) {
const [cardExpanded, setCardExpanded] = useState(false);
// All notifications including those user has dismissed.
const [messages, setMessages] = useState(new Map());
// notifications that are shown to the user.
const [activeMessages, setActiveMessages] = useState([]);
const [legChanging, setLegChanging] = useState(false);
const legRef = useRef(currentLeg);
const { isEqual: legChanged } = usePrevious(currentLeg, (prev, current) =>
isAnyLegPropertyIdentical(prev, current, ['legId', 'mode']),
);
const { isEqual: forceStart } = usePrevious(firstLeg?.forceStart);
const focusRef = useRef(false);
// Destination counter. How long user has been at the destination. * 10 seconds
const legEndRef = useRef(0);
const cardRef = useRef(null);
const { intl, config, match, router } = context;
const handleRemove = index => {
const msg = messages.get(activeMessages[index].id);
msg.closed = true; // remember closing action
setActiveMessages(activeMessages.filter((_, i) => i !== index));
};

const handleClick = () => {
setCardExpanded(!cardExpanded);
};

// track only relevant vehicles for the journey.
const getNaviTopics = () =>
getTopics(
Expand All @@ -117,13 +121,6 @@ function NaviCardContainer(
useEffect(() => {
const incomingMessages = new Map();

const legChanged = legRef.current?.legId
? legRef.current.legId !== currentLeg?.legId
: legRef.current?.mode !== currentLeg?.mode;
if (legChanged) {
legRef.current = currentLeg;
}

// Alerts for NaviStack
addMessages(
incomingMessages,
Expand Down Expand Up @@ -177,9 +174,8 @@ function NaviCardContainer(
]);
}
let timeoutId;
if (legChanged) {
if (legChanged || forceStart) {
updateClient(getNaviTopics(), context);
setCardExpanded(false);
setLegChanging(true);
timeoutId = setTimeout(() => {
setLegChanging(false);
Expand Down Expand Up @@ -232,7 +228,7 @@ function NaviCardContainer(
}

return () => clearTimeout(timeoutId);
}, [time]);
}, [time, firstLeg]);

// LegChange fires animation, we need to keep the old data until card goes out of the view.
const l = legChanging ? previousLeg : currentLeg;
Expand All @@ -259,23 +255,23 @@ function NaviCardContainer(
className={`navi-card-container ${className}`}
style={{ top: containerTopPosition }}
>
<button
type="button"
className={`navi-top-card ${cardExpanded ? 'expanded' : ''}`}
onClick={handleClick}
ref={cardRef}
>
{(!firstLeg.forceStart && time < legTime(firstLeg.start)) ||
(firstLeg.forceStart && time < legTime(firstLeg.start) && legChanging) ? (
<NaviStarter
time={legTimeStr(firstLeg.start)}
startItinerary={startItinerary}
/>
) : (
<NaviCard
leg={l}
nextLeg={nextLeg}
cardExpanded={cardExpanded}
legType={legType}
startTime={legTimeStr(firstLeg.start)}
time={time}
position={position}
tailLength={tailLength}
/>
</button>
)}
{activeMessages.length > 0 && (
<NaviStack messages={activeMessages} handleRemove={handleRemove} />
)}
Expand Down Expand Up @@ -303,6 +299,7 @@ NaviCardContainer.propTypes = {
lastLeg: legShape,
previousLeg: legShape,
isJourneyCompleted: PropTypes.bool,
startItinerary: PropTypes.func,
};

NaviCardContainer.defaultProps = {
Expand All @@ -314,6 +311,7 @@ NaviCardContainer.defaultProps = {
lastLeg: undefined,
previousLeg: undefined,
isJourneyCompleted: false,
startItinerary: () => {},
};

NaviCardContainer.contextTypes = {
Expand Down
2 changes: 2 additions & 0 deletions app/component/itinerary/navigator/NaviContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function NaviContainer(
previousLeg,
currentLeg,
nextLeg,
startItinerary,
} = useRealtimeLegs(relayEnvironment, legs, position);

useEffect(() => {
Expand Down Expand Up @@ -106,6 +107,7 @@ function NaviContainer(
lastLeg={lastLeg}
isJourneyCompleted={isJourneyCompleted}
previousLeg={previousLeg}
startItinerary={startItinerary}
/>
{isJourneyCompleted && isNavigatorIntroDismissed && (
<NavigatorOutroModal
Expand Down
Loading

0 comments on commit 8dc44ca

Please sign in to comment.