Skip to content

Commit

Permalink
Merge pull request #5146 from HSLdevcom/DT-6483
Browse files Browse the repository at this point in the history
DT-6483: Refactor top card
  • Loading branch information
vesameskanen authored Nov 8, 2024
2 parents c03dba6 + 115dfcc commit 9fa48ad
Show file tree
Hide file tree
Showing 13 changed files with 475 additions and 271 deletions.
60 changes: 60 additions & 0 deletions app/component/itinerary/NaviCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import PropTypes from 'prop-types';
import { legShape } from '../../util/shapes';
import Icon from '../Icon';
import { isRental } from '../../util/legUtils';
import NaviInstructions from './NaviInstructions';
import NaviCardExtension from './NaviCardExtension';

const iconMap = {
BICYCLE: 'icon-icon_cyclist',
CAR: 'icon-icon_car-withoutBox',
SCOOTER: 'icon-icon_scooter_rider',
WALK: 'icon-icon_walk',
WAIT: 'icon-icon_navigation_wait',
};

export default function NaviCard({ leg, nextLeg, legType, cardExpanded }) {
const iconName = legType === 'wait' ? iconMap.WAIT : iconMap[leg.mode];
let instructions = `navileg-${leg.mode.toLowerCase()}`;

if (isRental(leg, nextLeg)) {
if (leg.mode === 'WALK' && nextLeg?.mode === 'SCOOTER') {
instructions = `navileg-rent-scooter`;
} else {
instructions = `navileg-rent-cycle`;
}
}
return (
<div className="navi-top-card">
<div className="main-card">
<Icon img={iconName} color="black" className="mode" />
<div className="instructions">
<NaviInstructions
leg={leg}
nextLeg={nextLeg}
instructions={instructions}
legType={legType}
/>
<div type="button" className="navitop-arrow">
<Icon
img="icon-icon_arrow-collapse"
className={`cursor-pointer ${cardExpanded ? 'inverted' : ''}`}
/>
</div>
</div>
</div>
{cardExpanded && <NaviCardExtension leg={leg} />}
</div>
);
}

