Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DT-6564 Navigator journey end modal #5187

Merged
merged 19 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/component/itinerary/ItineraryPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,6 @@ export default function ItineraryPage(props, context) {

const cancelNavigatorUsage = () => {
setNavigation(false);
toggleNavigatorIntro();
};

const walkPlan = altStates[PLANTYPE.WALK][0].plan;
Expand Down Expand Up @@ -1183,12 +1182,13 @@ export default function ItineraryPage(props, context) {
/>
)}
<NaviContainer
itinerary={itineraryForNavigator}
legs={itineraryForNavigator.legs}
focusToLeg={focusToLeg}
relayEnvironment={props.relayEnvironment}
setNavigation={setNavigation}
mapRef={mwtRef.current}
mapLayerRef={mapLayerRef}
isNavigatorIntroDismissed={isNavigatorIntroDismissed}
/>
</>
);
Expand Down
2 changes: 1 addition & 1 deletion app/component/itinerary/navigator/NaviBottom.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function NaviBottom(
) {
const remainingDuration = Math.ceil((arrival - time) / 60000); // ms to minutes
return (
<div className="navibottomsheet">
<div className="navi-bottom-sheet">
<div className="divider" />
<div className="navi-bottom-controls">
<button
Expand Down
88 changes: 48 additions & 40 deletions app/component/itinerary/navigator/NaviCardContainer.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,55 @@
import React, { useEffect, useState, useRef } from 'react';
import PropTypes from 'prop-types';
import { intlShape } from 'react-intl';
import distance from '@digitransit-search-util/digitransit-search-util-distance';
import { matchShape, routerShape } from 'found';
import { legShape, configShape } from '../../../util/shapes';
import PropTypes from 'prop-types';
import React, { useEffect, useRef, useState } from 'react';
import { intlShape } from 'react-intl';
import { legTime, legTimeStr } from '../../../util/legUtils';
import { configShape, legShape } from '../../../util/shapes';
import NaviCard from './NaviCard';
Antiik91 marked this conversation as resolved.
Show resolved Hide resolved
import NaviStack from './NaviStack';
import {
getItineraryAlerts,
getTransitLegState,
getAdditionalMessages,
getFirstLastLegs,
getItineraryAlerts,
getTransitLegState,
LEGTYPE,
} from './NaviUtils';

const DESTINATION_RADIUS = 20; // meters
const TIME_AT_DESTINATION = 3; // * 10 seconds
const TOPBAR_PADDING = 8; // pixels

function getNextLeg(legs, time) {
return legs.find(leg => legTime(leg.start) > time);
}

function addMessages(incominMessages, newMessages) {
newMessages.forEach(m => {
incominMessages.set(m.id, m);
});
}
function NaviCardContainer(
{ focusToLeg, time, legs, position, origin, mapLayerRef },
{
focusToLeg,
time,
legs,
position,
origin,
mapLayerRef,
currentLeg,
nextLeg,
firstLeg,
isJourneyCompleted,
},
{ intl, config, match, router },
) {
const [currentLeg, setCurrentLeg] = useState(null);
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 [topPosition, setTopPosition] = useState(0);

const legRef = useRef(currentLeg);
const focusRef = useRef(false);
// Destination counter. How long user has been at the destination. * 10 seconds
const destCountRef = useRef(0);
const [topPosition, setTopPosition] = useState(0);
const cardRef = useRef(null);

const handleRemove = index => {
Expand All @@ -61,38 +69,32 @@ function NaviCardContainer(
}, [currentLeg, cardExpanded]);

useEffect(() => {
const newLeg = legs.find(leg => {
return legTime(leg.start) <= time && time <= legTime(leg.end);
});

setCurrentLeg(newLeg);
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,
getItineraryAlerts(legs, intl, messages, match.params, router),
);

const legChanged = newLeg?.legId
? newLeg.legId !== currentLeg?.legId
: currentLeg?.mode !== newLeg?.mode;
const l = currentLeg || newLeg;

if (l) {
const nextLeg = getNextLeg(legs, legTime(l.start));

if (currentLeg) {
if (nextLeg?.transitLeg) {
// Messages for NaviStack.
addMessages(incomingMessages, [
...getTransitLegState(nextLeg, intl, messages, time),
...getAdditionalMessages(nextLeg, time, intl, config, messages),
]);
}
if (newLeg && legChanged) {
focusToLeg?.(newLeg);
}
if (legChanged) {
focusToLeg?.(currentLeg);
setCardExpanded(false);
}
}
Expand Down Expand Up @@ -120,8 +122,8 @@ function NaviCardContainer(

if (!focusRef.current && focusToLeg) {
// handle initial focus when not tracking
if (newLeg) {
focusToLeg(newLeg);
if (currentLeg) {
focusToLeg(currentLeg);
destCountRef.current = 0;
} else {
const { first, last } = getFirstLastLegs(legs);
Expand All @@ -147,12 +149,9 @@ function NaviCardContainer(
}
}, [time]);

const { first, last } = getFirstLastLegs(legs);
let legType;
const t = currentLeg ? legTime(currentLeg.start) : time;
const nextLeg = getNextLeg(legs, t);

if (time < legTime(first.start)) {
if (time < legTime(firstLeg.start)) {
legType = LEGTYPE.PENDING;
} else if (currentLeg) {
if (!currentLeg.transitLeg) {
Expand All @@ -164,8 +163,6 @@ function NaviCardContainer(
} else {
legType = LEGTYPE.TRANSIT;
}
} else if (time > legTime(last.end)) {
legType = LEGTYPE.END;
} else {
legType = LEGTYPE.WAIT;
}
Expand All @@ -174,7 +171,9 @@ function NaviCardContainer(
mapLayerRef.current.getBoundingClientRect().top + TOPBAR_PADDING;

return (
<>
<div
className={`navi-card-container ${isJourneyCompleted ? 'slide-out' : ''}`}
>
<button
type="button"
className={`navitop ${cardExpanded ? 'expanded' : ''}`}
Expand All @@ -188,7 +187,7 @@ function NaviCardContainer(
nextLeg={nextLeg}
cardExpanded={cardExpanded}
legType={legType}
startTime={legTimeStr(first.start)}
startTime={legTimeStr(firstLeg.start)}
time={time}
position={position}
origin={origin}
Expand All @@ -202,7 +201,7 @@ function NaviCardContainer(
topPosition={topPosition}
/>
)}
</>
</div>
);
}

Expand All @@ -219,6 +218,11 @@ NaviCardContainer.propTypes = {
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired,
}).isRequired,
currentLeg: legShape,
nextLeg: legShape,
firstLeg: legShape,
isJourneyCompleted: PropTypes.bool,

/*
focusToPoint: PropTypes.func.isRequired,
*/
Expand All @@ -227,6 +231,10 @@ NaviCardContainer.propTypes = {
NaviCardContainer.defaultProps = {
focusToLeg: undefined,
position: undefined,
currentLeg: undefined,
nextLeg: undefined,
firstLeg: undefined,
isJourneyCompleted: false,
};

NaviCardContainer.contextTypes = {
Expand Down
68 changes: 38 additions & 30 deletions app/component/itinerary/navigator/NaviContainer.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,35 @@
import distance from '@digitransit-search-util/digitransit-search-util-distance';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import polyUtil from 'polyline-encoded';
import { legTime } from '../../../util/legUtils';
import { checkPositioningPermission } from '../../../action/PositionActions';
import { GeodeticToEcef, GeodeticToEnu } from '../../../util/geo-utils';
import { itineraryShape, relayShape } from '../../../util/shapes';
import { legShape, relayShape } from '../../../util/shapes';
import NaviBottom from './NaviBottom';
import NaviCardContainer from './NaviCardContainer';
import { useRealtimeLegs } from './hooks/useRealtimeLegs';
import NavigatorOutroModal from './navigatoroutro/NavigatorOutroModal';

const DESTINATION_RADIUS = 20; // meters
const ADDITIONAL_ARRIVAL_TIME = 60000; // 60 seconds in ms

function NaviContainer(
{
itinerary,
legs,
focusToLeg,
relayEnvironment,
setNavigation,
isNavigatorIntroDismissed,
mapRef,
mapLayerRef,
},
{ getStore },
) {
const [planarLegs, setPlanarLegs] = useState([]);
const [origin, setOrigin] = useState();
const [isPositioningAllowed, setPositioningAllowed] = useState(false);

const position = getStore('PositionStore').getLocationState();

useEffect(() => {
const { lat, lon } = itinerary.legs[0].from;
const orig = GeodeticToEcef(lat, lon);
const legs = itinerary.legs.map(leg => {
const geometry = polyUtil.decode(leg.legGeometry.points);
return {
...leg,
geometry: geometry.map(p => GeodeticToEnu(p[0], p[1], orig)),
};
});
setPlanarLegs(legs);
setOrigin(orig);
}, [itinerary]);
const { realTimeLegs, time, origin, firstLeg, lastLeg, currentLeg, nextLeg } =
useRealtimeLegs(relayEnvironment, legs);

useEffect(() => {
if (position.hasLocation) {
Expand All @@ -54,20 +45,22 @@ function NaviContainer(
}
}, [mapRef]);

const { realTimeLegs, time } = useRealtimeLegs(
planarLegs,
mapRef,
relayEnvironment,
);

if (!realTimeLegs.length) {
if (!realTimeLegs?.length) {
return null;
}

const arrivalTime = legTime(realTimeLegs[realTimeLegs.length - 1].end);
partisaani marked this conversation as resolved.
Show resolved Hide resolved

const isDestinationReached =
position && lastLeg && distance(position, lastLeg.to) <= DESTINATION_RADIUS;

const isPastExpectedArrival = time > arrivalTime + ADDITIONAL_ARRIVAL_TIME;

const isJourneyCompleted = isDestinationReached || isPastExpectedArrival;

return (
<>
<NaviCardContainer
itinerary={itinerary}
legs={realTimeLegs}
focusToLeg={
mapRef?.state.mapTracking || isPositioningAllowed ? null : focusToLeg
Expand All @@ -76,21 +69,33 @@ function NaviContainer(
position={position}
mapLayerRef={mapLayerRef}
origin={origin}
currentLeg={currentLeg}
nextLeg={nextLeg}
firstLeg={firstLeg}
lastLeg={lastLeg}
isJourneyCompleted={isJourneyCompleted}
/>
{isJourneyCompleted && isNavigatorIntroDismissed && (
<NavigatorOutroModal
destination={realTimeLegs[realTimeLegs.length - 1].to.name}
onClose={() => setNavigation(false)}
/>
)}
<NaviBottom
setNavigation={setNavigation}
arrival={legTime(realTimeLegs[realTimeLegs.length - 1].end)}
arrival={arrivalTime}
time={time}
/>
</>
);
}

NaviContainer.propTypes = {
itinerary: itineraryShape.isRequired,
legs: PropTypes.arrayOf(legShape).isRequired,
focusToLeg: PropTypes.func.isRequired,
relayEnvironment: relayShape.isRequired,
setNavigation: PropTypes.func.isRequired,
isNavigatorIntroDismissed: PropTypes.bool,
// eslint-disable-next-line
mapRef: PropTypes.object,
mapLayerRef: PropTypes.func.isRequired,
Expand All @@ -100,6 +105,9 @@ NaviContainer.contextTypes = {
getStore: PropTypes.func.isRequired,
};

NaviContainer.defaultProps = { mapRef: undefined };
NaviContainer.defaultProps = {
mapRef: undefined,
isNavigatorIntroDismissed: false,
};

export default NaviContainer;
5 changes: 4 additions & 1 deletion app/component/itinerary/navigator/NaviInstructions.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ export default function NaviInstructions(
remainingTraversal = 1.0 - traversed;
} else {
// estimate from elapsed time
remainingTraversal = (legTime(leg.end) - time) / (leg.duration * 1000);
remainingTraversal = Math.max(
(legTime(leg.end) - time) / (leg.duration * 1000),
0,
);
}
const duration = leg.duration * remainingTraversal;
const distance = leg.distance * remainingTraversal;
Expand Down
Loading
Loading