NaviCard.propTypes = {
leg: legShape.isRequired,
nextLeg: legShape.isRequired,
legType: PropTypes.string.isRequired,
cardExpanded: PropTypes.bool,
};
NaviCard.defaultProps = {
cardExpanded: false,
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import { FormattedMessage, intlShape } from 'react-intl';
import distance from '@digitransit-search-util/digitransit-search-util-distance';
import { legShape, configShape } from '../../util/shapes';
import { legTime, legTimeStr } from '../../util/legUtils';
import NaviLeg from './NaviLeg';
import Icon from '../Icon';
import NaviCard from './NaviCard';
import NaviStack from './NaviStack';
import {
getItineraryAlerts,
Expand All @@ -22,12 +21,12 @@ function getFirstLastLegs(legs) {
return { first, last };
}

function NaviTop(
function NaviCardContainer(
{ focusToLeg, time, realTimeLegs, position },
{ intl, config },
) {
const [currentLeg, setCurrentLeg] = useState(null);
const [showMessages, setShowMessages] = useState(true);
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.
Expand All @@ -37,17 +36,9 @@ function NaviTop(
const destCountRef = useRef(0);

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

useEffect(() => {
const timer = setTimeout(() => {
setShowMessages(false);
}, 5000);

return () => clearTimeout(timer);
}, []);

useEffect(() => {
const newLeg = realTimeLegs.find(leg => {
return legTime(leg.start) <= time && time <= legTime(leg.end);
Expand All @@ -68,21 +59,17 @@ function NaviTop(
const l = currentLeg || newLeg;

if (l) {
const nextTransitLeg = realTimeLegs.find(
leg => legTime(leg.start) > legTime(l.start) && leg.transitLeg,
const nextLeg = realTimeLegs.find(
leg => legTime(leg.start) > legTime(l.start),
);
if (nextTransitLeg) {
if (nextLeg?.transitLeg) {
// Messages for NaviStack.
const transitLegState = getTransitLegState(
nextTransitLeg,
intl,
messages,
);
const transitLegState = getTransitLegState(nextLeg, intl, messages);
if (transitLegState) {
incomingMessages.set(transitLegState.id, transitLegState);
}
const additionalMsgs = getAdditionalMessages(
nextTransitLeg,
nextLeg,
time,
intl,
config,
Expand All @@ -94,42 +81,50 @@ function NaviTop(
});
}
}

if (legChanged) {
if (newLeg) {
focusToLeg?.(newLeg);
setCurrentLeg(newLeg);
}

if (incomingMessages.size || legChanged) {
// Handle messages when new messages arrives or leg is changed.

// Current active messages. Filter away legChange messages when leg changes.
const currActiveMessages = legChanged
? activeMessages.filter(m => m.expiresOn !== 'legChange')
: activeMessages;

const newMessages = Array.from(incomingMessages.values());
setActiveMessages([...currActiveMessages, ...newMessages]);
setMessages(new Map([...messages, ...incomingMessages]));

setShowMessages(true);
if (legChanged) {
setCurrentLeg(newLeg);
setCardExpanded(false);
}
}
if (incomingMessages.size || legChanged) {
// Handle messages when new messages arrives or leg is changed.

// Current active messages. Filter away legChange messages when leg changes.
const previousValidMessages = legChanged
? activeMessages.filter(m => m.expiresOn !== 'legChange')
: activeMessages;

// handle messages that are updated.
const updatedMessages = previousValidMessages.map(msg => {
const incoming = incomingMessages.get(msg.id);
if (incoming) {
incomingMessages.delete(msg.id);
return incoming;
}
return msg;
});
const newMessages = Array.from(incomingMessages.values());
setActiveMessages([...updatedMessages, ...newMessages]);
setMessages(new Map([...messages, ...incomingMessages]));
}

if (!focusRef.current && focusToLeg) {
// handle initial focus when not tracking
if (newLeg) {
focusToLeg(newLeg);
destCountRef.current = 0;
if (!focusRef.current && focusToLeg) {
// handle initial focus when not tracking
if (newLeg) {
focusToLeg(newLeg);
destCountRef.current = 0;
} else {
const { first, last } = getFirstLastLegs(realTimeLegs);
if (time < legTime(first.start)) {
focusToLeg(first);
} else {
const { first, last } = getFirstLastLegs(realTimeLegs);
if (time < legTime(first.start)) {
focusToLeg(first);
} else {
focusToLeg(last);
}
focusToLeg(last);
}
focusRef.current = true;
}
focusRef.current = true;
}

// User position and distance from currentleg endpoint.
Expand Down Expand Up @@ -160,16 +155,20 @@ function NaviTop(
const nextLeg = realTimeLegs.find(leg => {
return legTime(leg.start) > legTime(currentLeg.start);
});
let legType;
if (destCountRef.current >= TIME_AT_DESTINATION) {
// User at the destination. show wait message.
naviTopContent = (
<NaviLeg leg={currentLeg} nextLeg={nextLeg} legType="wait" />
);
legType = 'wait';
} else {
naviTopContent = (
<NaviLeg leg={currentLeg} nextLeg={nextLeg} legType="move" />
);
legType = 'move';
}
naviTopContent = (
<NaviCard
leg={currentLeg}
nextLeg={nextLeg}
cardExpanded={cardExpanded}
legType={legType}
/>
);
} else {
naviTopContent = `Tracking ${currentLeg?.mode} leg`;
}
Expand All @@ -182,33 +181,27 @@ function NaviTop(
setActiveMessages(activeMessages.filter((_, i) => i !== index));
};

const showmessages = activeMessages.length > 0;
return (
<>
<button type="button" className="navitop" onClick={handleClick}>
<button
type="button"
className={`navitop ${cardExpanded ? 'expanded' : ''}`}
onClick={handleClick}
>
<div className="content">{naviTopContent}</div>
<div type="button" className="navitop-arrow">
{showmessages && (
<Icon
img="icon-icon_arrow-collapse"
className={`cursor-pointer ${showMessages ? 'inverted' : ''}`}
color={config.colors.primary}
/>
)}
</div>
</button>
{showmessages && (
{activeMessages.length > 0 && (
<NaviStack
messages={activeMessages}
show={showMessages}
cardExpanded={cardExpanded}
handleRemove={handleRemove}
/>
)}
</>
);
}

NaviTop.propTypes = {
NaviCardContainer.propTypes = {
focusToLeg: PropTypes.func,
time: PropTypes.number.isRequired,
realTimeLegs: PropTypes.arrayOf(legShape).isRequired,
Expand All @@ -222,14 +215,14 @@ NaviTop.propTypes = {
*/
};

NaviTop.defaultProps = {
NaviCardContainer.defaultProps = {
focusToLeg: undefined,
position: undefined,
};

NaviTop.contextTypes = {
NaviCardContainer.contextTypes = {
intl: intlShape.isRequired,
config: configShape.isRequired,
};

export default NaviTop;
export default NaviCardContainer;
68 changes: 68 additions & 0 deletions app/component/itinerary/NaviCardExtension.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import Icon from '../Icon';
import StopCode from '../StopCode';
import PlatformNumber from '../PlatformNumber';
import { getZoneLabel } from '../../util/legUtils';
import ZoneIcon from '../ZoneIcon';
import { legShape, configShape } from '../../util/shapes';
import { getDestinationProperties } from './NaviUtils';

const NaviCardExtension = ({ leg }, { config }) => {
const { stop, name } = leg.to;
const { code, platformCode, zoneId, vehicleMode } = stop || {};
const [place, address] = name?.split(/, (.+)/) || [];

let destination = {};
if (stop) {
destination = getDestinationProperties(leg, stop, config);
destination.name = stop.name;
} else {
destination.iconId = 'icon-icon_mapMarker-to';
destination.className = 'place';
destination.name = place;
}
return (
<div className="secondary-info">
<div className="secondary-divider" />
<div className="secondary-content">
<Icon img="navi-expand" className="icon-expand" />
<Icon
img={destination.iconId}
height={2}
width={2}
className={`destination-icon ${destination.className}`}
/>
<div className="destination">
{destination.name}
<div className="details">
{!stop && address && <div className="address">{address}</div>}
{code && <StopCode code={code} />}
{platformCode && (
<PlatformNumber
number={platformCode}
short
isRailOrSubway={
vehicleMode === 'RAIL' || vehicleMode === 'SUBWAY'
}
/>
)}
<ZoneIcon
zoneId={getZoneLabel(zoneId, config)}
showUnknown={false}
/>
</div>
</div>
</div>
</div>
);
};

NaviCardExtension.propTypes = {
leg: legShape.isRequired,
};

NaviCardExtension.contextTypes = {
config: configShape.isRequired,
};

export default NaviCardExtension;
4 changes: 2 additions & 2 deletions app/component/itinerary/NaviContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useEffect, useState, useRef } from 'react';
import PropTypes from 'prop-types';
import { graphql, fetchQuery } from 'react-relay';
import { itineraryShape, relayShape } from '../../util/shapes';
import NaviTop from './NaviTop';
import NaviCardContainer from './NaviCardContainer';
import NaviBottom from './NaviBottom';
import { legTime } from '../../util/legUtils';
import { checkPositioningPermission } from '../../action/PositionActions';
Expand Down Expand Up @@ -119,7 +119,7 @@ function NaviContainer(

return (
<>
<NaviTop
<NaviCardContainer
itinerary={itinerary}
realTimeLegs={realTimeLegs}
focusToLeg={
Expand Down
Loading

0 comments on commit 9fa48ad

Please sign in to comment.