From 579e39d9b769bc13169d976e2d16a944705c06bf Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 30 Mar 2020 15:11:31 -0400 Subject: [PATCH 01/31] fix(index.css): Remove unused reference --- lib/index.css | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/index.css b/lib/index.css index 0fe0005bc..d768385b0 100644 --- a/lib/index.css +++ b/lib/index.css @@ -11,7 +11,6 @@ @import url(lib/components/form/form.css); @import url(lib/components/narrative/narrative.css); @import url(lib/components/narrative/default/itinerary.css); -@import url(lib/components/narrative/line-itin/itinerary.css); @import url(lib/components/mobile/mobile.css); @import url(lib/components/viewers/viewers.css); From fe4c15d83e396db876e155dc06b31589390ad040 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 30 Mar 2020 15:23:58 -0400 Subject: [PATCH 02/31] build(package.json): Restore bowser dependency. --- package.json | 1 + yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/package.json b/package.json index bd6f79ddd..f6e318863 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@opentripplanner/location-field": "^0.0.18", "@opentripplanner/trip-form": "^0.0.18", "bootstrap": "^3.3.7", + "bowser": "^1.9.3", "clone": "^2.1.0", "connected-react-router": "^6.5.2", "copy-to-clipboard": "^3.0.8", diff --git a/yarn.lock b/yarn.lock index 6d7ac4463..09112550a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3393,6 +3393,11 @@ bottleneck@^2.18.1: resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.4.tgz#63c505687a0ddaf89a6f515225c75e05833bb079" integrity sha512-2poBdvpAGG+dkMVKZqtDhyuMN6JviD81h89W4bfmt3UO7O60F+qf/84V0alNqL8PM1RByl4SZ1fVMu/ZvxkmcA== +bowser@^1.9.3: + version "1.9.4" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a" + integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ== + bowser@^2.7.0: version "2.9.0" resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.9.0.tgz#3bed854233b419b9a7422d9ee3e85504373821c9" From ae22643fb1e9ff39922cd10195960ba4327ee282 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 2 Apr 2020 19:29:33 -0400 Subject: [PATCH 03/31] refactor(itinerary): Migrate uses of itinerary.getIcon() to OTP-ui icons package. --- lib/components/app/print-layout.js | 1 - .../narrative/line-itin/itin-summary.js | 109 ------------------ .../narrative/line-itin/line-itinerary.js | 77 ------------- lib/components/narrative/mode-icon.js | 30 ----- .../narrative/narrative-profile-summary.js | 6 +- lib/index.js | 2 - lib/util/itinerary.js | 60 ---------- 7 files changed, 4 insertions(+), 281 deletions(-) delete mode 100644 lib/components/narrative/line-itin/itin-summary.js delete mode 100644 lib/components/narrative/line-itin/line-itinerary.js delete mode 100644 lib/components/narrative/mode-icon.js diff --git a/lib/components/app/print-layout.js b/lib/components/app/print-layout.js index 81e987672..b90771cb1 100644 --- a/lib/components/app/print-layout.js +++ b/lib/components/app/print-layout.js @@ -8,7 +8,6 @@ import React, { Component } from 'react' import { connect } from 'react-redux' import { Button } from 'react-bootstrap' -import TripDetails from '../narrative/connected-trip-details' import { parseUrlQueryString } from '../../actions/form' import { routingQuery } from '../../actions/api' import { getActiveItinerary } from '../../util/state' diff --git a/lib/components/narrative/line-itin/itin-summary.js b/lib/components/narrative/line-itin/itin-summary.js deleted file mode 100644 index e8965fe35..000000000 --- a/lib/components/narrative/line-itin/itin-summary.js +++ /dev/null @@ -1,109 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' - -import { calculateFares, calculatePhysicalActivity, getLegIcon, isTransit } from '../../../util/itinerary' -import { formatDuration, formatTime } from '../../../util/time' - -// TODO: make this a prop -const defaultRouteColor = '#008' - -export default class ItinerarySummary extends Component { - static propTypes = { - itinerary: PropTypes.object - } - - _onSummaryClicked = () => { - if (typeof this.props.onClick === 'function') this.props.onClick() - } - - render () { - const { customIcons, itinerary, timeOptions } = this.props - const { - centsToString, - maxTNCFare, - minTNCFare, - transitFare - } = calculateFares(itinerary) - // TODO: support non-USD - const minTotalFare = minTNCFare * 100 + transitFare - const maxTotalFare = maxTNCFare * 100 + transitFare - - const { caloriesBurned } = calculatePhysicalActivity(itinerary) - - return ( -
-
- {/* Travel time in hrs/mins */} -
{formatDuration(itinerary.duration)}
- - {/* Duration as time range */} -
- {formatTime(itinerary.startTime, timeOptions)} - {formatTime(itinerary.endTime, timeOptions)} -
- - {/* Fare / Calories */} -
- {minTotalFare > 0 && - {centsToString(minTotalFare)} - {minTotalFare !== maxTotalFare && - {centsToString(maxTotalFare)}} - - } - {Math.round(caloriesBurned)} Cals -
- - {/* Number of transfers, if applicable */} - {itinerary.transfers > 0 && ( -
- {itinerary.transfers} transfer{itinerary.transfers > 1 ? 's' : ''} -
- )} - -
-
- {itinerary.legs.filter(leg => { - return !(leg.mode === 'WALK' && itinerary.transitTime > 0) - }).map((leg, k) => { - return
-
{getLegIcon(leg, customIcons)}
- {isTransit(leg.mode) - ? ( -
- {getRouteNameForBadge(leg)} -
- ) - : (
) - } -
- })} -
-
- ) - } -} - -// Helper functions - -function getRouteLongName (leg) { - return leg.routes && leg.routes.length > 0 - ? leg.routes[0].longName - : leg.routeLongName -} - -function getRouteNameForBadge (leg) { - const shortName = leg.routes && leg.routes.length > 0 - ? leg.routes[0].shortName : leg.routeShortName - - const longName = getRouteLongName(leg) - - // check for max - if (longName && longName.toLowerCase().startsWith('max')) return null - - // check for streetcar - if (longName && longName.startsWith('Portland Streetcar')) return longName.split('-')[1].trim().split(' ')[0] - - return shortName || longName -} - -function getRouteColorForBadge (leg) { - return leg.routeColor ? '#' + leg.routeColor : defaultRouteColor -} diff --git a/lib/components/narrative/line-itin/line-itinerary.js b/lib/components/narrative/line-itin/line-itinerary.js deleted file mode 100644 index 2ca69e267..000000000 --- a/lib/components/narrative/line-itin/line-itinerary.js +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react' - -import NarrativeItinerary from '../narrative-itinerary' -import SimpleRealtimeAnnotation from '../simple-realtime-annotation' -import { getLegModeLabel, getTimeZoneOffset, isTransit } from '../../../util/itinerary' - -import ItinerarySummary from './itin-summary' -import ItineraryBody from './connected-itinerary-body' - -export default class LineItinerary extends NarrativeItinerary { - _headerText () { - const { itinerary } = this.props - return itinerary.summary || this._getSummary(itinerary) - } - - _getSummary (itinerary) { - let summary = '' - let transitModes = [] - itinerary.legs.forEach((leg, index) => { - if (isTransit(leg.mode)) { - const modeStr = getLegModeLabel(leg) - if (transitModes.indexOf(modeStr) === -1) transitModes.push(modeStr) - } - }) - - // check for access mode - if (!isTransit(itinerary.legs[0].mode)) { - summary += getLegModeLabel(itinerary.legs[0]) - } - - // append transit modes, if applicable - if (transitModes.length > 0) { - summary += ' to ' + transitModes.join(', ') - } - - return summary - } - - render () { - const { - active, - companies, - customIcons, - expanded, - itinerary, - itineraryFooter, - showRealtimeAnnotation, - onClick, - timeFormat - } = this.props - - if (!itinerary) { - return
No Itinerary!
- } - - const timeOptions = { - format: timeFormat, - offset: getTimeZoneOffset(itinerary) - } - - return ( -
- - {showRealtimeAnnotation && } - {active || expanded - ? - : null} - {itineraryFooter} -
- ) - } -} diff --git a/lib/components/narrative/mode-icon.js b/lib/components/narrative/mode-icon.js deleted file mode 100644 index 453c1188e..000000000 --- a/lib/components/narrative/mode-icon.js +++ /dev/null @@ -1,30 +0,0 @@ -import Icon from './icon' -import React, { Component } from 'react' -import PropTypes from 'prop-types' - -export default class ModeIcon extends Component { - static propTypes = { - mode: PropTypes.string - } - render () { - const { mode, defaultToText } = this.props - switch (mode) { - case 'BICYCLE': - return - case 'BUS': - return - case 'CAR': - return - case 'TRAM': - return - case 'SUBWAY': - return - case 'WALK': - return - case 'MICROMOBILITY': - return - default: - return defaultToText ? {mode} : null - } - } -} diff --git a/lib/components/narrative/narrative-profile-summary.js b/lib/components/narrative/narrative-profile-summary.js index cf8db6c6e..c6a0fcee3 100644 --- a/lib/components/narrative/narrative-profile-summary.js +++ b/lib/components/narrative/narrative-profile-summary.js @@ -1,7 +1,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' +import { ModeIcon } from '@opentripplanner/trip-form/lib/ModeIcon' // TODO: export this to OTP-ui package root. -import { getIcon } from '../../util/itinerary' export default class NarrativeProfileSummary extends Component { static propTypes = { @@ -63,7 +63,9 @@ export default class NarrativeProfileSummary extends Component { textAlign: 'center', color: 'white' }} > -
{getIcon(option.icon, this.props.customIcons)}
+
+ +
{option.title}
{option.time > 0 diff --git a/lib/index.js b/lib/index.js index ad1bb1d41..8f8bdd6ae 100644 --- a/lib/index.js +++ b/lib/index.js @@ -24,7 +24,6 @@ import SimpleRealtimeAnnotation from './components/narrative/simple-realtime-ann import TransportationNetworkCompanyLeg from './components/narrative/default/tnc-leg' import TripDetails from './components/narrative/connected-trip-details' import TripTools from './components/narrative/trip-tools' -import LineItinerary from './components/narrative/line-itin/line-itinerary' import MobileMain from './components/mobile/main' @@ -68,7 +67,6 @@ export { // narrative components LegDiagramPreview, - LineItinerary, NarrativeItineraries, NarrativeItinerary, NarrativeProfileOptions, diff --git a/lib/util/itinerary.js b/lib/util/itinerary.js index df4196484..4b468599d 100644 --- a/lib/util/itinerary.js +++ b/lib/util/itinerary.js @@ -192,31 +192,6 @@ export function getLegModeLabel (leg) { return toSentenceCase(leg.mode) } -/** - * Returns a react element of the desired icon. If customIcons are defined, then - * the icon will be attempted to be used from that lookup of icons. Otherwise, - * a ModeIcon element will be returned. - * - * @param {string} iconId A string with the desired icon ID. This icon can - * include modes or companies or anything that is defined in the customIcons. - * @param {[Map]} customIcons A customized lookup of - * icons. These are defined as part of the implementing webapp. If this lookup - * is not defined, then the ModeIcon class will be used instead. - * @return {React.Element} - */ -export function getIcon (iconId, customIcons) { - // Check if there is a custom icon - if (customIcons && iconId in customIcons) { - return customIcons[iconId] - } - - // Custom icon not available for the given iconId. Use the ModeIcon component - // to show the icon based on the iconId, but always use the default car icon - // for any car-based modes that didn't have custom icon - if (iconId && iconId.startsWith('CAR')) iconId = 'CAR' - return -} - export function getItineraryBounds (itinerary) { let coords = [] itinerary.legs.forEach(leg => { @@ -599,41 +574,6 @@ export function toSentenceCase (str) { return str.charAt(0).toUpperCase() + str.substr(1).toLowerCase() } -/** - * Return an icon depending on the leg info - * - * @param {Object} leg The leg data of an itinerary in an OTP trip plan result - * @param {[Object]} customIcons If defined for this webapp, the custom icons - * consist of a lookup table of icons to return for a specific icon ID. These - * icons typically show either companies or transport modes, but they could show - * other icons too. See this file in trimet-mod-otp for an example setup: - * https://github.com/ibi-group/trimet-mod-otp/blob/6a32e2142655c4f4d09a3f349b971b7505e2866a/lib/icons/index.js#L24-L55 - */ -export function getLegIcon (leg, customIcons) { - // check if a custom function exists for determining the icon for a leg - if (customIcons && typeof customIcons.customIconForLeg === 'function') { - // function exits, get the icon string lookup. It's possible for there to be - // a custom function that only returns a string for when a leg meets the - // criteria of the custom function - const customIconStr = customIcons.customIconForLeg(leg) - // the customIconStr could be undefined for this leg, but if it is not, then - // immediately return this custom icon for the leg - if (customIconStr) return getIcon(customIconStr, customIcons) - } - let iconStr = leg.mode - if (iconStr === 'CAR' && leg.rentedCar) { - iconStr = leg.from.networks[0] - } else if (iconStr === 'CAR' && leg.tncData) { - iconStr = leg.tncData.company - } else if (iconStr === 'BICYCLE' && leg.rentedBike && leg.from.networks) { - iconStr = leg.from.networks[0] - } else if (iconStr === 'MICROMOBILITY' && leg.rentedVehicle && leg.from.networks) { - iconStr = leg.from.networks[0] - } - - return getIcon(iconStr, customIcons) -} - /** * Get the configured company object for the given network string if the company * has been defined in the provided companies array config. From 462611ed8943d1f10be1e7fa157ee0ad03f61169 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 2 Apr 2020 19:59:25 -0400 Subject: [PATCH 04/31] refactor: Remove profile-related components. Replace ModeIcon on OTP-rr with TriMetLegIcon. --- lib/components/icons/mode-icon.js | 129 ------------------ .../narrative/default/itinerary-summary.js | 6 +- .../narrative/default/transit-leg.js | 4 +- .../narrative/narrative-profile-options.js | 86 ------------ .../narrative/narrative-profile-summary.js | 82 ----------- .../narrative/narrative-routing-results.js | 60 -------- lib/util/itinerary.js | 3 - 7 files changed, 6 insertions(+), 364 deletions(-) delete mode 100644 lib/components/icons/mode-icon.js delete mode 100644 lib/components/narrative/narrative-profile-options.js delete mode 100644 lib/components/narrative/narrative-profile-summary.js delete mode 100644 lib/components/narrative/narrative-routing-results.js diff --git a/lib/components/icons/mode-icon.js b/lib/components/icons/mode-icon.js deleted file mode 100644 index dbbd75b54..000000000 --- a/lib/components/icons/mode-icon.js +++ /dev/null @@ -1,129 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' - -export default class ModeIcon extends Component { - static propTypes = { - mode: PropTypes.string - } - - render () { - if (!this.props.mode) return null - switch (this.props.mode.toLowerCase()) { - case 'bus': return - case 'tram': - case 'rail': - case 'subway': - return - case 'walk': return - case 'bicycle': - case 'bicycle_rent': - return - case 'ferry': return - case 'gondola': return - case 'car': return - case 'micromobility': - case 'micromobility_rent': - return - } - return null - } -} - -class BusIcon extends Component { - render () { - return ( - - - - - - - - - ) - } -} - -class TramIcon extends Component { - render () { - return ( - - - - - - ) - } -} - -class WalkIcon extends Component { - render () { - return ( - - - - - ) - } -} - -class BicycleIcon extends Component { - render () { - return ( - - - - ) - } -} - -class FerryIcon extends Component { - render () { - return ( - - - - - - - ) - } -} - -class GondolaIcon extends Component { - render () { - return ( - - - - - - - ) - } -} - -class CarIcon extends Component { - render () { - return ( - - - - ) - } -} - -/** - * Icons made by Freepik (https://www.freepik.com/) - * from Flaticon (https://www.flaticon.com/) - * licensed by Creative Commons BY 3.0 (http://creativecommons.org/licenses/by/3.0/) - */ -class MicromobilityIcon extends Component { - render () { - return ( - - - - ) - } -} diff --git a/lib/components/narrative/default/itinerary-summary.js b/lib/components/narrative/default/itinerary-summary.js index d93ff3cfb..ed24b33a4 100644 --- a/lib/components/narrative/default/itinerary-summary.js +++ b/lib/components/narrative/default/itinerary-summary.js @@ -1,7 +1,9 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import ModeIcon from '../../icons/mode-icon' +import TriMetLegIcon from '@opentripplanner/icons/lib/trimet-leg-icon' + +// TODO: add the custom icons hook found in print-layout. export default class ItinerarySummary extends Component { static propTypes = { @@ -26,7 +28,7 @@ export default class ItinerarySummary extends Component { // Add the mode icon blocks.push(
- +
) diff --git a/lib/components/narrative/default/transit-leg.js b/lib/components/narrative/default/transit-leg.js index 5809f3f1b..b4007b6bf 100644 --- a/lib/components/narrative/default/transit-leg.js +++ b/lib/components/narrative/default/transit-leg.js @@ -1,8 +1,8 @@ import Icon from '../icon' import React, { Component } from 'react' import PropTypes from 'prop-types' +import TriMetLegIcon from '@opentripplanner/icons/lib/trimet-leg-icon' -import ModeIcon from '../../icons/mode-icon' import ViewTripButton from '../../viewers/view-trip-button' import ViewStopButton from '../../viewers/view-stop-button' @@ -46,7 +46,7 @@ export default class TransitLeg extends Component { onClick={(e) => this._onLegClick(e, leg, index)} >
- +
diff --git a/lib/components/narrative/narrative-profile-options.js b/lib/components/narrative/narrative-profile-options.js deleted file mode 100644 index 405d6609a..000000000 --- a/lib/components/narrative/narrative-profile-options.js +++ /dev/null @@ -1,86 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' - -import { setActiveItinerary, setActiveLeg, setActiveStep } from '../../actions/narrative' -import DefaultItinerary from './default/default-itinerary' -import NarrativeProfileSummary from './narrative-profile-summary' -import Loading from './loading' -import { getActiveSearch } from '../../util/state' -import { profileOptionsToItineraries } from '../../util/profile' - -class NarrativeProfileOptions extends Component { - static propTypes = { - options: PropTypes.array, - query: PropTypes.object, - itineraryClass: PropTypes.func, - pending: PropTypes.bool, - activeOption: PropTypes.number, - setActiveItinerary: PropTypes.func, - setActiveLeg: PropTypes.func, - setActiveStep: PropTypes.func, - customIcons: PropTypes.object - } - - static defaultProps = { - itineraryClass: DefaultItinerary - } - - render () { - const { pending, itineraryClass, query, activeItinerary } = this.props - if (pending) return - - const options = this.props.options - if (!options) return null - - const itineraries = profileOptionsToItineraries(options, query) - - return ( -
-
Your best options:
- -
We found {options.length} total options:
- {itineraries.map((itinerary, index) => { - return React.createElement(itineraryClass, { - itinerary, - index, - key: index, - active: index === activeItinerary, - routingType: 'PROFILE', - ...this.props - }) - })} -
- ) - } -} - -// connect to the redux store -const mapStateToProps = (state, ownProps) => { - const activeSearch = getActiveSearch(state.otp) - // const { activeItinerary, activeLeg, activeStep } = activeSearch ? activeSearch.activeItinerary : {} - const pending = activeSearch && activeSearch.pending - return { - options: - activeSearch && - activeSearch.response && - activeSearch.response.otp - ? activeSearch.response.otp.profile - : null, - pending, - activeItinerary: activeSearch && activeSearch.activeItinerary, - activeLeg: activeSearch && activeSearch.activeLeg, - activeStep: activeSearch && activeSearch.activeStep, - query: activeSearch && activeSearch.query - } -} - -const mapDispatchToProps = (dispatch, ownProps) => { - return { - setActiveItinerary: (index) => { dispatch(setActiveItinerary({ index })) }, - setActiveLeg: (index, leg) => { dispatch(setActiveLeg({ index, leg })) }, - setActiveStep: (index, step) => { dispatch(setActiveStep({ index, step })) } - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(NarrativeProfileOptions) diff --git a/lib/components/narrative/narrative-profile-summary.js b/lib/components/narrative/narrative-profile-summary.js deleted file mode 100644 index c6a0fcee3..000000000 --- a/lib/components/narrative/narrative-profile-summary.js +++ /dev/null @@ -1,82 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { ModeIcon } from '@opentripplanner/trip-form/lib/ModeIcon' // TODO: export this to OTP-ui package root. - - -export default class NarrativeProfileSummary extends Component { - static propTypes = { - options: PropTypes.array, - customIcons: PropTypes.object - } - - render () { - const { options } = this.props - - let bestTransit = 0 - let walk = 0 - let bicycle = 0 - let bicycleRent = 0 - - options.forEach((option, i) => { - if (option.transit) { - if (option.time < bestTransit || bestTransit === 0) { - bestTransit = option.time - } - } else { - if (option.modes.length === 1 && option.modes[0] === 'bicycle') bicycle = option.time - else if (option.modes.length === 1 && option.modes[0] === 'walk') walk = option.time - else if (option.modes.indexOf('bicycle_rent') !== -1) bicycleRent = option.time - } - }) - - const summary = [ - { - icon: 'BUS', - title: 'Transit', - time: bestTransit - }, { - icon: 'BICYCLE', - title: 'Bicycle', - time: bicycle - }, { - icon: 'BICYCLE_RENT', - title: 'Bikeshare', - time: bicycleRent - }, { - icon: 'WALK', - title: 'Walk', - time: walk - } - ] - - return ( -
- {summary.map((option, k) => { - return ( -
0 ? '#084C8D' : '#bbb', - width: '22%', - display: 'inline-block', - verticalAlign: 'top', - marginRight: (k < 3 ? '4%' : 0), - padding: '3px', - textAlign: 'center', - color: 'white' }} - > -
- -
-
{option.title}
-
- {option.time > 0 - ? {Math.round(option.time / 60)} min - : (Not Found) - } -
-
- ) - })} -
- ) - } -} diff --git a/lib/components/narrative/narrative-routing-results.js b/lib/components/narrative/narrative-routing-results.js deleted file mode 100644 index f7f407420..000000000 --- a/lib/components/narrative/narrative-routing-results.js +++ /dev/null @@ -1,60 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' - -import Loading from './loading' -import NarrativeProfileOptions from './narrative-profile-options' -import TabbedItineraries from './tabbed-itineraries' -import ErrorMessage from '../form/error-message' - -import { getActiveSearch, getActiveItineraries } from '../../util/state' -import { setMainPanelContent } from '../../actions/ui' - -class NarrativeRoutingResults extends Component { - static propTypes = { - customIcons: PropTypes.object, - itineraryClass: PropTypes.func, - routingType: PropTypes.string - } - - componentDidUpdate (prevProps) { - if ((!prevProps.itineraries || prevProps.itineraries.length === 0) && - (this.props.itineraries && this.props.itineraries.length > 0)) { - this.props.setMainPanelContent(null) - } - if (!prevProps.error && this.props.error) this.props.setMainPanelContent(null) - } - - render () { - const { customIcons, error, itineraryClass, itineraryFooter, pending, routingType, itineraries, mainPanelContent } = this.props - if (pending) return - if (error) return - if (mainPanelContent) return null - - return ( - routingType === 'ITINERARY' - ? - : - ) - } -} - -const mapStateToProps = (state, ownProps) => { - const activeSearch = getActiveSearch(state.otp) - return { - mainPanelContent: state.otp.ui.mainPanelContent, - error: activeSearch && activeSearch.response && activeSearch.response.error, - itineraries: getActiveItineraries(state.otp), - pending: activeSearch && activeSearch.pending, - routingType: activeSearch && activeSearch.query.routingType - } -} - -const mapDispatchToProps = { - setMainPanelContent -} - -export default connect(mapStateToProps, mapDispatchToProps)(NarrativeRoutingResults) diff --git a/lib/util/itinerary.js b/lib/util/itinerary.js index 4b468599d..77fb9fe3a 100644 --- a/lib/util/itinerary.js +++ b/lib/util/itinerary.js @@ -1,10 +1,7 @@ -import React from 'react' import { latLngBounds } from 'leaflet' import polyline from '@mapbox/polyline' import turfAlong from '@turf/along' -import ModeIcon from '../components/icons/mode-icon' - // All OTP transit modes export const transitModes = ['TRAM', 'BUS', 'SUBWAY', 'FERRY', 'RAIL', 'GONDOLA'] From d05337fa622c2bc8897de3f27b2673d58f404810 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 3 Apr 2020 10:47:33 -0400 Subject: [PATCH 05/31] refactor(icons): Remove lib/components/icons and use OTP-ui icons package instead. --- lib/components/form/default-search-form.js | 14 +++- lib/components/icons/bike-icon.js | 19 ----- lib/components/icons/biketown-icon.js | 18 ---- lib/components/icons/bus-icon.js | 23 ------ lib/components/icons/car2go-icon.js | 16 ---- lib/components/icons/direction-icon.js | 82 ------------------- lib/components/icons/gondola-icon.js | 12 --- lib/components/icons/index.js | 36 -------- lib/components/icons/lyft-icon.js | 11 --- lib/components/icons/rail-icon.js | 11 --- lib/components/icons/reachnow-icon.js | 14 ---- lib/components/icons/streetcar-icon.js | 12 --- lib/components/icons/tram-icon.js | 12 --- lib/components/icons/transit-icon.js | 23 ------ lib/components/icons/uber-icon.js | 14 ---- lib/components/icons/walk-icon.js | 14 ---- .../narrative/itinerary-carousel.js | 35 ++------ .../narrative/narrative-routing-results.js | 55 +++++++++++++ lib/index.js | 2 - 19 files changed, 72 insertions(+), 351 deletions(-) delete mode 100644 lib/components/icons/bike-icon.js delete mode 100644 lib/components/icons/biketown-icon.js delete mode 100644 lib/components/icons/bus-icon.js delete mode 100644 lib/components/icons/car2go-icon.js delete mode 100644 lib/components/icons/direction-icon.js delete mode 100644 lib/components/icons/gondola-icon.js delete mode 100644 lib/components/icons/index.js delete mode 100644 lib/components/icons/lyft-icon.js delete mode 100644 lib/components/icons/rail-icon.js delete mode 100644 lib/components/icons/reachnow-icon.js delete mode 100644 lib/components/icons/streetcar-icon.js delete mode 100644 lib/components/icons/tram-icon.js delete mode 100644 lib/components/icons/transit-icon.js delete mode 100644 lib/components/icons/uber-icon.js delete mode 100644 lib/components/icons/walk-icon.js create mode 100644 lib/components/narrative/narrative-routing-results.js diff --git a/lib/components/form/default-search-form.js b/lib/components/form/default-search-form.js index 1f2578231..31df598d8 100644 --- a/lib/components/form/default-search-form.js +++ b/lib/components/form/default-search-form.js @@ -1,10 +1,16 @@ -import React, { Component } from 'react' +import TriMetModeIcon from '@opentripplanner/icons/lib/trimet-mode-icon' import PropTypes from 'prop-types' +import React, { Component } from 'react' import LocationField from './connected-location-field' -import SwitchButton from './switch-button' import TabbedFormPanel from './tabbed-form-panel' -import defaultIcons from '../icons' +import SwitchButton from './switch-button' + +// As of OTP-ui 0.0.18, use the icons props +// to override icons for certain modes in the mode selector panel. +const customIcons = { + TRANSIT: +} export default class DefaultSearchForm extends Component { static propTypes = { @@ -13,7 +19,7 @@ export default class DefaultSearchForm extends Component { } static defaultProps = { - icons: defaultIcons, + icons: customIcons, showFrom: true, showTo: true } diff --git a/lib/components/icons/bike-icon.js b/lib/components/icons/bike-icon.js deleted file mode 100644 index c97d0de98..000000000 --- a/lib/components/icons/bike-icon.js +++ /dev/null @@ -1,19 +0,0 @@ -import React, { Component } from 'react' - -export default class BikeIcon extends Component { - render () { - return ( - - - - - - - - - - - - ) - } -} diff --git a/lib/components/icons/biketown-icon.js b/lib/components/icons/biketown-icon.js deleted file mode 100644 index 50fcefac7..000000000 --- a/lib/components/icons/biketown-icon.js +++ /dev/null @@ -1,18 +0,0 @@ -import React, { Component } from 'react' - -export default class BiketownIcon extends Component { - render () { - return ( - - - - - - - - - - - ) - } -} diff --git a/lib/components/icons/bus-icon.js b/lib/components/icons/bus-icon.js deleted file mode 100644 index 64bbcaf71..000000000 --- a/lib/components/icons/bus-icon.js +++ /dev/null @@ -1,23 +0,0 @@ -import React, { Component } from 'react' - -export default class BusIcon extends Component { - render () { - return ( - - - - - - - - - - - - - - - - ) - } -} diff --git a/lib/components/icons/car2go-icon.js b/lib/components/icons/car2go-icon.js deleted file mode 100644 index fe4c01f3d..000000000 --- a/lib/components/icons/car2go-icon.js +++ /dev/null @@ -1,16 +0,0 @@ -import React, { Component } from 'react' - -export default class Car2goIcon extends Component { - render () { - return ( - > - - - - - - - - ) - } -} diff --git a/lib/components/icons/direction-icon.js b/lib/components/icons/direction-icon.js deleted file mode 100644 index 3203d1e1c..000000000 --- a/lib/components/icons/direction-icon.js +++ /dev/null @@ -1,82 +0,0 @@ -import React, { Component } from 'react' - -export default class DirectionIcon extends Component { - render () { - const { relativeDirection } = this.props - if (!relativeDirection) return null - switch (relativeDirection.toUpperCase()) { - case 'DEPART': - case 'CONTINUE': return ( - - - - ) - case 'LEFT': return ( - - - - ) - case 'RIGHT': return ( - - - - ) - case 'SLIGHTLY_LEFT': return ( - - - - ) - case 'SLIGHTLY_RIGHT': return ( - - - - ) - case 'HARD_LEFT': return ( - - - - ) - case 'HARD_RIGHT': return ( - - - - ) - case 'UTURN_LEFT': return ( - - - - ) - case 'UTURN_RIGHT': return ( - - - - ) - case 'CIRCLE_CLOCKWISE': return ( - - - - - - - - - ) - case 'CIRCLE_COUNTERCLOCKWISE': return ( - - - - - - - - - ) - case 'ELEVATOR': return ( - - - - ) - } - return null - } -} diff --git a/lib/components/icons/gondola-icon.js b/lib/components/icons/gondola-icon.js deleted file mode 100644 index d14bcb744..000000000 --- a/lib/components/icons/gondola-icon.js +++ /dev/null @@ -1,12 +0,0 @@ -import React, { Component } from 'react' - -export default class GondolaIcon extends Component { - render () { - return ( - - - - - ) - } -} diff --git a/lib/components/icons/index.js b/lib/components/icons/index.js deleted file mode 100644 index 8bdf35517..000000000 --- a/lib/components/icons/index.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' - -import BikeIcon from './bike-icon' -import BiketownIcon from './biketown-icon' -import BusIcon from './bus-icon' -import Car2goIcon from './car2go-icon' -import ReachNowIcon from './reachnow-icon' -import GondolaIcon from './gondola-icon' -import LyftIcon from './lyft-icon' -import RailIcon from './rail-icon' -import StreetcarIcon from './streetcar-icon' -import TramIcon from './tram-icon' -import TransitIcon from './transit-icon' -import UberIcon from './uber-icon' -import WalkIcon from './walk-icon' - -// define Portland-specific mode icons -export default { - BICYCLE: , - BICYCLE_RENT: , - BUS: , - CAR_HAIL_LYFT: , - CAR_HAIL_UBER: , - CAR_RENT_CAR2GO: , - CAR_RENT_REACHNOW: , - GONDOLA: , - RAIL: , - STREETCAR: , - TRAM: , - TRANSIT: , - WALK: , - customModeForLeg: (leg) => { - if (leg.routeLongName && leg.routeLongName.startsWith('Portland Streetcar')) return 'STREETCAR' - return null - } -} diff --git a/lib/components/icons/lyft-icon.js b/lib/components/icons/lyft-icon.js deleted file mode 100644 index add6801bf..000000000 --- a/lib/components/icons/lyft-icon.js +++ /dev/null @@ -1,11 +0,0 @@ -import React, { Component } from 'react' - -export default class LyftIcon extends Component { - render () { - return ( - - - - ) - } -} diff --git a/lib/components/icons/rail-icon.js b/lib/components/icons/rail-icon.js deleted file mode 100644 index 8f2ab99d6..000000000 --- a/lib/components/icons/rail-icon.js +++ /dev/null @@ -1,11 +0,0 @@ -import React, { Component } from 'react' - -export default class RailIcon extends Component { - render () { - return ( - - - - ) - } -} diff --git a/lib/components/icons/reachnow-icon.js b/lib/components/icons/reachnow-icon.js deleted file mode 100644 index bab628729..000000000 --- a/lib/components/icons/reachnow-icon.js +++ /dev/null @@ -1,14 +0,0 @@ -import React, { Component } from 'react' - -export default class ReachNowIcon extends Component { - render () { - return ( - - - - - - - ) - } -} diff --git a/lib/components/icons/streetcar-icon.js b/lib/components/icons/streetcar-icon.js deleted file mode 100644 index e53550142..000000000 --- a/lib/components/icons/streetcar-icon.js +++ /dev/null @@ -1,12 +0,0 @@ -import React, { Component } from 'react' - -export default class StreetcarIcon extends Component { - render () { - return ( - - - - - ) - } -} diff --git a/lib/components/icons/tram-icon.js b/lib/components/icons/tram-icon.js deleted file mode 100644 index 10e69e931..000000000 --- a/lib/components/icons/tram-icon.js +++ /dev/null @@ -1,12 +0,0 @@ -import React, { Component } from 'react' - -export default class TramIcon extends Component { - render () { - return ( - - - - - ) - } -} diff --git a/lib/components/icons/transit-icon.js b/lib/components/icons/transit-icon.js deleted file mode 100644 index d2e96487c..000000000 --- a/lib/components/icons/transit-icon.js +++ /dev/null @@ -1,23 +0,0 @@ -import React, { Component } from 'react' - -export default class TransitIcon extends Component { - render () { - // TODO: Find a better general transit icon to use than the bus icon. - return ( - - - - - - - - - - - - - - - ) - } -} diff --git a/lib/components/icons/uber-icon.js b/lib/components/icons/uber-icon.js deleted file mode 100644 index 77daea91a..000000000 --- a/lib/components/icons/uber-icon.js +++ /dev/null @@ -1,14 +0,0 @@ -import React, { Component } from 'react' - -export default class UberIcon extends Component { - render () { - return ( - - - - - - - ) - } -} diff --git a/lib/components/icons/walk-icon.js b/lib/components/icons/walk-icon.js deleted file mode 100644 index a071dcfa4..000000000 --- a/lib/components/icons/walk-icon.js +++ /dev/null @@ -1,14 +0,0 @@ -import React, { Component } from 'react' - -export default class WalkIcon extends Component { - render () { - return ( - - - - - - - ) - } -} diff --git a/lib/components/narrative/itinerary-carousel.js b/lib/components/narrative/itinerary-carousel.js index ce7a373fa..c560362bd 100644 --- a/lib/components/narrative/itinerary-carousel.js +++ b/lib/components/narrative/itinerary-carousel.js @@ -8,9 +8,7 @@ import { setActiveItinerary, setActiveLeg, setActiveStep } from '../../actions/n import Icon from './icon' import DefaultItinerary from './default/default-itinerary' import Loading from './loading' -import NarrativeProfileSummary from './narrative-profile-summary' import { getActiveItineraries, getActiveSearch } from '../../util/state' -import { profileOptionsToItineraries } from '../../util/profile' import { getTimeFormat } from '../../util/time' class ItineraryCarousel extends Component { @@ -26,8 +24,6 @@ class ItineraryCarousel extends Component { setActiveLeg: PropTypes.func, setActiveStep: PropTypes.func, expanded: PropTypes.bool, - showProfileSummary: PropTypes.bool, - profileOptions: PropTypes.array, companies: PropTypes.string } @@ -54,18 +50,11 @@ class ItineraryCarousel extends Component { } render () { - const { activeItinerary, itineraries, itineraryClass, hideHeader, pending, showProfileSummary } = this.props + const { activeItinerary, itineraries, itineraryClass, hideHeader, pending } = this.props if (pending) return if (!itineraries) return null - let views = [] - if (showProfileSummary) { - views.push(
-
Your Best Options (Swipe to View All)
- -
) - } - views = views.concat(itineraries.map((itinerary, index) => { + const views = itineraries.map((itinerary, index) => { return React.createElement(itineraryClass, { itinerary, index, @@ -74,7 +63,7 @@ class ItineraryCarousel extends Component { onClick: this._onItineraryClick, ...this.props }) - })) + }) return (
@@ -112,23 +101,13 @@ class ItineraryCarousel extends Component { const mapStateToProps = (state, ownProps) => { const activeSearch = getActiveSearch(state.otp) - let itineraries = null - let profileOptions = null - let showProfileSummary = false - if (activeSearch && activeSearch.response && activeSearch.response.plan) { - itineraries = getActiveItineraries(state.otp) - } else if (activeSearch && activeSearch.response && activeSearch.response.otp) { - profileOptions = activeSearch.response.otp.profile - itineraries = profileOptionsToItineraries(profileOptions) - showProfileSummary = true - } + const itineraries = activeSearch && activeSearch.response && activeSearch.response.plan + ? getActiveItineraries(state.otp) + : null - const pending = activeSearch && activeSearch.pending return { itineraries, - profileOptions, - pending, - showProfileSummary, + pending: activeSearch && activeSearch.pending, activeItinerary: activeSearch && activeSearch.activeItinerary, activeLeg: activeSearch && activeSearch.activeLeg, activeStep: activeSearch && activeSearch.activeStep, diff --git a/lib/components/narrative/narrative-routing-results.js b/lib/components/narrative/narrative-routing-results.js new file mode 100644 index 000000000..2047d4cfb --- /dev/null +++ b/lib/components/narrative/narrative-routing-results.js @@ -0,0 +1,55 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' + +import Loading from './loading' +import TabbedItineraries from './tabbed-itineraries' +import ErrorMessage from '../form/error-message' + +import { getActiveSearch, getActiveItineraries } from '../../util/state' +import { setMainPanelContent } from '../../actions/ui' + +class NarrativeRoutingResults extends Component { + static propTypes = { + customIcons: PropTypes.object, + itineraryClass: PropTypes.func, + routingType: PropTypes.string + } + + componentDidUpdate (prevProps) { + if ((!prevProps.itineraries || prevProps.itineraries.length === 0) && + (this.props.itineraries && this.props.itineraries.length > 0)) { + this.props.setMainPanelContent(null) + } + if (!prevProps.error && this.props.error) this.props.setMainPanelContent(null) + } + + render () { + const { customIcons, error, itineraryClass, itineraryFooter, pending, routingType, itineraries, mainPanelContent } = this.props + if (pending) return + if (error) return + if (mainPanelContent) return null + + return ( + routingType === 'ITINERARY' && + + ) + } +} + +const mapStateToProps = (state, ownProps) => { + const activeSearch = getActiveSearch(state.otp) + return { + mainPanelContent: state.otp.ui.mainPanelContent, + error: activeSearch && activeSearch.response && activeSearch.response.error, + itineraries: getActiveItineraries(state.otp), + pending: activeSearch && activeSearch.pending, + routingType: activeSearch && activeSearch.query.routingType + } +} + +const mapDispatchToProps = { + setMainPanelContent +} + +export default connect(mapStateToProps, mapDispatchToProps)(NarrativeRoutingResults) diff --git a/lib/index.js b/lib/index.js index 8f8bdd6ae..5fb277044 100644 --- a/lib/index.js +++ b/lib/index.js @@ -16,7 +16,6 @@ import TileOverlay from './components/map/tile-overlay' import ItineraryCarousel from './components/narrative/itinerary-carousel' import LegDiagramPreview from './components/narrative/leg-diagram-preview' import NarrativeItineraries from './components/narrative/narrative-itineraries' -import NarrativeProfileOptions from './components/narrative/narrative-profile-options' import NarrativeItinerary from './components/narrative/narrative-itinerary' import NarrativeRoutingResults from './components/narrative/narrative-routing-results' import RealtimeAnnotation from './components/narrative/realtime-annotation' @@ -69,7 +68,6 @@ export { LegDiagramPreview, NarrativeItineraries, NarrativeItinerary, - NarrativeProfileOptions, NarrativeRoutingResults, RealtimeAnnotation, SimpleRealtimeAnnotation, From 36430381a9cabb26d2d8b3307770e1f61d452391 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 3 Apr 2020 10:52:54 -0400 Subject: [PATCH 06/31] docs(default-search-form): Tweak comments. --- lib/components/form/default-search-form.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/components/form/default-search-form.js b/lib/components/form/default-search-form.js index 31df598d8..299c19ef5 100644 --- a/lib/components/form/default-search-form.js +++ b/lib/components/form/default-search-form.js @@ -6,8 +6,8 @@ import LocationField from './connected-location-field' import TabbedFormPanel from './tabbed-form-panel' import SwitchButton from './switch-button' -// As of OTP-ui 0.0.18, use the icons props -// to override icons for certain modes in the mode selector panel. +// Use the icons props to override icons for certain modes in the mode selector panel. +// If no icon is provided for a specific mode, the default OTP-ui icon will be used. const customIcons = { TRANSIT: } From eeab320b4559230788a24a9939a4f56d6398468a Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 3 Apr 2020 11:08:14 -0400 Subject: [PATCH 07/31] fix: Fix linting errors and other print-layout merge. --- lib/components/app/print-layout.js | 13 ++++--------- lib/components/form/default-search-form.js | 2 +- .../narrative/line-itin/connected-itinerary-body.js | 2 -- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/lib/components/app/print-layout.js b/lib/components/app/print-layout.js index b90771cb1..2fda1815f 100644 --- a/lib/components/app/print-layout.js +++ b/lib/components/app/print-layout.js @@ -1,17 +1,15 @@ -import BaseMap from '@opentripplanner/base-map' -import EndpointsOverlay from '@opentripplanner/endpoints-overlay' import TriMetLegIcon from '@opentripplanner/icons/lib/trimet-leg-icon' import PrintableItinerary from '@opentripplanner/printable-itinerary' -import TransitiveOverlay from '@opentripplanner/transitive-overlay' import PropTypes from 'prop-types' import React, { Component } from 'react' -import { connect } from 'react-redux' import { Button } from 'react-bootstrap' +import { connect } from 'react-redux' import { parseUrlQueryString } from '../../actions/form' import { routingQuery } from '../../actions/api' -import { getActiveItinerary } from '../../util/state' +import DefaultMap from '../map/default-map' import TripDetails from '../narrative/connected-trip-details' +import { getActiveItinerary } from '../../util/state' class PrintLayout extends Component { static propTypes = { @@ -91,10 +89,7 @@ class PrintLayout extends Component { {/* The map, if visible */} {this.state.mapVisible &&
- - - - +
} diff --git a/lib/components/form/default-search-form.js b/lib/components/form/default-search-form.js index 299c19ef5..d6cf8a03a 100644 --- a/lib/components/form/default-search-form.js +++ b/lib/components/form/default-search-form.js @@ -7,7 +7,7 @@ import TabbedFormPanel from './tabbed-form-panel' import SwitchButton from './switch-button' // Use the icons props to override icons for certain modes in the mode selector panel. -// If no icon is provided for a specific mode, the default OTP-ui icon will be used. +// If no icon is provided for a specific mode, the default OTP-ui icon will be used. const customIcons = { TRANSIT: } diff --git a/lib/components/narrative/line-itin/connected-itinerary-body.js b/lib/components/narrative/line-itin/connected-itinerary-body.js index 6ff8994d8..ff174e78f 100644 --- a/lib/components/narrative/line-itin/connected-itinerary-body.js +++ b/lib/components/narrative/line-itin/connected-itinerary-body.js @@ -5,7 +5,6 @@ import ItineraryBody from '@opentripplanner/itinerary-body/lib/otp-react-redux/i import LineColumnContent from '@opentripplanner/itinerary-body/lib/otp-react-redux/line-column-content' import PlaceName from '@opentripplanner/itinerary-body/lib/otp-react-redux/place-name' import RouteDescription from '@opentripplanner/itinerary-body/lib/otp-react-redux/route-description' -import TripDetails from '@opentripplanner/trip-details/lib' import React, { Component } from 'react' import { connect } from 'react-redux' @@ -16,7 +15,6 @@ import TransitLegSubheader from './connected-transit-leg-subheader' import TripDetails from '../connected-trip-details' import TripTools from '../trip-tools' -import TransitLegSubheader from './connected-transit-leg-subheader' const noop = () => {} From 0bc4d967742c826770b08a93512afa81bc050b2c Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 3 Apr 2020 11:09:55 -0400 Subject: [PATCH 08/31] fix: One more lint error fix. --- lib/components/narrative/line-itin/connected-itinerary-body.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/components/narrative/line-itin/connected-itinerary-body.js b/lib/components/narrative/line-itin/connected-itinerary-body.js index ff174e78f..9f2d1cf1d 100644 --- a/lib/components/narrative/line-itin/connected-itinerary-body.js +++ b/lib/components/narrative/line-itin/connected-itinerary-body.js @@ -15,7 +15,6 @@ import TransitLegSubheader from './connected-transit-leg-subheader' import TripDetails from '../connected-trip-details' import TripTools from '../trip-tools' - const noop = () => {} class ConnectedItineraryBody extends Component { From c781f67a30bcb5ed4bb8c98fbc628d4b2a06d23e Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 3 Apr 2020 17:54:11 -0400 Subject: [PATCH 09/31] fix(Temp commit with ItinSummary visible in desktop mode.): --- lib/components/app/default-main-panel.js | 3 +- lib/components/icons/bike-icon.js | 19 +++ lib/components/icons/biketown-icon.js | 18 +++ lib/components/icons/bus-icon.js | 23 ++++ lib/components/icons/car2go-icon.js | 16 +++ lib/components/icons/direction-icon.js | 82 ++++++++++++ lib/components/icons/gondola-icon.js | 12 ++ lib/components/icons/index.js | 36 +++++ lib/components/icons/lyft-icon.js | 11 ++ lib/components/icons/rail-icon.js | 11 ++ lib/components/icons/reachnow-icon.js | 14 ++ lib/components/icons/streetcar-icon.js | 12 ++ lib/components/icons/tram-icon.js | 12 ++ lib/components/icons/transit-icon.js | 23 ++++ lib/components/icons/uber-icon.js | 14 ++ lib/components/icons/walk-icon.js | 14 ++ .../narrative/itinerary-carousel.js | 3 +- .../narrative/line-itin/itin-summary.js | 123 ++++++++++++++++++ .../narrative/line-itin/line-itinerary.js | 77 +++++++++++ .../narrative/narrative-itineraries.js | 3 +- .../narrative/tabbed-itineraries.js | 3 +- 21 files changed, 525 insertions(+), 4 deletions(-) create mode 100644 lib/components/icons/bike-icon.js create mode 100644 lib/components/icons/biketown-icon.js create mode 100644 lib/components/icons/bus-icon.js create mode 100644 lib/components/icons/car2go-icon.js create mode 100644 lib/components/icons/direction-icon.js create mode 100644 lib/components/icons/gondola-icon.js create mode 100644 lib/components/icons/index.js create mode 100644 lib/components/icons/lyft-icon.js create mode 100644 lib/components/icons/rail-icon.js create mode 100644 lib/components/icons/reachnow-icon.js create mode 100644 lib/components/icons/streetcar-icon.js create mode 100644 lib/components/icons/tram-icon.js create mode 100644 lib/components/icons/transit-icon.js create mode 100644 lib/components/icons/uber-icon.js create mode 100644 lib/components/icons/walk-icon.js create mode 100644 lib/components/narrative/line-itin/itin-summary.js create mode 100644 lib/components/narrative/line-itin/line-itinerary.js diff --git a/lib/components/app/default-main-panel.js b/lib/components/app/default-main-panel.js index ed3de24b6..9333c9ff1 100644 --- a/lib/components/app/default-main-panel.js +++ b/lib/components/app/default-main-panel.js @@ -8,13 +8,14 @@ import PlanTripButton from '../form/plan-trip-button' import UserSettings from '../form/user-settings' import NarrativeRoutingResults from '../narrative/narrative-routing-results' import { getActiveSearch, getShowUserSettings } from '../../util/state' +import defaultIcons from '../icons' class DefaultMainPanel extends Component { render () { const { activeSearch, currentQuery, - customIcons, + customIcons = defaultIcons, itineraryClass, itineraryFooter, mainPanelContent, diff --git a/lib/components/icons/bike-icon.js b/lib/components/icons/bike-icon.js new file mode 100644 index 000000000..c97d0de98 --- /dev/null +++ b/lib/components/icons/bike-icon.js @@ -0,0 +1,19 @@ +import React, { Component } from 'react' + +export default class BikeIcon extends Component { + render () { + return ( + + + + + + + + + + + + ) + } +} diff --git a/lib/components/icons/biketown-icon.js b/lib/components/icons/biketown-icon.js new file mode 100644 index 000000000..50fcefac7 --- /dev/null +++ b/lib/components/icons/biketown-icon.js @@ -0,0 +1,18 @@ +import React, { Component } from 'react' + +export default class BiketownIcon extends Component { + render () { + return ( + + + + + + + + + + + ) + } +} diff --git a/lib/components/icons/bus-icon.js b/lib/components/icons/bus-icon.js new file mode 100644 index 000000000..64bbcaf71 --- /dev/null +++ b/lib/components/icons/bus-icon.js @@ -0,0 +1,23 @@ +import React, { Component } from 'react' + +export default class BusIcon extends Component { + render () { + return ( + + + + + + + + + + + + + + + + ) + } +} diff --git a/lib/components/icons/car2go-icon.js b/lib/components/icons/car2go-icon.js new file mode 100644 index 000000000..fe4c01f3d --- /dev/null +++ b/lib/components/icons/car2go-icon.js @@ -0,0 +1,16 @@ +import React, { Component } from 'react' + +export default class Car2goIcon extends Component { + render () { + return ( + > + + + + + + + + ) + } +} diff --git a/lib/components/icons/direction-icon.js b/lib/components/icons/direction-icon.js new file mode 100644 index 000000000..3203d1e1c --- /dev/null +++ b/lib/components/icons/direction-icon.js @@ -0,0 +1,82 @@ +import React, { Component } from 'react' + +export default class DirectionIcon extends Component { + render () { + const { relativeDirection } = this.props + if (!relativeDirection) return null + switch (relativeDirection.toUpperCase()) { + case 'DEPART': + case 'CONTINUE': return ( + + + + ) + case 'LEFT': return ( + + + + ) + case 'RIGHT': return ( + + + + ) + case 'SLIGHTLY_LEFT': return ( + + + + ) + case 'SLIGHTLY_RIGHT': return ( + + + + ) + case 'HARD_LEFT': return ( + + + + ) + case 'HARD_RIGHT': return ( + + + + ) + case 'UTURN_LEFT': return ( + + + + ) + case 'UTURN_RIGHT': return ( + + + + ) + case 'CIRCLE_CLOCKWISE': return ( + + + + + + + + + ) + case 'CIRCLE_COUNTERCLOCKWISE': return ( + + + + + + + + + ) + case 'ELEVATOR': return ( + + + + ) + } + return null + } +} diff --git a/lib/components/icons/gondola-icon.js b/lib/components/icons/gondola-icon.js new file mode 100644 index 000000000..d14bcb744 --- /dev/null +++ b/lib/components/icons/gondola-icon.js @@ -0,0 +1,12 @@ +import React, { Component } from 'react' + +export default class GondolaIcon extends Component { + render () { + return ( + + + + + ) + } +} diff --git a/lib/components/icons/index.js b/lib/components/icons/index.js new file mode 100644 index 000000000..8bdf35517 --- /dev/null +++ b/lib/components/icons/index.js @@ -0,0 +1,36 @@ +import React from 'react' + +import BikeIcon from './bike-icon' +import BiketownIcon from './biketown-icon' +import BusIcon from './bus-icon' +import Car2goIcon from './car2go-icon' +import ReachNowIcon from './reachnow-icon' +import GondolaIcon from './gondola-icon' +import LyftIcon from './lyft-icon' +import RailIcon from './rail-icon' +import StreetcarIcon from './streetcar-icon' +import TramIcon from './tram-icon' +import TransitIcon from './transit-icon' +import UberIcon from './uber-icon' +import WalkIcon from './walk-icon' + +// define Portland-specific mode icons +export default { + BICYCLE: , + BICYCLE_RENT: , + BUS: , + CAR_HAIL_LYFT: , + CAR_HAIL_UBER: , + CAR_RENT_CAR2GO: , + CAR_RENT_REACHNOW: , + GONDOLA: , + RAIL: , + STREETCAR: , + TRAM: , + TRANSIT: , + WALK: , + customModeForLeg: (leg) => { + if (leg.routeLongName && leg.routeLongName.startsWith('Portland Streetcar')) return 'STREETCAR' + return null + } +} diff --git a/lib/components/icons/lyft-icon.js b/lib/components/icons/lyft-icon.js new file mode 100644 index 000000000..add6801bf --- /dev/null +++ b/lib/components/icons/lyft-icon.js @@ -0,0 +1,11 @@ +import React, { Component } from 'react' + +export default class LyftIcon extends Component { + render () { + return ( + + + + ) + } +} diff --git a/lib/components/icons/rail-icon.js b/lib/components/icons/rail-icon.js new file mode 100644 index 000000000..8f2ab99d6 --- /dev/null +++ b/lib/components/icons/rail-icon.js @@ -0,0 +1,11 @@ +import React, { Component } from 'react' + +export default class RailIcon extends Component { + render () { + return ( + + + + ) + } +} diff --git a/lib/components/icons/reachnow-icon.js b/lib/components/icons/reachnow-icon.js new file mode 100644 index 000000000..bab628729 --- /dev/null +++ b/lib/components/icons/reachnow-icon.js @@ -0,0 +1,14 @@ +import React, { Component } from 'react' + +export default class ReachNowIcon extends Component { + render () { + return ( + + + + + + + ) + } +} diff --git a/lib/components/icons/streetcar-icon.js b/lib/components/icons/streetcar-icon.js new file mode 100644 index 000000000..e53550142 --- /dev/null +++ b/lib/components/icons/streetcar-icon.js @@ -0,0 +1,12 @@ +import React, { Component } from 'react' + +export default class StreetcarIcon extends Component { + render () { + return ( + + + + + ) + } +} diff --git a/lib/components/icons/tram-icon.js b/lib/components/icons/tram-icon.js new file mode 100644 index 000000000..10e69e931 --- /dev/null +++ b/lib/components/icons/tram-icon.js @@ -0,0 +1,12 @@ +import React, { Component } from 'react' + +export default class TramIcon extends Component { + render () { + return ( + + + + + ) + } +} diff --git a/lib/components/icons/transit-icon.js b/lib/components/icons/transit-icon.js new file mode 100644 index 000000000..d2e96487c --- /dev/null +++ b/lib/components/icons/transit-icon.js @@ -0,0 +1,23 @@ +import React, { Component } from 'react' + +export default class TransitIcon extends Component { + render () { + // TODO: Find a better general transit icon to use than the bus icon. + return ( + + + + + + + + + + + + + + + ) + } +} diff --git a/lib/components/icons/uber-icon.js b/lib/components/icons/uber-icon.js new file mode 100644 index 000000000..77daea91a --- /dev/null +++ b/lib/components/icons/uber-icon.js @@ -0,0 +1,14 @@ +import React, { Component } from 'react' + +export default class UberIcon extends Component { + render () { + return ( + + + + + + + ) + } +} diff --git a/lib/components/icons/walk-icon.js b/lib/components/icons/walk-icon.js new file mode 100644 index 000000000..a071dcfa4 --- /dev/null +++ b/lib/components/icons/walk-icon.js @@ -0,0 +1,14 @@ +import React, { Component } from 'react' + +export default class WalkIcon extends Component { + render () { + return ( + + + + + + + ) + } +} diff --git a/lib/components/narrative/itinerary-carousel.js b/lib/components/narrative/itinerary-carousel.js index c560362bd..494d5f2b8 100644 --- a/lib/components/narrative/itinerary-carousel.js +++ b/lib/components/narrative/itinerary-carousel.js @@ -7,6 +7,7 @@ import SwipeableViews from 'react-swipeable-views' import { setActiveItinerary, setActiveLeg, setActiveStep } from '../../actions/narrative' import Icon from './icon' import DefaultItinerary from './default/default-itinerary' +import LineItinerary from './line-itin/line-itinerary' import Loading from './loading' import { getActiveItineraries, getActiveSearch } from '../../util/state' import { getTimeFormat } from '../../util/time' @@ -28,7 +29,7 @@ class ItineraryCarousel extends Component { } static defaultProps = { - itineraryClass: DefaultItinerary + itineraryClass: LineItinerary // DefaultItinerary } _onItineraryClick = () => { diff --git a/lib/components/narrative/line-itin/itin-summary.js b/lib/components/narrative/line-itin/itin-summary.js new file mode 100644 index 000000000..6d211b258 --- /dev/null +++ b/lib/components/narrative/line-itin/itin-summary.js @@ -0,0 +1,123 @@ +import TriMetModeIcon from '@opentripplanner/icons/lib/trimet-mode-icon' +import React, { Component } from 'react' +import PropTypes from 'prop-types' + +import { calculateFares, calculatePhysicalActivity, isTransit } from '../../../util/itinerary' +import { formatDuration, formatTime } from '../../../util/time' + +// TODO: make this a prop +const defaultRouteColor = '#008' + +export default class ItinerarySummary extends Component { + static propTypes = { + itinerary: PropTypes.object + } + + getIcon = (iconId, customIcons) => { + // Check if there is a custom icon + if (customIcons && iconId in customIcons) { + return customIcons[iconId] + } + + // Custom icon not available for the given iconId. Use the ModeIcon component + // to show the icon based on the iconId, but always use the default car icon + // for any car-based modes that didn't have custom icon + if (iconId && iconId.startsWith('CAR')) iconId = 'CAR' + return + } + + _onSummaryClicked = () => { + if (typeof this.props.onClick === 'function') this.props.onClick() + } + + render () { + const { customIcons, itinerary, timeOptions } = this.props + const { + centsToString, + maxTNCFare, + minTNCFare, + transitFare + } = calculateFares(itinerary) + // TODO: support non-USD + const minTotalFare = minTNCFare * 100 + transitFare + const maxTotalFare = maxTNCFare * 100 + transitFare + + const { caloriesBurned } = calculatePhysicalActivity(itinerary) + + return ( +
+
+ {/* Travel time in hrs/mins */} +
{formatDuration(itinerary.duration)}
+ + {/* Duration as time range */} +
+ {formatTime(itinerary.startTime, timeOptions)} - {formatTime(itinerary.endTime, timeOptions)} +
+ + {/* Fare / Calories */} +
+ {minTotalFare > 0 && + {centsToString(minTotalFare)} + {minTotalFare !== maxTotalFare && - {centsToString(maxTotalFare)}} + + } + {Math.round(caloriesBurned)} Cals +
+ + {/* Number of transfers, if applicable */} + {itinerary.transfers > 0 && ( +
+ {itinerary.transfers} transfer{itinerary.transfers > 1 ? 's' : ''} +
+ )} + +
+
+ {itinerary.legs.filter(leg => { + return !(leg.mode === 'WALK' && itinerary.transitTime > 0) + }).map((leg, k) => { + return
+
{this.getIcon(leg.mode, customIcons)}
+ {isTransit(leg.mode) + ? ( +
+ {getRouteNameForBadge(leg)} +
+ ) + : (
) + } +
+ })} +
+
+ ) + } +} + +// Helper functions + +function getRouteLongName (leg) { + return leg.routes && leg.routes.length > 0 + ? leg.routes[0].longName + : leg.routeLongName +} + +function getRouteNameForBadge (leg) { + const shortName = leg.routes && leg.routes.length > 0 + ? leg.routes[0].shortName : leg.routeShortName + + const longName = getRouteLongName(leg) + + // check for max + if (longName && longName.toLowerCase().startsWith('max')) return null + + // check for streetcar + if (longName && longName.startsWith('Portland Streetcar')) return longName.split('-')[1].trim().split(' ')[0] + + return shortName || longName +} + +function getRouteColorForBadge (leg) { + return leg.routeColor ? '#' + leg.routeColor : defaultRouteColor +} diff --git a/lib/components/narrative/line-itin/line-itinerary.js b/lib/components/narrative/line-itin/line-itinerary.js new file mode 100644 index 000000000..2ca69e267 --- /dev/null +++ b/lib/components/narrative/line-itin/line-itinerary.js @@ -0,0 +1,77 @@ +import React from 'react' + +import NarrativeItinerary from '../narrative-itinerary' +import SimpleRealtimeAnnotation from '../simple-realtime-annotation' +import { getLegModeLabel, getTimeZoneOffset, isTransit } from '../../../util/itinerary' + +import ItinerarySummary from './itin-summary' +import ItineraryBody from './connected-itinerary-body' + +export default class LineItinerary extends NarrativeItinerary { + _headerText () { + const { itinerary } = this.props + return itinerary.summary || this._getSummary(itinerary) + } + + _getSummary (itinerary) { + let summary = '' + let transitModes = [] + itinerary.legs.forEach((leg, index) => { + if (isTransit(leg.mode)) { + const modeStr = getLegModeLabel(leg) + if (transitModes.indexOf(modeStr) === -1) transitModes.push(modeStr) + } + }) + + // check for access mode + if (!isTransit(itinerary.legs[0].mode)) { + summary += getLegModeLabel(itinerary.legs[0]) + } + + // append transit modes, if applicable + if (transitModes.length > 0) { + summary += ' to ' + transitModes.join(', ') + } + + return summary + } + + render () { + const { + active, + companies, + customIcons, + expanded, + itinerary, + itineraryFooter, + showRealtimeAnnotation, + onClick, + timeFormat + } = this.props + + if (!itinerary) { + return
No Itinerary!
+ } + + const timeOptions = { + format: timeFormat, + offset: getTimeZoneOffset(itinerary) + } + + return ( +
+ + {showRealtimeAnnotation && } + {active || expanded + ? + : null} + {itineraryFooter} +
+ ) + } +} diff --git a/lib/components/narrative/narrative-itineraries.js b/lib/components/narrative/narrative-itineraries.js index 318c107ee..4c43ef1bf 100644 --- a/lib/components/narrative/narrative-itineraries.js +++ b/lib/components/narrative/narrative-itineraries.js @@ -9,6 +9,7 @@ import { setUseRealtimeResponse } from '../../actions/narrative' import DefaultItinerary from './default/default-itinerary' +import LineItinerary from './line-itin/line-itinerary' import { getActiveItineraries, getActiveSearch, getRealtimeEffects } from '../../util/state' import RealtimeAnnotation from './realtime-annotation' @@ -27,7 +28,7 @@ class NarrativeItineraries extends Component { } static defaultProps = { - itineraryClass: DefaultItinerary + itineraryClass: LineItinerary // DefaultItinerary } _toggleRealtimeItineraryClick = (e) => { diff --git a/lib/components/narrative/tabbed-itineraries.js b/lib/components/narrative/tabbed-itineraries.js index f3c4e2d76..e9d82355c 100644 --- a/lib/components/narrative/tabbed-itineraries.js +++ b/lib/components/narrative/tabbed-itineraries.js @@ -5,6 +5,7 @@ import { Button } from 'react-bootstrap' import { setActiveItinerary, setActiveLeg, setActiveStep, setUseRealtimeResponse } from '../../actions/narrative' import DefaultItinerary from './default/default-itinerary' +import LineItinerary from './line-itin/line-itinerary' import { getActiveSearch, getRealtimeEffects } from '../../util/state' import { calculateFares, calculatePhysicalActivity, getTimeZoneOffset } from '../../util/itinerary' import { formatDuration, formatTime, getTimeFormat } from '../../util/time' @@ -23,7 +24,7 @@ class TabbedItineraries extends Component { } static defaultProps = { - itineraryClass: DefaultItinerary + itineraryClass: LineItinerary // DefaultItinerary } _toggleRealtimeItineraryClick = (e) => { From 32cc8ba5c7f8a458644822fbad8bb4fc43d8a6ef Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 6 Apr 2020 15:22:09 -0400 Subject: [PATCH 10/31] feat(line-itin/itineray.css): Reinstate file. --- lib/components/app/default-main-panel.js | 4 +- .../narrative/line-itin/itinerary.css | 375 ++++++++++++++++++ lib/index.css | 1 + 3 files changed, 378 insertions(+), 2 deletions(-) create mode 100644 lib/components/narrative/line-itin/itinerary.css diff --git a/lib/components/app/default-main-panel.js b/lib/components/app/default-main-panel.js index 9333c9ff1..720c132c0 100644 --- a/lib/components/app/default-main-panel.js +++ b/lib/components/app/default-main-panel.js @@ -8,14 +8,14 @@ import PlanTripButton from '../form/plan-trip-button' import UserSettings from '../form/user-settings' import NarrativeRoutingResults from '../narrative/narrative-routing-results' import { getActiveSearch, getShowUserSettings } from '../../util/state' -import defaultIcons from '../icons' +//import defaultIcons from '../icons' class DefaultMainPanel extends Component { render () { const { activeSearch, currentQuery, - customIcons = defaultIcons, + customIcons, // = defaultIcons, itineraryClass, itineraryFooter, mainPanelContent, diff --git a/lib/components/narrative/line-itin/itinerary.css b/lib/components/narrative/line-itin/itinerary.css new file mode 100644 index 000000000..89df3acf0 --- /dev/null +++ b/lib/components/narrative/line-itin/itinerary.css @@ -0,0 +1,375 @@ +.otp .options.profile .itin-body .place-row { + margin-left: 55px; +} + +.otp .line-itin { + margin-bottom: 20px; +} + +/* Itinerary summary */ + +.otp .line-itin .itin-summary { + padding-right: 5px; + height: 60px; + display: table; + width: 100%; + margin-bottom: 15px; +} + +.otp .desktop-narrative-container .options.itinerary .line-itin .itin-summary { + display: none; +} + +.otp .line-itin .itin-summary .details { + display: table-cell; + vertical-align: top; +} + +.otp .line-itin .itin-summary .header { + font-weight: bold; + font-size: 18px; + margin-top: -3px; +} + +.otp .line-itin .itin-summary .detail { + font-size: 13px; + color: #999999; +} + +.otp .line-itin .itin-summary .routes { + display: table-cell; + text-align: right; +} + +.otp .line-itin .itin-summary .routes .route-preview { + display: inline-block; + margin-left: 8px; + vertical-align: top; +} + +.otp .line-itin .itin-summary .routes .route-preview .mode-icon { + height: 30px; + width: 30px; +} + +.otp .line-itin .itin-summary .routes .route-preview .short-name { + color: white; + font-weight: 500; + text-align: center; + margin-top: 6px; + font-size: 15px; + padding-top: 2px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + width: 30px; + height: 30px; + border-radius: 15px; + border: 2px solid white; + box-shadow: 0 0 0.5em #000; +} + +/* Itinerary main body */ + +.otp .line-itin .itin-body { + padding: 20px 0px; +} + +.otp .line-itin .place-row { + display: table; + width: 100%; +} + + +/* Departure/arrival time (1st column in table) */ + +.otp .line-itin .time { + display: table-cell; + width: 60px; + font-size: 14px; + color: #999999; + text-align: right; + padding-right: 4px; + padding-top: 1px; + vertical-align: top; +} + +/* The place icon and line itself (2nd column in table) */ +.otp .line-itin .line-container { + position: relative; + display: table-cell; + width: 20px; + max-width: 20px; +} + +.otp .line-itin .place-icon-group { + position: absolute; + font-size: 18px; + left: -8px; + top: -7px; + z-index: 20; +} + +.otp .line-itin .leg-line { + position: absolute; + top: 11px; + bottom: -11px; + z-index: 10; +} + +// Internet explorer specific media query to apply the below styling to fix +// rendering issues with table cell display with undefined heights. +/*@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + .otp .line-itin .line-container { + overflow: hidden; // hack for IE to render table cell correctly. + } + + .otp .line-itin .leg-line { + height: 1000px; // hack for IE to render table cell correctly. + } +}*/ + +.otp .line-itin .leg-line-walk { + left: 6px; + right: 6px; + background: radial-gradient(ellipse at center, #87cefa 40%, transparent 10%); + background-size: 12px 12px; + background-repeat: repeat-y; + background-position: center -5px; +} + +.otp .line-itin .leg-line-bicycle { + left: 7.5px; + right: 7.5px; + background: repeating-linear-gradient( + 0deg, + red, + red 8px, + white 8px, + white 12.5px + ); +} + +.otp .line-itin .leg-line-car { + left: 7.5px; + right: 7.5px; + background: repeating-linear-gradient( + 0deg, + grey, + grey 8px, + white 8px, + white 12.5px + ); +} + +.otp .line-itin .leg-line-micromobility { + left: 7.5px; + right: 7.5px; + background: repeating-linear-gradient( + 0deg, + #f5a729, + #f5a729 8px, + white 8px, + white 12.5px + ); +} + +.otp .line-itin .leg-line-transit { + left: 5px; + right: 5px; + background-color: gray; +} + +/* Place/Leg details (3rd column in table) */ + +.otp .line-itin .place-details { + font-size: 13px; + display: table-cell; + padding-top: 1px; +} + +.otp .line-itin .place-name { + font-size: 18px; + line-height: 20px; + padding-left: 4px; + font-weight: 500; + color: black; +} + +.otp .line-itin .place-subheader { + font-size: 12px; + padding-left: 4px; + padding-top: 1px; + font-weight: 300; + color: gray; +} + +.otp .line-itin .interline-dot { + position: relative; + float: left; + margin-left: -13.5px; + z-index: 25; + color: #fff; +} + +.otp .line-itin .interline-name { + font-size: 14px; + font-weight: 400; + line-height: 16px; +} + +/* Leg body general */ + +.otp .line-itin .leg-body { + padding: 12px 0px 18px 4px; + font-size: 13px; + color: #999999; +} + +.otp .line-itin .summary { + cursor: pointer; +} + +.otp .line-itin .leg-body .icon { + height: 24px; + width: 24px; + float: left; + margin-right: 6px; +} + +.otp .line-itin .leg-body .leg-description { + display: table; +} + +.otp .line-itin .leg-body .leg-description > div { + display: table-cell; + vertical-align: middle; +} + +/* Leg steps (for turn-by-turn) */ + +.otp .line-itin .leg-body .steps-header { + font-size: 13px; + margin-top: 10px; + color: #999999; + font-style: normal; + display: inline-block; +} + +.otp .line-itin .leg-body .step-row { + font-size: 13px; + margin-top: 8px; + color: #999999; + font-style: normal; +} + +/* Transit leg details */ + +.otp .line-itin .leg-body .route-name { + color: #999999; + margin-top: 5px; +} + +.otp .line-itin .leg-body .route-short-name { + display: inline-block; + background-color: #0f6aac; + padding-top: 1px; + color: white; + font-weight: 500; + font-size: 14px; + margin-right: 6px; + text-align: center; + width: 24px; + height: 24px; + border-radius: 12px; + border: 1px solid white; + box-shadow: 0 0 0.25em #000; + margin-right: 8px; +} + +.otp .line-itin .leg-body .route-long-name { + font-size: 13px; + line-height: 16px; + font-weight: 500; +} + +.otp .line-itin .leg-body .transit-leg-details { + margin-top: 5px; +} + +.otp .line-itin .leg-body .agency-info { + margin-top: 5px; + +} + +.otp .line-itin .leg-body .transit-leg-details .header { + cursor: pointer; + color: #999999; + font-size: 13px; +} + +/* Intermediate stops */ + +.otp .line-itin .leg-body .transit-leg-details .intermediate-stops .stop-row { + z-index: 30; + position: relative; +} + +.otp .line-itin .leg-body .transit-leg-details .intermediate-stops .stop-marker { + float: left; + margin-left: -17px; + color: white; +} + +.otp .line-itin .leg-body .transit-leg-details .intermediate-stops .stop-name { + color: #999999; + font-size: 14px; + margin-top: 3px; +} + +/* Transit alerts */ + +.otp .line-itin .leg-body .transit-alerts-toggle { + display: inline-block; + margin-top: 8px; + color: #D14727; + font-weight: 400; + cursor: pointer; +} + +.otp .line-itin .leg-body .transit-alerts { + margin-top: 3px; +} + +.otp .line-itin .leg-body .transit-alerts .transit-alert { + margin-top: 5px; + background-color: #eee; + padding: 8px; + color: black; + border-radius: 4px; +} + +.otp .line-itin .leg-body .transit-alerts .transit-alert .alert-icon { + float: left; + font-size: 18px; +} + +.otp .line-itin .leg-body .transit-alerts .transit-alert .alert-header { + font-size: 14px; + margin-left: 30px; + font-weight: 600; +} + +.otp .line-itin .leg-body .transit-alerts .transit-alert .alert-body { + font-size: 12px; + margin-left: 30px; + /* white space pre-wrap is required to render line breaks correctly. */ + white-space: pre-wrap; +} + +.otp .line-itin .leg-body .transit-alerts .transit-alert .effective-date { + margin-top: 5px; + margin-left: 30px; + font-size: 12px; + font-style: italic; +} diff --git a/lib/index.css b/lib/index.css index d768385b0..0fe0005bc 100644 --- a/lib/index.css +++ b/lib/index.css @@ -11,6 +11,7 @@ @import url(lib/components/form/form.css); @import url(lib/components/narrative/narrative.css); @import url(lib/components/narrative/default/itinerary.css); +@import url(lib/components/narrative/line-itin/itinerary.css); @import url(lib/components/mobile/mobile.css); @import url(lib/components/viewers/viewers.css); From 48a9544a64e1770bfab44b5fd16c34098e9516fb Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 6 Apr 2020 15:39:16 -0400 Subject: [PATCH 11/31] refactor(icons): Remove unused old icons. --- lib/components/app/default-main-panel.js | 3 +- lib/components/icons/bike-icon.js | 19 ------ lib/components/icons/biketown-icon.js | 18 ------ lib/components/icons/bus-icon.js | 23 ------- lib/components/icons/car2go-icon.js | 16 ----- lib/components/icons/direction-icon.js | 82 ------------------------ lib/components/icons/gondola-icon.js | 12 ---- lib/components/icons/index.js | 36 ----------- lib/components/icons/lyft-icon.js | 11 ---- lib/components/icons/rail-icon.js | 11 ---- lib/components/icons/reachnow-icon.js | 14 ---- lib/components/icons/streetcar-icon.js | 12 ---- lib/components/icons/tram-icon.js | 12 ---- lib/components/icons/transit-icon.js | 23 ------- lib/components/icons/uber-icon.js | 14 ---- lib/components/icons/walk-icon.js | 14 ---- 16 files changed, 1 insertion(+), 319 deletions(-) delete mode 100644 lib/components/icons/bike-icon.js delete mode 100644 lib/components/icons/biketown-icon.js delete mode 100644 lib/components/icons/bus-icon.js delete mode 100644 lib/components/icons/car2go-icon.js delete mode 100644 lib/components/icons/direction-icon.js delete mode 100644 lib/components/icons/gondola-icon.js delete mode 100644 lib/components/icons/index.js delete mode 100644 lib/components/icons/lyft-icon.js delete mode 100644 lib/components/icons/rail-icon.js delete mode 100644 lib/components/icons/reachnow-icon.js delete mode 100644 lib/components/icons/streetcar-icon.js delete mode 100644 lib/components/icons/tram-icon.js delete mode 100644 lib/components/icons/transit-icon.js delete mode 100644 lib/components/icons/uber-icon.js delete mode 100644 lib/components/icons/walk-icon.js diff --git a/lib/components/app/default-main-panel.js b/lib/components/app/default-main-panel.js index 720c132c0..ed3de24b6 100644 --- a/lib/components/app/default-main-panel.js +++ b/lib/components/app/default-main-panel.js @@ -8,14 +8,13 @@ import PlanTripButton from '../form/plan-trip-button' import UserSettings from '../form/user-settings' import NarrativeRoutingResults from '../narrative/narrative-routing-results' import { getActiveSearch, getShowUserSettings } from '../../util/state' -//import defaultIcons from '../icons' class DefaultMainPanel extends Component { render () { const { activeSearch, currentQuery, - customIcons, // = defaultIcons, + customIcons, itineraryClass, itineraryFooter, mainPanelContent, diff --git a/lib/components/icons/bike-icon.js b/lib/components/icons/bike-icon.js deleted file mode 100644 index c97d0de98..000000000 --- a/lib/components/icons/bike-icon.js +++ /dev/null @@ -1,19 +0,0 @@ -import React, { Component } from 'react' - -export default class BikeIcon extends Component { - render () { - return ( - - - - - - - - - - - - ) - } -} diff --git a/lib/components/icons/biketown-icon.js b/lib/components/icons/biketown-icon.js deleted file mode 100644 index 50fcefac7..000000000 --- a/lib/components/icons/biketown-icon.js +++ /dev/null @@ -1,18 +0,0 @@ -import React, { Component } from 'react' - -export default class BiketownIcon extends Component { - render () { - return ( - - - - - - - - - - - ) - } -} diff --git a/lib/components/icons/bus-icon.js b/lib/components/icons/bus-icon.js deleted file mode 100644 index 64bbcaf71..000000000 --- a/lib/components/icons/bus-icon.js +++ /dev/null @@ -1,23 +0,0 @@ -import React, { Component } from 'react' - -export default class BusIcon extends Component { - render () { - return ( - - - - - - - - - - - - - - - - ) - } -} diff --git a/lib/components/icons/car2go-icon.js b/lib/components/icons/car2go-icon.js deleted file mode 100644 index fe4c01f3d..000000000 --- a/lib/components/icons/car2go-icon.js +++ /dev/null @@ -1,16 +0,0 @@ -import React, { Component } from 'react' - -export default class Car2goIcon extends Component { - render () { - return ( - > - - - - - - - - ) - } -} diff --git a/lib/components/icons/direction-icon.js b/lib/components/icons/direction-icon.js deleted file mode 100644 index 3203d1e1c..000000000 --- a/lib/components/icons/direction-icon.js +++ /dev/null @@ -1,82 +0,0 @@ -import React, { Component } from 'react' - -export default class DirectionIcon extends Component { - render () { - const { relativeDirection } = this.props - if (!relativeDirection) return null - switch (relativeDirection.toUpperCase()) { - case 'DEPART': - case 'CONTINUE': return ( - - - - ) - case 'LEFT': return ( - - - - ) - case 'RIGHT': return ( - - - - ) - case 'SLIGHTLY_LEFT': return ( - - - - ) - case 'SLIGHTLY_RIGHT': return ( - - - - ) - case 'HARD_LEFT': return ( - - - - ) - case 'HARD_RIGHT': return ( - - - - ) - case 'UTURN_LEFT': return ( - - - - ) - case 'UTURN_RIGHT': return ( - - - - ) - case 'CIRCLE_CLOCKWISE': return ( - - - - - - - - - ) - case 'CIRCLE_COUNTERCLOCKWISE': return ( - - - - - - - - - ) - case 'ELEVATOR': return ( - - - - ) - } - return null - } -} diff --git a/lib/components/icons/gondola-icon.js b/lib/components/icons/gondola-icon.js deleted file mode 100644 index d14bcb744..000000000 --- a/lib/components/icons/gondola-icon.js +++ /dev/null @@ -1,12 +0,0 @@ -import React, { Component } from 'react' - -export default class GondolaIcon extends Component { - render () { - return ( - - - - - ) - } -} diff --git a/lib/components/icons/index.js b/lib/components/icons/index.js deleted file mode 100644 index 8bdf35517..000000000 --- a/lib/components/icons/index.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' - -import BikeIcon from './bike-icon' -import BiketownIcon from './biketown-icon' -import BusIcon from './bus-icon' -import Car2goIcon from './car2go-icon' -import ReachNowIcon from './reachnow-icon' -import GondolaIcon from './gondola-icon' -import LyftIcon from './lyft-icon' -import RailIcon from './rail-icon' -import StreetcarIcon from './streetcar-icon' -import TramIcon from './tram-icon' -import TransitIcon from './transit-icon' -import UberIcon from './uber-icon' -import WalkIcon from './walk-icon' - -// define Portland-specific mode icons -export default { - BICYCLE: , - BICYCLE_RENT: , - BUS: , - CAR_HAIL_LYFT: , - CAR_HAIL_UBER: , - CAR_RENT_CAR2GO: , - CAR_RENT_REACHNOW: , - GONDOLA: , - RAIL: , - STREETCAR: , - TRAM: , - TRANSIT: , - WALK: , - customModeForLeg: (leg) => { - if (leg.routeLongName && leg.routeLongName.startsWith('Portland Streetcar')) return 'STREETCAR' - return null - } -} diff --git a/lib/components/icons/lyft-icon.js b/lib/components/icons/lyft-icon.js deleted file mode 100644 index add6801bf..000000000 --- a/lib/components/icons/lyft-icon.js +++ /dev/null @@ -1,11 +0,0 @@ -import React, { Component } from 'react' - -export default class LyftIcon extends Component { - render () { - return ( - - - - ) - } -} diff --git a/lib/components/icons/rail-icon.js b/lib/components/icons/rail-icon.js deleted file mode 100644 index 8f2ab99d6..000000000 --- a/lib/components/icons/rail-icon.js +++ /dev/null @@ -1,11 +0,0 @@ -import React, { Component } from 'react' - -export default class RailIcon extends Component { - render () { - return ( - - - - ) - } -} diff --git a/lib/components/icons/reachnow-icon.js b/lib/components/icons/reachnow-icon.js deleted file mode 100644 index bab628729..000000000 --- a/lib/components/icons/reachnow-icon.js +++ /dev/null @@ -1,14 +0,0 @@ -import React, { Component } from 'react' - -export default class ReachNowIcon extends Component { - render () { - return ( - - - - - - - ) - } -} diff --git a/lib/components/icons/streetcar-icon.js b/lib/components/icons/streetcar-icon.js deleted file mode 100644 index e53550142..000000000 --- a/lib/components/icons/streetcar-icon.js +++ /dev/null @@ -1,12 +0,0 @@ -import React, { Component } from 'react' - -export default class StreetcarIcon extends Component { - render () { - return ( - - - - - ) - } -} diff --git a/lib/components/icons/tram-icon.js b/lib/components/icons/tram-icon.js deleted file mode 100644 index 10e69e931..000000000 --- a/lib/components/icons/tram-icon.js +++ /dev/null @@ -1,12 +0,0 @@ -import React, { Component } from 'react' - -export default class TramIcon extends Component { - render () { - return ( - - - - - ) - } -} diff --git a/lib/components/icons/transit-icon.js b/lib/components/icons/transit-icon.js deleted file mode 100644 index d2e96487c..000000000 --- a/lib/components/icons/transit-icon.js +++ /dev/null @@ -1,23 +0,0 @@ -import React, { Component } from 'react' - -export default class TransitIcon extends Component { - render () { - // TODO: Find a better general transit icon to use than the bus icon. - return ( - - - - - - - - - - - - - - - ) - } -} diff --git a/lib/components/icons/uber-icon.js b/lib/components/icons/uber-icon.js deleted file mode 100644 index 77daea91a..000000000 --- a/lib/components/icons/uber-icon.js +++ /dev/null @@ -1,14 +0,0 @@ -import React, { Component } from 'react' - -export default class UberIcon extends Component { - render () { - return ( - - - - - - - ) - } -} diff --git a/lib/components/icons/walk-icon.js b/lib/components/icons/walk-icon.js deleted file mode 100644 index a071dcfa4..000000000 --- a/lib/components/icons/walk-icon.js +++ /dev/null @@ -1,14 +0,0 @@ -import React, { Component } from 'react' - -export default class WalkIcon extends Component { - render () { - return ( - - - - - - - ) - } -} From d729c2d7fa8de96e1fdd9999d9a421f5ee58d4af Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 6 Apr 2020 15:50:58 -0400 Subject: [PATCH 12/31] fix(Fix lint, revert to DefaultItinerary.): --- lib/components/narrative/itinerary-carousel.js | 3 +-- lib/components/narrative/line-itin/itin-summary.js | 2 +- lib/components/narrative/narrative-itineraries.js | 3 +-- lib/components/narrative/tabbed-itineraries.js | 5 ++--- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/components/narrative/itinerary-carousel.js b/lib/components/narrative/itinerary-carousel.js index 494d5f2b8..c560362bd 100644 --- a/lib/components/narrative/itinerary-carousel.js +++ b/lib/components/narrative/itinerary-carousel.js @@ -7,7 +7,6 @@ import SwipeableViews from 'react-swipeable-views' import { setActiveItinerary, setActiveLeg, setActiveStep } from '../../actions/narrative' import Icon from './icon' import DefaultItinerary from './default/default-itinerary' -import LineItinerary from './line-itin/line-itinerary' import Loading from './loading' import { getActiveItineraries, getActiveSearch } from '../../util/state' import { getTimeFormat } from '../../util/time' @@ -29,7 +28,7 @@ class ItineraryCarousel extends Component { } static defaultProps = { - itineraryClass: LineItinerary // DefaultItinerary + itineraryClass: DefaultItinerary } _onItineraryClick = () => { diff --git a/lib/components/narrative/line-itin/itin-summary.js b/lib/components/narrative/line-itin/itin-summary.js index 6d211b258..b7d9d93b2 100644 --- a/lib/components/narrative/line-itin/itin-summary.js +++ b/lib/components/narrative/line-itin/itin-summary.js @@ -18,7 +18,7 @@ export default class ItinerarySummary extends Component { if (customIcons && iconId in customIcons) { return customIcons[iconId] } - + // Custom icon not available for the given iconId. Use the ModeIcon component // to show the icon based on the iconId, but always use the default car icon // for any car-based modes that didn't have custom icon diff --git a/lib/components/narrative/narrative-itineraries.js b/lib/components/narrative/narrative-itineraries.js index 4c43ef1bf..318c107ee 100644 --- a/lib/components/narrative/narrative-itineraries.js +++ b/lib/components/narrative/narrative-itineraries.js @@ -9,7 +9,6 @@ import { setUseRealtimeResponse } from '../../actions/narrative' import DefaultItinerary from './default/default-itinerary' -import LineItinerary from './line-itin/line-itinerary' import { getActiveItineraries, getActiveSearch, getRealtimeEffects } from '../../util/state' import RealtimeAnnotation from './realtime-annotation' @@ -28,7 +27,7 @@ class NarrativeItineraries extends Component { } static defaultProps = { - itineraryClass: LineItinerary // DefaultItinerary + itineraryClass: DefaultItinerary } _toggleRealtimeItineraryClick = (e) => { diff --git a/lib/components/narrative/tabbed-itineraries.js b/lib/components/narrative/tabbed-itineraries.js index e9d82355c..35331bd57 100644 --- a/lib/components/narrative/tabbed-itineraries.js +++ b/lib/components/narrative/tabbed-itineraries.js @@ -5,9 +5,8 @@ import { Button } from 'react-bootstrap' import { setActiveItinerary, setActiveLeg, setActiveStep, setUseRealtimeResponse } from '../../actions/narrative' import DefaultItinerary from './default/default-itinerary' -import LineItinerary from './line-itin/line-itinerary' -import { getActiveSearch, getRealtimeEffects } from '../../util/state' import { calculateFares, calculatePhysicalActivity, getTimeZoneOffset } from '../../util/itinerary' +import { getActiveSearch, getRealtimeEffects } from '../../util/state' import { formatDuration, formatTime, getTimeFormat } from '../../util/time' class TabbedItineraries extends Component { @@ -24,7 +23,7 @@ class TabbedItineraries extends Component { } static defaultProps = { - itineraryClass: LineItinerary // DefaultItinerary + itineraryClass: DefaultItinerary } _toggleRealtimeItineraryClick = (e) => { From e89098d37fd11180cc030eea6c316c85f32579ea Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 6 Apr 2020 15:58:01 -0400 Subject: [PATCH 13/31] style: Organize imports per PR comments. --- lib/components/map/default-map.js | 1 - lib/components/narrative/line-itin/itin-summary.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/components/map/default-map.js b/lib/components/map/default-map.js index 65f103de6..df7b2d28a 100644 --- a/lib/components/map/default-map.js +++ b/lib/components/map/default-map.js @@ -13,7 +13,6 @@ import { setMapPopupLocation, setMapPopupLocationAndGeocode } from '../../actions/map' - import BoundsUpdatingOverlay from './bounds-updating-overlay' import EndpointsOverlay from './connected-endpoints-overlay' import ParkAndRideOverlay from './connected-park-and-ride-overlay' diff --git a/lib/components/narrative/line-itin/itin-summary.js b/lib/components/narrative/line-itin/itin-summary.js index b7d9d93b2..146a88b32 100644 --- a/lib/components/narrative/line-itin/itin-summary.js +++ b/lib/components/narrative/line-itin/itin-summary.js @@ -1,6 +1,6 @@ import TriMetModeIcon from '@opentripplanner/icons/lib/trimet-mode-icon' -import React, { Component } from 'react' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { calculateFares, calculatePhysicalActivity, isTransit } from '../../../util/itinerary' import { formatDuration, formatTime } from '../../../util/time' From e1a8e1fa0c1efae62e7c556b8f4848059606b0c0 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 6 Apr 2020 16:01:21 -0400 Subject: [PATCH 14/31] style(Organize imports per PR comments.): --- lib/components/narrative/default/itinerary-summary.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/components/narrative/default/itinerary-summary.js b/lib/components/narrative/default/itinerary-summary.js index ed24b33a4..6b4800ba1 100644 --- a/lib/components/narrative/default/itinerary-summary.js +++ b/lib/components/narrative/default/itinerary-summary.js @@ -1,7 +1,6 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' - import TriMetLegIcon from '@opentripplanner/icons/lib/trimet-leg-icon' +import PropTypes from 'prop-types' +import React, { Component } from 'react' // TODO: add the custom icons hook found in print-layout. From 1f4a5f986d850404d944b5d0cea2a7faac911be2 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 7 Apr 2020 17:08:25 -0400 Subject: [PATCH 15/31] refactor(core-utils/map): Replace util/map with @opentripplanner/core-utils/map. --- lib/actions/map.js | 2 +- lib/components/form/user-settings.js | 6 +- lib/components/map/stylized-map.js | 9 +- lib/reducers/create-otp-reducer.js | 2 +- lib/util/index.js | 4 - lib/util/map.js | 223 --------------------------- lib/util/reverse.js | 12 -- 7 files changed, 9 insertions(+), 249 deletions(-) delete mode 100644 lib/util/map.js delete mode 100644 lib/util/reverse.js diff --git a/lib/actions/map.js b/lib/actions/map.js index fce9deb91..6e0e2bdc1 100644 --- a/lib/actions/map.js +++ b/lib/actions/map.js @@ -1,9 +1,9 @@ +import { constructLocation } from '@opentripplanner/core-utils/lib/map' import getGeocoder from '@opentripplanner/geocoder' import { createAction } from 'redux-actions' import { routingQuery } from './api' import { clearActiveSearch } from './form' -import { constructLocation } from '../util/map' /* SET_LOCATION action creator. Updates a from or to location in the store * diff --git a/lib/components/form/user-settings.js b/lib/components/form/user-settings.js index fe9e88cb4..159668ce5 100644 --- a/lib/components/form/user-settings.js +++ b/lib/components/form/user-settings.js @@ -1,14 +1,14 @@ -import React, { Component } from 'react' -import { connect } from 'react-redux' import moment from 'moment' +import { getDetailText, formatStoredPlaceName, matchLatLon } from '@opentripplanner/core-utils/lib/map' +import React, { Component } from 'react' import { Button } from 'react-bootstrap' +import { connect } from 'react-redux' import Icon from '../narrative/icon' import { forgetSearch, toggleTracking } from '../../actions/api' import { setQueryParam } from '../../actions/form' import { forgetPlace, forgetStop, setLocation } from '../../actions/map' import { setViewedStop } from '../../actions/ui' -import { getDetailText, formatStoredPlaceName, matchLatLon } from '../../util/map' import { summarizeQuery } from '../../util/query' const BUTTON_WIDTH = 40 diff --git a/lib/components/map/stylized-map.js b/lib/components/map/stylized-map.js index cf4a124fa..f3635a5e8 100644 --- a/lib/components/map/stylized-map.js +++ b/lib/components/map/stylized-map.js @@ -1,13 +1,12 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' import { select, event } from 'd3-selection' import { zoom } from 'd3-zoom' - +import { isBikeshareStation, itineraryToTransitive } from '@opentripplanner/core-utils/lib/map' +import PropTypes from 'prop-types' +import React, { Component } from 'react' +import { connect } from 'react-redux' import Transitive from 'transitive-js' import { getActiveSearch, getActiveItineraries } from '../../util/state' -import { isBikeshareStation, itineraryToTransitive } from '../../util/map' var STYLES = {} diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index 2ea229618..4293dfc62 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -2,8 +2,8 @@ import clone from 'clone' import update from 'immutability-helper' import isEqual from 'lodash.isequal' import objectPath from 'object-path' +import { matchLatLon } from '@opentripplanner/core-utils/lib/map' -import { matchLatLon } from '../util/map' import { ensureSingleAccessMode, getDefaultQuery, diff --git a/lib/util/index.js b/lib/util/index.js index 8d225a961..e95db1bec 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -1,9 +1,7 @@ import * as distance from './distance' import * as itinerary from './itinerary' -import * as map from './map' import * as profile from './profile' import * as query from './query' -import * as reverse from './reverse' import * as state from './state' import * as time from './time' import * as ui from './ui' @@ -11,10 +9,8 @@ import * as ui from './ui' const OtpUtils = { distance, itinerary, - map, profile, query, - reverse, state, time, ui diff --git a/lib/util/map.js b/lib/util/map.js deleted file mode 100644 index 8c5d44db7..000000000 --- a/lib/util/map.js +++ /dev/null @@ -1,223 +0,0 @@ -import moment from 'moment' - -import { isTransit, toSentenceCase } from './itinerary' - -export function latlngToString (latlng) { - return latlng && `${latlng.lat.toFixed(5)}, ${(latlng.lng || latlng.lon).toFixed(5)}` -} - -export function coordsToString (coords) { - return coords.length && coords.map(c => (+c).toFixed(5)).join(', ') -} - -export function stringToCoords (str) { - return (str && str.split(',').map(c => +c)) || [] -} - -export function constructLocation (latlng) { - return { - name: latlngToString(latlng), - lat: latlng.lat, - lon: latlng.lng - } -} - -export function formatStoredPlaceName (location, withDetails = true) { - let displayName = location.type === 'home' || location.type === 'work' - ? toSentenceCase(location.type) - : location.name - if (withDetails) { - let detailText = getDetailText(location) - if (detailText) displayName += ` (${detailText})` - } - return displayName -} - -export function getDetailText (location) { - let detailText - if (location.type === 'home' || location.type === 'work') { - detailText = location.name - } - if (location.type === 'stop') { - detailText = location.id - } else if (location.type === 'recent' && location.timestamp) { - detailText = moment(location.timestamp).fromNow() - } - return detailText -} - -export function matchLatLon (location1, location2) { - if (!location1 || !location2) return location1 === location2 - return location1.lat === location2.lat && location1.lon === location2.lon -} - -export function itineraryToTransitive (itin, includeGeometry) { - // console.log('itineraryToTransitive', itin); - const tdata = { - journeys: [], - streetEdges: [], - places: [], - patterns: [], - routes: [], - stops: [] - } - const routes = {} - const stops = {} - let streetEdgeId = 0 - let patternId = 0 - - const journey = { - journey_id: 'itin', - journey_name: 'Iterarary-derived Journey', - segments: [] - } - - // add 'from' and 'to' places to the tdata places array - tdata.places.push({ - place_id: 'from', - place_lat: itin.legs[0].from.lat, - place_lon: itin.legs[0].from.lon - }) - tdata.places.push({ - place_id: 'to', - place_lat: itin.legs[itin.legs.length - 1].to.lat, - place_lon: itin.legs[itin.legs.length - 1].to.lon - }) - - itin.legs.forEach(leg => { - if ( - leg.mode === 'WALK' || - leg.mode === 'BICYCLE' || - leg.mode === 'CAR' || - leg.mode === 'MICROMOBILITY' - ) { - const fromPlaceId = leg.from.bikeShareId - ? `bicycle_rent_station_${leg.from.bikeShareId}` - : `itin_street_${streetEdgeId}_from` - const toPlaceId = leg.to.bikeShareId - ? `bicycle_rent_station_${leg.to.bikeShareId}` - : `itin_street_${streetEdgeId}_to` - - const segment = { - type: leg.mode, - streetEdges: [streetEdgeId], - from: { type: 'PLACE', place_id: fromPlaceId }, - to: { type: 'PLACE', place_id: toPlaceId } - } - // For TNC segments, draw using an arc - if (leg.mode === 'CAR' && leg.hailedCar) segment.arc = true - journey.segments.push(segment) - - tdata.streetEdges.push({ - edge_id: streetEdgeId, - geometry: leg.legGeometry - }) - tdata.places.push({ - place_id: fromPlaceId, - // Do not label the from place in addition to the to place. Otherwise, - // in some cases (bike rental station) the label for a single place will - // appear twice on the rendered transitive view. - // See https://github.com/conveyal/trimet-mod-otp/issues/152 - // place_name: leg.from.name, - place_lat: leg.from.lat, - place_lon: leg.from.lon - }) - tdata.places.push({ - place_id: toPlaceId, - place_name: leg.to.name, - place_lat: leg.to.lat, - place_lon: leg.to.lon - }) - streetEdgeId++ - } - if (isTransit(leg.mode)) { - // determine if we have valid inter-stop geometry - const hasInterStopGeometry = - leg.interStopGeometry && - leg.interStopGeometry.length === leg.intermediateStops.length + 1 - - // create leg-specific pattern - const ptnId = 'ptn_' + patternId - const pattern = { - pattern_id: ptnId, - pattern_name: 'Pattern ' + patternId, - route_id: leg.routeId, - stops: [] - } - - // add 'from' stop to stops dictionary and pattern object - stops[leg.from.stopId] = { - stop_id: leg.from.stopId, - stop_name: leg.from.name, - stop_lat: leg.from.lat, - stop_lon: leg.from.lon - } - pattern.stops.push({ stop_id: leg.from.stopId }) - - // add intermediate stops to stops dictionary and pattern object - for (const [i, stop] of leg.intermediateStops.entries()) { - stops[stop.stopId] = { - stop_id: stop.stopId, - stop_name: stop.name, - stop_lat: stop.lat, - stop_lon: stop.lon - } - pattern.stops.push({ - stop_id: stop.stopId, - geometry: hasInterStopGeometry && leg.interStopGeometry[i].points - }) - } - - // add 'to' stop to stops dictionary and pattern object - stops[leg.to.stopId] = { - stop_id: leg.to.stopId, - stop_name: leg.to.name, - stop_lat: leg.to.lat, - stop_lon: leg.to.lon - } - pattern.stops.push({ - stop_id: leg.to.stopId, - geometry: hasInterStopGeometry && leg.interStopGeometry[leg.interStopGeometry.length - 1].points - }) - - // add route to the route dictionary - routes[leg.routeId] = { - agency_id: leg.agencyId, - route_id: leg.routeId, - route_short_name: leg.routeShortName || '', - route_long_name: leg.routeLongName || '', - route_type: leg.routeType, - route_color: leg.routeColor - } - - // add the pattern to the tdata patterns array - tdata.patterns.push(pattern) - - // add the pattern refrerence to the journey object - journey.segments.push({ - type: 'TRANSIT', - patterns: [{ - pattern_id: ptnId, - from_stop_index: 0, - to_stop_index: (leg.intermediateStops.length + 2) - 1 - }] - }) - - patternId++ - } - }) - - // add the routes and stops to the tdata arrays - for (const k in routes) tdata.routes.push(routes[k]) - for (const k in stops) tdata.stops.push(stops[k]) - - // add the journey to the tdata journeys array - tdata.journeys.push(journey) - - // console.log('derived tdata', tdata); - return tdata -} - -export function isBikeshareStation (place) { - return place.place_id.lastIndexOf('bicycle_rent_station') !== -1 -} diff --git a/lib/util/reverse.js b/lib/util/reverse.js deleted file mode 100644 index 8f8f93409..000000000 --- a/lib/util/reverse.js +++ /dev/null @@ -1,12 +0,0 @@ -// TODO: add reverse geocode for map click -// export async function reversePelias (point) { -// const location = {lon: point.lng, lat: point.lat} -// const apiKey = getConfigProperty('MAPZEN_TURN_BY_TURN_KEY') -// const params = { -// api_key: apiKey, -// ...location -// } -// const url = `https://search.mapzen.com/v1/reverse?${qs.stringify(params)}` -// const response = await fetch(url) -// return await response.json() -// } From 59c4c004c36790137d109e23b152fa7b5159aede Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 7 Apr 2020 17:26:50 -0400 Subject: [PATCH 16/31] refactor(util/profile,storage): Replace util/profile, storage with @opentripplanner/core-utils count --- lib/actions/api.js | 6 +- lib/actions/form.js | 4 +- .../narrative/itinerary-carousel.js | 4 +- .../narrative/narrative-profile-options.js | 4 +- lib/reducers/create-otp-reducer.js | 4 +- lib/util/index.js | 2 - lib/util/profile.js | 180 ------------------ lib/util/storage.js | 42 ---- 8 files changed, 11 insertions(+), 235 deletions(-) delete mode 100644 lib/util/profile.js delete mode 100644 lib/util/storage.js diff --git a/lib/actions/api.js b/lib/actions/api.js index 705822ecb..17bee06dc 100644 --- a/lib/actions/api.js +++ b/lib/actions/api.js @@ -1,17 +1,17 @@ /* globals fetch */ import { push, replace } from 'connected-react-router' +import haversine from 'haversine' +import moment from 'moment' import hash from 'object-hash' +import { randId } from '@opentripplanner/core-utils/lib/storage' import { createAction } from 'redux-actions' import qs from 'qs' -import moment from 'moment' -import haversine from 'haversine' import { rememberPlace } from './map' import { hasCar } from '../util/itinerary' import { getTripOptionsFromQuery, getUrlParams } from '../util/query' import queryParams from '../util/query-params' import { getStopViewerConfig, queryIsValid } from '../util/state' -import { randId } from '../util/storage' import { OTP_API_DATE_FORMAT, OTP_API_TIME_FORMAT } from '../util/time' if (typeof (fetch) === 'undefined') require('isomorphic-fetch') diff --git a/lib/actions/form.js b/lib/actions/form.js index 66860f1a4..a6678f386 100644 --- a/lib/actions/form.js +++ b/lib/actions/form.js @@ -1,7 +1,8 @@ import debounce from 'lodash.debounce' +import isEqual from 'lodash.isequal' import moment from 'moment' +import { getItem, randId } from '@opentripplanner/core-utils/lib/storage' import { createAction } from 'redux-actions' -import isEqual from 'lodash.isequal' import { getDefaultQuery, @@ -9,7 +10,6 @@ import { getUrlParams, planParamsToQuery } from '../util/query' -import { getItem, randId } from '../util/storage' import { queryIsValid } from '../util/state' import { OTP_API_TIME_FORMAT } from '../util/time' import { isMobile } from '../util/ui' diff --git a/lib/components/narrative/itinerary-carousel.js b/lib/components/narrative/itinerary-carousel.js index ce7a373fa..46bd4cf05 100644 --- a/lib/components/narrative/itinerary-carousel.js +++ b/lib/components/narrative/itinerary-carousel.js @@ -1,5 +1,6 @@ -import React, { Component } from 'react' +import { profileOptionsToItineraries } from '@opentripplanner/core-utils/lib/profile' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { Button } from 'react-bootstrap' import { connect } from 'react-redux' import SwipeableViews from 'react-swipeable-views' @@ -10,7 +11,6 @@ import DefaultItinerary from './default/default-itinerary' import Loading from './loading' import NarrativeProfileSummary from './narrative-profile-summary' import { getActiveItineraries, getActiveSearch } from '../../util/state' -import { profileOptionsToItineraries } from '../../util/profile' import { getTimeFormat } from '../../util/time' class ItineraryCarousel extends Component { diff --git a/lib/components/narrative/narrative-profile-options.js b/lib/components/narrative/narrative-profile-options.js index 405d6609a..4b7f1efc5 100644 --- a/lib/components/narrative/narrative-profile-options.js +++ b/lib/components/narrative/narrative-profile-options.js @@ -1,5 +1,6 @@ -import React, { Component } from 'react' +import { profileOptionsToItineraries } from '@opentripplanner/core-utils/lib/profile' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { connect } from 'react-redux' import { setActiveItinerary, setActiveLeg, setActiveStep } from '../../actions/narrative' @@ -7,7 +8,6 @@ import DefaultItinerary from './default/default-itinerary' import NarrativeProfileSummary from './narrative-profile-summary' import Loading from './loading' import { getActiveSearch } from '../../util/state' -import { profileOptionsToItineraries } from '../../util/profile' class NarrativeProfileOptions extends Component { static propTypes = { diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index 4293dfc62..8268aa535 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -3,6 +3,8 @@ import update from 'immutability-helper' import isEqual from 'lodash.isequal' import objectPath from 'object-path' import { matchLatLon } from '@opentripplanner/core-utils/lib/map' +import { filterProfileOptions } from '@opentripplanner/core-utils/lib/profile' +import { getItem, removeItem, storeItem } from '@opentripplanner/core-utils/lib/storage' import { ensureSingleAccessMode, @@ -10,8 +12,6 @@ import { getTripOptionsFromQuery } from '../util/query' import { isTransit, getTransitModes } from '../util/itinerary' -import { filterProfileOptions } from '../util/profile' -import { getItem, removeItem, storeItem } from '../util/storage' import { getUserTimezone } from '../util/time' import { MainPanelContent, MobileScreens } from '../actions/ui' diff --git a/lib/util/index.js b/lib/util/index.js index e95db1bec..291111a9a 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -1,6 +1,5 @@ import * as distance from './distance' import * as itinerary from './itinerary' -import * as profile from './profile' import * as query from './query' import * as state from './state' import * as time from './time' @@ -9,7 +8,6 @@ import * as ui from './ui' const OtpUtils = { distance, itinerary, - profile, query, state, time, diff --git a/lib/util/profile.js b/lib/util/profile.js deleted file mode 100644 index ed79ed806..000000000 --- a/lib/util/profile.js +++ /dev/null @@ -1,180 +0,0 @@ -export function filterProfileOptions (response) { - // Filter out similar options. TODO: handle on server? - const optStrs = [] - const filteredIndices = [] - - const filteredProfile = response.otp.profile.filter((option, i) => { - let optStr = option.access.map(a => a.mode).join('/') - if (option.transit) { - optStr += ' to ' + option.transit.map(transit => { - return transit.routes.map(route => route.id).join('/') - }).join(',') - } - if (optStrs.indexOf(optStr) !== -1) return false - optStrs.push(optStr) - filteredIndices.push(i) - return true - }) - - const filteredJourneys = response.otp.journeys.filter((journey, i) => filteredIndices.indexOf(i) !== -1) - - response.otp.profile = filteredProfile - response.otp.journeys = filteredJourneys - return response -} - -/** profileOptionsToItineraries **/ - -export function profileOptionsToItineraries (options, query) { - return options.map(option => optionToItinerary(option, query)) -} - -// helper functions for profileOptionsToItineraries: - -function optionToItinerary (option, query) { - const itin = { - duration: option.time, - legs: [], - walkTime: 0, - waitingTime: 0 - } - - // access leg - if (option.access && option.access.length > 0) { - if (option.access[0].mode === 'BICYCLE_RENT') { - let status = 'WALK_ON' - const walkOnEdges = [] - const bikeEdges = [] - const walkOffEdges = [] - let onStationName - let walkOnTime = 0 - let offStationName - let walkOffTime = 0 - option.access[0].streetEdges.forEach(edge => { - // check if we're returning the bike - if (edge.bikeRentalOffStation) { - status = 'WALK_OFF' - offStationName = edge.bikeRentalOffStation.name - } - - if (status === 'WALK_ON') { - walkOnEdges.push(edge) - walkOnTime += edge.distance - } else if (status === 'BIKE') { - bikeEdges.push(edge) - } else if (status === 'WALK_OFF') { - walkOffEdges.push(edge) - walkOffTime += edge.distance - } - - // check if we're picking up the bike - if (edge.bikeRentalOnStation) { - status = 'BIKE' - onStationName = edge.bikeRentalOnStation.name - } - }) - - itin.walkTime += (walkOnTime + walkOffTime) - - // create the 'on' walk leg - itin.legs.push({ - mode: 'WALK', - duration: walkOnTime, - transitLeg: false, - from: { - name: locationString(query && query.from.name, 'Destination') - }, - to: { - name: onStationName - } - }) - - // create the bike leg - itin.legs.push({ - mode: 'BICYCLE_RENT', - duration: option.time - walkOnTime - walkOffTime, - transitLeg: false, - from: { - name: onStationName - }, - to: { - name: offStationName - } - }) - - // create the 'off' walk leg - itin.legs.push({ - mode: 'WALK', - duration: walkOffTime, - transitLeg: false, - from: { - name: offStationName - }, - to: { - name: locationString(query && query.to.name, 'Destination') - } - }) - } else { - itin.legs.push(accessToLeg(option.access[0], query && query.from.name, option.transit ? null : query && query.to.name)) - if (option.access[0].mode === 'WALK') itin.walkTime += option.access[0].time - } - } - - // transit legs - if (option.transit) { - option.transit.forEach(transit => { - itin.legs.push({ - transitLeg: true, - mode: transit.mode, - from: { - name: transit.fromName - }, - to: { - name: transit.toName - }, - routes: transit.routes, - duration: transit.rideStats.avg, - averageWait: transit.waitStats.avg - }) - itin.waitingTime += transit.waitStats.avg - }) - } - - // egress leg - if (option.egress && option.egress.length > 0) { - // find the origin name, for transit trips - const origin = option.transit ? option.transit[option.transit.length - 1].toName : null - - itin.legs.push(accessToLeg(option.egress[0], origin, query && query.to.name)) - if (option.egress[0].mode === 'WALK') itin.walkTime += option.egress[0].time - } - - // construct summary - if (option.transit) { - itin.summary = 'Transit' - } else { - if (option.modes.length === 1 && option.modes[0] === 'bicycle') itin.summary = 'Bicycle' - else if (option.modes.length === 1 && option.modes[0] === 'walk') itin.summary = 'Walk' - else if (option.modes.indexOf('bicycle_rent') !== -1) itin.summary = 'Bikeshare' - } - - return itin -} - -function accessToLeg (access, origin, destination) { - return { - mode: access.mode, - duration: access.time, - transitLeg: false, - from: { - name: locationString(origin, 'Origin') - }, - to: { - name: locationString(destination, 'Destination') - } - } -} - -function locationString (str, defaultStr) { - return str ? str.split(',')[0] : defaultStr -} diff --git a/lib/util/storage.js b/lib/util/storage.js deleted file mode 100644 index 4d9b7d88c..000000000 --- a/lib/util/storage.js +++ /dev/null @@ -1,42 +0,0 @@ -// Prefix to use with local storage keys. -const STORAGE_PREFIX = 'otp' - -/** - * Store a javascript object at the specified key. - */ -export function storeItem (key, object) { - window.localStorage.setItem(`${STORAGE_PREFIX}.${key}`, JSON.stringify(object)) -} - -/** - * Retrieve a javascript object at the specified key. If not found, defaults to - * null or, the optionally provided notFoundValue. - */ -export function getItem (key, notFoundValue = null) { - let itemAsString - try { - itemAsString = window.localStorage.getItem(`${STORAGE_PREFIX}.${key}`) - const json = JSON.parse(itemAsString) - if (json) return json - else return notFoundValue - } catch (e) { - // Catch any errors associated with parsing bad JSON. - console.warn(e, itemAsString) - return notFoundValue - } -} - -/** - * Remove item at specified key. - */ -export function removeItem (key) { - window.localStorage.removeItem(`${STORAGE_PREFIX}.${key}`) -} - -/** - * Generate a random ID. This might not quite be a UUID, but it serves our - * purposes for now. - */ -export function randId () { - return Math.random().toString(36).substr(2, 9) -} From 15eb9ede3f5d4d9f6d5cbe8df80239efbfc73b2f Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 7 Apr 2020 18:33:38 -0400 Subject: [PATCH 17/31] refactor(util/time): Replace util/time with @opentripplanner/core-utils/time --- lib/actions/api.js | 2 +- lib/actions/form.js | 2 +- lib/components/form/date-time-preview.js | 11 ++- .../narrative/connected-trip-details.js | 5 +- .../narrative/default/access-leg.js | 5 +- .../narrative/default/default-itinerary.js | 2 +- lib/components/narrative/default/tnc-leg.js | 4 +- .../narrative/default/transit-leg.js | 7 +- .../narrative/itinerary-carousel.js | 2 +- .../narrative/line-itin/itin-summary.js | 4 +- .../narrative/realtime-annotation.js | 5 +- .../narrative/tabbed-itineraries.js | 6 +- lib/components/viewers/stop-viewer.js | 11 ++- lib/components/viewers/trip-viewer.js | 4 +- lib/reducers/create-otp-reducer.js | 2 +- lib/util/index.js | 2 - lib/util/time.js | 89 ------------------- 17 files changed, 33 insertions(+), 130 deletions(-) delete mode 100644 lib/util/time.js diff --git a/lib/actions/api.js b/lib/actions/api.js index 17bee06dc..f717450fd 100644 --- a/lib/actions/api.js +++ b/lib/actions/api.js @@ -4,6 +4,7 @@ import haversine from 'haversine' import moment from 'moment' import hash from 'object-hash' import { randId } from '@opentripplanner/core-utils/lib/storage' +import { OTP_API_DATE_FORMAT, OTP_API_TIME_FORMAT } from '@opentripplanner/core-utils/lib/time' import { createAction } from 'redux-actions' import qs from 'qs' @@ -12,7 +13,6 @@ import { hasCar } from '../util/itinerary' import { getTripOptionsFromQuery, getUrlParams } from '../util/query' import queryParams from '../util/query-params' import { getStopViewerConfig, queryIsValid } from '../util/state' -import { OTP_API_DATE_FORMAT, OTP_API_TIME_FORMAT } from '../util/time' if (typeof (fetch) === 'undefined') require('isomorphic-fetch') // Generic API actions diff --git a/lib/actions/form.js b/lib/actions/form.js index a6678f386..5d73405f1 100644 --- a/lib/actions/form.js +++ b/lib/actions/form.js @@ -2,6 +2,7 @@ import debounce from 'lodash.debounce' import isEqual from 'lodash.isequal' import moment from 'moment' import { getItem, randId } from '@opentripplanner/core-utils/lib/storage' +import { OTP_API_TIME_FORMAT } from '@opentripplanner/core-utils/lib/time' import { createAction } from 'redux-actions' import { @@ -11,7 +12,6 @@ import { planParamsToQuery } from '../util/query' import { queryIsValid } from '../util/state' -import { OTP_API_TIME_FORMAT } from '../util/time' import { isMobile } from '../util/ui' import { MobileScreens, diff --git a/lib/components/form/date-time-preview.js b/lib/components/form/date-time-preview.js index 08ce1d25b..44754d044 100644 --- a/lib/components/form/date-time-preview.js +++ b/lib/components/form/date-time-preview.js @@ -1,15 +1,14 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' import moment from 'moment' -import { connect } from 'react-redux' -import { Button } from 'react-bootstrap' - import { OTP_API_DATE_FORMAT, OTP_API_TIME_FORMAT, getTimeFormat, getDateFormat -} from '../../util/time' +} from '@opentripplanner/core-utils/lib/time' +import PropTypes from 'prop-types' +import React, { Component } from 'react' +import { Button } from 'react-bootstrap' +import { connect } from 'react-redux' class DateTimePreview extends Component { static propTypes = { diff --git a/lib/components/narrative/connected-trip-details.js b/lib/components/narrative/connected-trip-details.js index 16183ea1e..daf54e076 100644 --- a/lib/components/narrative/connected-trip-details.js +++ b/lib/components/narrative/connected-trip-details.js @@ -1,8 +1,7 @@ +import { getTimeFormat, getLongDateFormat } from '@opentripplanner/core-utils/lib/time' +import TripDetailsBase from '@opentripplanner/trip-details' import { connect } from 'react-redux' import styled from 'styled-components' -import TripDetailsBase from '@opentripplanner/trip-details' - -import { getTimeFormat, getLongDateFormat } from '../../util/time' const TripDetails = styled(TripDetailsBase)` border: 2px solid gray; diff --git a/lib/components/narrative/default/access-leg.js b/lib/components/narrative/default/access-leg.js index 274282544..1101d3a38 100644 --- a/lib/components/narrative/default/access-leg.js +++ b/lib/components/narrative/default/access-leg.js @@ -1,12 +1,11 @@ -import React, {Component} from 'react' +import { formatDuration } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' +import React, {Component} from 'react' import Icon from '../icon' - import LegDiagramPreview from '../leg-diagram-preview' import { distanceString } from '../../../util/distance' import { getStepInstructions } from '../../../util/itinerary' -import { formatDuration } from '../../../util/time' /** * Default access leg component for narrative itinerary. diff --git a/lib/components/narrative/default/default-itinerary.js b/lib/components/narrative/default/default-itinerary.js index 57f2717de..3eae4c049 100644 --- a/lib/components/narrative/default/default-itinerary.js +++ b/lib/components/narrative/default/default-itinerary.js @@ -1,3 +1,4 @@ +import { formatDuration, formatTime } from '@opentripplanner/core-utils/lib/time' import React from 'react' import NarrativeItinerary from '../narrative-itinerary' @@ -5,7 +6,6 @@ import ItinerarySummary from './itinerary-summary' import ItineraryDetails from './itinerary-details' import TripDetails from '../connected-trip-details' import TripTools from '../trip-tools' -import { formatDuration, formatTime } from '../../../util/time' export default class DefaultItinerary extends NarrativeItinerary { render () { diff --git a/lib/components/narrative/default/tnc-leg.js b/lib/components/narrative/default/tnc-leg.js index 0c8505ff5..afd1ddb22 100644 --- a/lib/components/narrative/default/tnc-leg.js +++ b/lib/components/narrative/default/tnc-leg.js @@ -1,6 +1,7 @@ import currencyFormatter from 'currency-formatter' -import React, { Component } from 'react' +import { formatDuration } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { connect } from 'react-redux' import { @@ -8,7 +9,6 @@ import { getTransportationNetworkCompanyRideEstimate } from '../../../actions/api' import { toSentenceCase } from '../../../util/itinerary' -import { formatDuration } from '../../../util/time' import { isMobile } from '../../../util/ui' class TransportationNetworkCompanyLeg extends Component { diff --git a/lib/components/narrative/default/transit-leg.js b/lib/components/narrative/default/transit-leg.js index 5809f3f1b..aef7c13eb 100644 --- a/lib/components/narrative/default/transit-leg.js +++ b/lib/components/narrative/default/transit-leg.js @@ -1,13 +1,12 @@ -import Icon from '../icon' -import React, { Component } from 'react' +import { formatDuration, formatTime } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' +import React, { Component } from 'react' +import Icon from '../icon' import ModeIcon from '../../icons/mode-icon' import ViewTripButton from '../../viewers/view-trip-button' import ViewStopButton from '../../viewers/view-stop-button' - import { getMapColor } from '../../../util/itinerary' -import { formatDuration, formatTime } from '../../../util/time' export default class TransitLeg extends Component { static propTypes = { diff --git a/lib/components/narrative/itinerary-carousel.js b/lib/components/narrative/itinerary-carousel.js index 46bd4cf05..10505ddf6 100644 --- a/lib/components/narrative/itinerary-carousel.js +++ b/lib/components/narrative/itinerary-carousel.js @@ -1,4 +1,5 @@ import { profileOptionsToItineraries } from '@opentripplanner/core-utils/lib/profile' +import { getTimeFormat } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' import React, { Component } from 'react' import { Button } from 'react-bootstrap' @@ -11,7 +12,6 @@ import DefaultItinerary from './default/default-itinerary' import Loading from './loading' import NarrativeProfileSummary from './narrative-profile-summary' import { getActiveItineraries, getActiveSearch } from '../../util/state' -import { getTimeFormat } from '../../util/time' class ItineraryCarousel extends Component { state = {} diff --git a/lib/components/narrative/line-itin/itin-summary.js b/lib/components/narrative/line-itin/itin-summary.js index e8965fe35..8766cedeb 100644 --- a/lib/components/narrative/line-itin/itin-summary.js +++ b/lib/components/narrative/line-itin/itin-summary.js @@ -1,8 +1,8 @@ -import React, { Component } from 'react' +import { formatDuration, formatTime } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { calculateFares, calculatePhysicalActivity, getLegIcon, isTransit } from '../../../util/itinerary' -import { formatDuration, formatTime } from '../../../util/time' // TODO: make this a prop const defaultRouteColor = '#008' diff --git a/lib/components/narrative/realtime-annotation.js b/lib/components/narrative/realtime-annotation.js index 834046d50..d0ecd93d5 100644 --- a/lib/components/narrative/realtime-annotation.js +++ b/lib/components/narrative/realtime-annotation.js @@ -1,9 +1,8 @@ -import React, { Component } from 'react' +import { formatDuration } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { Button, OverlayTrigger, Popover } from 'react-bootstrap' -import { formatDuration } from '../../util/time' - export default class RealtimeAnnotation extends Component { static propTypes = { realtimeEffects: PropTypes.object, diff --git a/lib/components/narrative/tabbed-itineraries.js b/lib/components/narrative/tabbed-itineraries.js index f3c4e2d76..b707b822a 100644 --- a/lib/components/narrative/tabbed-itineraries.js +++ b/lib/components/narrative/tabbed-itineraries.js @@ -1,13 +1,13 @@ -import React, { Component } from 'react' +import { formatDuration, formatTime, getTimeFormat } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' -import { connect } from 'react-redux' +import React, { Component } from 'react' import { Button } from 'react-bootstrap' +import { connect } from 'react-redux' import { setActiveItinerary, setActiveLeg, setActiveStep, setUseRealtimeResponse } from '../../actions/narrative' import DefaultItinerary from './default/default-itinerary' import { getActiveSearch, getRealtimeEffects } from '../../util/state' import { calculateFares, calculatePhysicalActivity, getTimeZoneOffset } from '../../util/itinerary' -import { formatDuration, formatTime, getTimeFormat } from '../../util/time' class TabbedItineraries extends Component { static propTypes = { diff --git a/lib/components/viewers/stop-viewer.js b/lib/components/viewers/stop-viewer.js index 424d7278b..1c04a1d80 100644 --- a/lib/components/viewers/stop-viewer.js +++ b/lib/components/viewers/stop-viewer.js @@ -1,20 +1,19 @@ -import React, { Component } from 'react' +import moment from 'moment' +import 'moment-timezone' +import { formatDuration, formatSecondsAfterMidnight, getTimeFormat, getUserTimezone } from '@opentripplanner/core-utils/lib/time' +import FromToLocationPicker from '@opentripplanner/from-to-location-picker' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { Button } from 'react-bootstrap' import { connect } from 'react-redux' -import moment from 'moment' -import 'moment-timezone' import { VelocityTransitionGroup } from 'velocity-react' -import FromToLocationPicker from '@opentripplanner/from-to-location-picker' - import Icon from '../narrative/icon' import { setMainPanelContent, toggleAutoRefresh } from '../../actions/ui' import { findStop, findStopTimesForStop } from '../../actions/api' import { forgetStop, rememberStop, setLocation } from '../../actions/map' import { routeComparator } from '../../util/itinerary' import { getShowUserSettings, getStopViewerConfig } from '../../util/state' -import { formatDuration, formatSecondsAfterMidnight, getTimeFormat, getUserTimezone } from '../../util/time' class StopViewer extends Component { state = {} diff --git a/lib/components/viewers/trip-viewer.js b/lib/components/viewers/trip-viewer.js index f1d3fe928..821f5d091 100644 --- a/lib/components/viewers/trip-viewer.js +++ b/lib/components/viewers/trip-viewer.js @@ -1,5 +1,6 @@ -import React, { Component } from 'react' +import { formatSecondsAfterMidnight, getTimeFormat } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { Button, Label } from 'react-bootstrap' import { connect } from 'react-redux' @@ -10,7 +11,6 @@ import { setViewedTrip } from '../../actions/ui' import { findTrip } from '../../actions/api' import { setLocation } from '../../actions/map' -import { formatSecondsAfterMidnight, getTimeFormat } from '../../util/time' class TripViewer extends Component { static propTypes = { diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index 8268aa535..84d7358f0 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -5,6 +5,7 @@ import objectPath from 'object-path' import { matchLatLon } from '@opentripplanner/core-utils/lib/map' import { filterProfileOptions } from '@opentripplanner/core-utils/lib/profile' import { getItem, removeItem, storeItem } from '@opentripplanner/core-utils/lib/storage' +import { getUserTimezone } from '@opentripplanner/core-utils/lib/time' import { ensureSingleAccessMode, @@ -12,7 +13,6 @@ import { getTripOptionsFromQuery } from '../util/query' import { isTransit, getTransitModes } from '../util/itinerary' -import { getUserTimezone } from '../util/time' import { MainPanelContent, MobileScreens } from '../actions/ui' const MAX_RECENT_STORAGE = 5 diff --git a/lib/util/index.js b/lib/util/index.js index 291111a9a..3cb72d8d0 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -2,7 +2,6 @@ import * as distance from './distance' import * as itinerary from './itinerary' import * as query from './query' import * as state from './state' -import * as time from './time' import * as ui from './ui' const OtpUtils = { @@ -10,7 +9,6 @@ const OtpUtils = { itinerary, query, state, - time, ui } diff --git a/lib/util/time.js b/lib/util/time.js deleted file mode 100644 index be7e5964c..000000000 --- a/lib/util/time.js +++ /dev/null @@ -1,89 +0,0 @@ -import moment from 'moment' -import 'moment-timezone' - -// special constants for making sure the following date format is always sent to -// OTP regardless of whatever the user has configured as the display format -export const OTP_API_DATE_FORMAT = 'YYYY-MM-DD' -export const OTP_API_TIME_FORMAT = 'HH:mm' - -/** - * @param {[type]} config the OTP config object found in store - * @return {string} the config-defined time formatter or HH:mm (24-hr time) - */ -export function getTimeFormat (config) { - return (config.dateTime && config.dateTime.timeFormat) - ? config.dateTime.timeFormat - : OTP_API_TIME_FORMAT -} - -export function getDateFormat (config) { - return (config.dateTime && config.dateTime.dateFormat) - ? config.dateTime.dateFormat - : OTP_API_DATE_FORMAT -} - -export function getLongDateFormat (config) { - return (config.dateTime && config.dateTime.longDateFormat) - ? config.dateTime.longDateFormat - : 'D MMMM YYYY' -} - -/** - * Formats an elapsed time duration for display in narrative - * TODO: internationalization - * @param {number} seconds duration in seconds - * @returns {string} formatted text representation - */ -export function formatDuration (seconds) { - const dur = moment.duration(seconds, 'seconds') - let text = '' - if (dur.hours() > 0) text += dur.hours() + ' hr, ' - text += dur.minutes() + ' min' - return text -} - -/** - * Formats a time value for display in narrative - * TODO: internationalization/timezone - * @param {number} ms epoch time value in milliseconds - * @returns {string} formatted text representation - */ -export function formatTime (ms, options) { - return moment(ms + (options && options.offset ? options.offset : 0)) - .format(options && options.format ? options.format : OTP_API_TIME_FORMAT) -} - -/** - * Formats a seconds after midnight value for display in narrative - * @param {number} seconds time since midnight in seconds - * @param {string} timeFormat A valid moment.js time format - * @return {string} formatted text representation - */ -export function formatSecondsAfterMidnight (seconds, timeFormat) { - return moment().startOf('day').seconds(seconds).format(timeFormat) -} - -/** - * Formats current time for use in OTP query - * The conversion to the user's timezone is needed for testing purposes. - */ -export function getCurrentTime () { - return moment().tz(getUserTimezone()).format(OTP_API_TIME_FORMAT) -} - -/** - * Formats current date for use in OTP query - * The conversion to the user's timezone is needed for testing purposes. - */ -export function getCurrentDate (config) { - return moment().tz(getUserTimezone()).format(OTP_API_DATE_FORMAT) -} - -/** - * Get the timezone name that is set for the user that is currently looking at - * this website. Use a bit of hackery to force a specific timezone if in a - * test environment. - */ -export function getUserTimezone () { - return process.env.NODE_ENV === 'test' ? process.env.TZ : moment.tz.guess() -} From 9d5c408ba92fb00ab20aa508a8a9a7fbe12897ce Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 8 Apr 2020 11:05:56 -0400 Subject: [PATCH 18/31] refactor(util/query-params): Replace query-params with @opentripplanner/core-utils/query-params. --- lib/actions/api.js | 2 +- lib/components/viewers/trip-viewer.js | 1 - lib/util/query-params.js | 546 -------------------------- lib/util/query.js | 21 +- 4 files changed, 18 insertions(+), 552 deletions(-) delete mode 100644 lib/util/query-params.js diff --git a/lib/actions/api.js b/lib/actions/api.js index f717450fd..b15855e40 100644 --- a/lib/actions/api.js +++ b/lib/actions/api.js @@ -3,6 +3,7 @@ import { push, replace } from 'connected-react-router' import haversine from 'haversine' import moment from 'moment' import hash from 'object-hash' +import queryParams from '@opentripplanner/core-utils/lib/query-params' import { randId } from '@opentripplanner/core-utils/lib/storage' import { OTP_API_DATE_FORMAT, OTP_API_TIME_FORMAT } from '@opentripplanner/core-utils/lib/time' import { createAction } from 'redux-actions' @@ -11,7 +12,6 @@ import qs from 'qs' import { rememberPlace } from './map' import { hasCar } from '../util/itinerary' import { getTripOptionsFromQuery, getUrlParams } from '../util/query' -import queryParams from '../util/query-params' import { getStopViewerConfig, queryIsValid } from '../util/state' if (typeof (fetch) === 'undefined') require('isomorphic-fetch') diff --git a/lib/components/viewers/trip-viewer.js b/lib/components/viewers/trip-viewer.js index 821f5d091..d517938aa 100644 --- a/lib/components/viewers/trip-viewer.js +++ b/lib/components/viewers/trip-viewer.js @@ -11,7 +11,6 @@ import { setViewedTrip } from '../../actions/ui' import { findTrip } from '../../actions/api' import { setLocation } from '../../actions/map' - class TripViewer extends Component { static propTypes = { hideBackButton: PropTypes.bool, diff --git a/lib/util/query-params.js b/lib/util/query-params.js deleted file mode 100644 index 94ebf17e2..000000000 --- a/lib/util/query-params.js +++ /dev/null @@ -1,546 +0,0 @@ -import { - isTransit, - isAccessMode, - isCar, - hasTransit, - hasBike, - hasMicromobility -} from './itinerary' -import { getItem } from './storage' -import { getCurrentDate, getCurrentTime } from './time' - -/** - * name: the default name of the parameter used for internal reference and API calls - * - * routingTypes: array of routing type(s) (ITINERARY, PROFILE, or both) this param applies to - * - * applicable: an optional function (accepting the current full query as a - * parameter) indicating whether this query parameter is applicable to the query. - * (Applicability is assumed if this function is not provided.) - * - * default: the default value for this parameter. The default can be also be a - * function that gets executed when accessing the default value. When the value - * is a funciton, it will take an argument of the current config of the otp-rr - * store. This is needed when a brand new time-dependent value is desired to be - * calculated. It's also helpful for producing tests that have consistent data - * output. - * - * itineraryRewrite: an optional function for translating the key and/or value - * for ITINERARY mode only (e.g. 'to' is rewritten as 'toPlace'). Accepts the - * intial internal value as a function parameter. - * - * profileRewrite: an optional function for translating the value for PROFILE mode - * - * label: a text label for for onscreen display. May either be a text string or a - * function (accepting the current full query as a parameter) returning a string - * - * selector: the default type of UI selector to use in the form. Can be one of: - * - DROPDOWN: a standard drop-down menu selector - * - * options: an array of text/value pairs used with a dropdown selector - * - * TODO: validation system for rewrite functions and/or better user documentation - * TODO: alphabetize below list - */ - -// FIXME: Use for parsing URL values? -// const stringToLocation = string => { -// const split = string.split(',') -// return split.length === 2 -// ? {lat: split[0], lon: split[1]} -// : {lat: null, lon: null} -// } - -const formatPlace = (location, alternateName) => { - if (!location) return null - const name = location.name || `${alternateName || 'Place'} (${location.lat},${location.lon})` - return `${name}::${location.lat},${location.lon}` -} - -// Load stored default query settings from local storage -let storedSettings = getItem('defaultQuery', {}) - -const queryParams = [ - { /* from - the trip origin. stored internally as a location (lat/lon/name) object */ - name: 'from', - routingTypes: [ 'ITINERARY', 'PROFILE' ], - default: null, - itineraryRewrite: value => ({ fromPlace: formatPlace(value, 'Origin') }), - profileRewrite: value => ({ from: { lat: value.lat, lon: value.lon } }) - // FIXME: Use for parsing URL values? - // fromURL: stringToLocation - }, - - { /* to - the trip destination. stored internally as a location (lat/lon/name) object */ - name: 'to', - routingTypes: [ 'ITINERARY', 'PROFILE' ], - default: null, - itineraryRewrite: value => ({ toPlace: formatPlace(value, 'Destination') }), - profileRewrite: value => ({ to: { lat: value.lat, lon: value.lon } }) - // FIXME: Use for parsing URL values? - // fromURL: stringToLocation - }, - - { /* date - the date of travel, in MM-DD-YYYY format */ - name: 'date', - routingTypes: [ 'ITINERARY', 'PROFILE' ], - default: getCurrentDate - }, - - { /* time - the arrival/departure time for an itinerary trip, in HH:mm format */ - name: 'time', - routingTypes: [ 'ITINERARY' ], - default: getCurrentTime - }, - - { /* departArrive - whether this is a depart-at, arrive-by, or leave-now trip */ - name: 'departArrive', - routingTypes: [ 'ITINERARY' ], - default: 'NOW', - itineraryRewrite: value => ({ arriveBy: (value === 'ARRIVE') }) - }, - - { /* startTime - the start time for a profile trip, in HH:mm format */ - name: 'startTime', - routingTypes: [ 'PROFILE' ], - default: '07:00' - }, - - { /* endTime - the end time for a profile trip, in HH:mm format */ - name: 'endTime', - routingTypes: [ 'PROFILE' ], - default: '09:00' - }, - - { /* mode - the allowed modes for a trip, as a comma-separated list */ - name: 'mode', - routingTypes: [ 'ITINERARY', 'PROFILE' ], - default: 'WALK,TRANSIT', // TODO: make this dependent on routingType? - profileRewrite: value => { - const accessModes = [] - const directModes = [] - const transitModes = [] - - if (value && value.length > 0) { - value.split(',').forEach(m => { - if (isTransit(m)) transitModes.push(m) - if (isAccessMode(m)) { - accessModes.push(m) - // TODO: make configurable whether direct-driving is considered - if (!isCar(m)) directModes.push(m) - } - }) - } - - return { accessModes, directModes, transitModes } - } - }, - - { /* showIntermediateStops - whether response should include intermediate stops for transit legs */ - name: 'showIntermediateStops', - routingTypes: [ 'ITINERARY' ], - default: true - }, - - { /* maxWalkDistance - the maximum distance in meters the user will walk to transit. */ - name: 'maxWalkDistance', - routingTypes: [ 'ITINERARY' ], - applicable: query => query.mode && hasTransit(query.mode) && query.mode.indexOf('WALK') !== -1, - default: 1207, // 3/4 mi. - selector: 'DROPDOWN', - label: 'Maximum Walk', - options: [ - { - text: '1/10 mile', - value: 160.9 - }, { - text: '1/4 mile', - value: 402.3 - }, { - text: '1/2 mile', - value: 804.7 - }, { - text: '3/4 mile', - value: 1207 - }, { - text: '1 mile', - value: 1609 - }, { - text: '2 miles', - value: 3219 - }, { - text: '5 miles', - value: 8047 - } - ] - }, - - { /* maxBikeDistance - the maximum distance in meters the user will bike. Not - * actually an OTP parameter (maxWalkDistance doubles for biking) but we - * store it separately internally in order to allow different default values, - * options, etc. Translated to 'maxWalkDistance' via the rewrite function. - */ - name: 'maxBikeDistance', - routingTypes: [ 'ITINERARY' ], - applicable: query => query.mode && hasTransit(query.mode) && query.mode.indexOf('BICYCLE') !== -1, - default: 4828, // 3 mi. - selector: 'DROPDOWN', - label: 'Maximum Bike', - options: [ - { - text: '1/4 mile', - value: 402.3 - }, { - text: '1/2 mile', - value: 804.7 - }, { - text: '3/4 mile', - value: 1207 - }, { - text: '1 mile', - value: 1609 - }, { - text: '2 miles', - value: 3219 - }, { - text: '3 miles', - value: 4828 - }, { - text: '5 miles', - value: 8047 - }, { - text: '10 miles', - value: 16093 - }, { - text: '20 miles', - value: 32187 - }, { - text: '30 miles', - value: 48280 - } - ], - itineraryRewrite: value => ({ - maxWalkDistance: value, - // ensures that the value is repopulated when loaded from URL params - maxBikeDistance: value - }) - }, - - { /* optimize -- how to optimize a trip (non-bike, non-micromobility trips) */ - name: 'optimize', - applicable: query => hasTransit(query.mode) && !hasBike(query.mode), - routingTypes: [ 'ITINERARY' ], - default: 'QUICK', - selector: 'DROPDOWN', - label: 'Optimize for', - options: [ - { - text: 'Speed', - value: 'QUICK' - }, { - text: 'Fewest Transfers', - value: 'TRANSFERS' - } - ] - }, - - { /* optimizeBike -- how to optimize an bike-based trip */ - name: 'optimizeBike', - applicable: query => hasBike(query.mode), - routingTypes: [ 'ITINERARY' ], - default: 'SAFE', - selector: 'DROPDOWN', - label: 'Optimize for', - options: query => { - const opts = [{ - text: 'Speed', - value: 'QUICK' - }, { - text: 'Bike-Friendly Trip', - value: 'SAFE' - }, { - text: 'Flat Trip', - value: 'FLAT' - }] - - // Include transit-specific option, if applicable - if (hasTransit(query.mode)) { - opts.splice(1, 0, { - text: 'Fewest Transfers', - value: 'TRANSFERS' - }) - } - - return opts - }, - itineraryRewrite: value => ({ optimize: value }) - }, - - { /* maxWalkTime -- the maximum time the user will spend walking in minutes */ - name: 'maxWalkTime', - routingTypes: [ 'PROFILE' ], - default: 15, - selector: 'DROPDOWN', - label: 'Max Walk Time', - applicable: query => query.mode && hasTransit(query.mode) && query.mode.indexOf('WALK') !== -1, - options: [ - { - text: '5 minutes', - value: 5 - }, { - text: '10 minutes', - value: 10 - }, { - text: '15 minutes', - value: 15 - }, { - text: '20 minutes', - value: 20 - }, { - text: '30 minutes', - value: 30 - }, { - text: '45 minutes', - value: 45 - }, { - text: '1 hour', - value: 60 - } - ] - }, - - { /* walkSpeed -- the user's walking speed in m/s */ - name: 'walkSpeed', - routingTypes: [ 'ITINERARY', 'PROFILE' ], - default: 1.34, - selector: 'DROPDOWN', - label: 'Walk Speed', - applicable: query => query.mode && query.mode.indexOf('WALK') !== -1, - options: [ - { - text: '2 MPH', - value: 0.89 - }, { - text: '3 MPH', - value: 1.34 - }, { - text: '4 MPH', - value: 1.79 - } - ] - }, - - { /* maxBikeTime -- the maximum time the user will spend biking in minutes */ - name: 'maxBikeTime', - routingTypes: [ 'PROFILE' ], - default: 20, - selector: 'DROPDOWN', - label: 'Max Bike Time', - applicable: query => query.mode && hasTransit(query.mode) && query.mode.indexOf('BICYCLE') !== -1, - options: [ - { - text: '5 minutes', - value: 5 - }, { - text: '10 minutes', - value: 10 - }, { - text: '15 minutes', - value: 15 - }, { - text: '20 minutes', - value: 20 - }, { - text: '30 minutes', - value: 30 - }, { - text: '45 minutes', - value: 45 - }, { - text: '1 hour', - value: 60 - } - ] - }, - - { /* bikeSpeed -- the user's bikeSpeed speed in m/s */ - name: 'bikeSpeed', - routingTypes: [ 'ITINERARY', 'PROFILE' ], - default: 3.58, - selector: 'DROPDOWN', - label: 'Bicycle Speed', - applicable: query => query.mode && query.mode.indexOf('BICYCLE') !== -1, - options: [ - { - text: '6 MPH', - value: 2.68 - }, { - text: '8 MPH', - value: 3.58 - }, { - text: '10 MPH', - value: 4.47 - }, { - text: '12 MPH', - value: 5.36 - } - ] - }, - - { /* maxEScooterDistance - the maximum distance in meters the user will ride - * an E-scooter. Not actually an OTP parameter (maxWalkDistance doubles for - * any non-transit mode except for car) but we store it separately - * internally in order to allow different default values, options, etc. - * Translated to 'maxWalkDistance' via the rewrite function. - */ - name: 'maxEScooterDistance', - routingTypes: [ 'ITINERARY' ], - applicable: query => query.mode && hasTransit(query.mode) && hasMicromobility(query.mode), - default: 4828, // 3 mi. - selector: 'DROPDOWN', - label: 'Maximum E-scooter Distance', - options: [ - { - text: '1/4 mile', - value: 402.3 - }, { - text: '1/2 mile', - value: 804.7 - }, { - text: '3/4 mile', - value: 1207 - }, { - text: '1 mile', - value: 1609 - }, { - text: '2 miles', - value: 3219 - }, { - text: '3 miles', - value: 4828 - }, { - text: '5 miles', - value: 8047 - }, { - text: '10 miles', - value: 16093 - }, { - text: '20 miles', - value: 32187 - }, { - text: '30 miles', - value: 48280 - } - ], - itineraryRewrite: value => ({ - maxWalkDistance: value, - // ensures that the value is repopulated when loaded from URL params - maxEScooterDistance: value - }) - }, - - { /* bikeSpeed -- the user's bikeSpeed speed in m/s */ - name: 'watts', - routingTypes: [ 'ITINERARY', 'PROFILE' ], - default: 250, - selector: 'DROPDOWN', - label: 'E-scooter Power', - // this configuration should only be allowed for personal E-scooters as these - // settings will be defined by the vehicle type of an E-scooter being rented - applicable: query => ( - query.mode && - query.mode.indexOf('MICROMOBILITY') !== -1 && - query.mode.indexOf('MICROMOBILITY_RENT') === -1 - ), - options: [ - { - text: 'Kid\'s hoverboard (6mph)', - value: 125 - }, { - text: 'Entry-level scooter (11mph)', - value: 250 - }, { - text: 'Robust E-scooter (18mph)', - value: 500 - }, { - text: 'Powerful E-scooter (24mph)', - value: 1500 - } - ], - // rewrite a few other values to add some baseline assumptions about the - // vehicle - itineraryRewrite: value => { - const watts = value - // the maximum cruising and downhill speed. Units in m/s - let maximumMicromobilitySpeed - let weight - // see https://en.wikipedia.org/wiki/Human_body_weight#Average_weight_around_the_world - // estimate is for an average North American human with clothes and stuff - // units are in kg - const TYPICAL_RIDER_WEIGHT = 90 - switch (watts) { - case 125: - // exemplar: Swagtron Turbo 5 hoverboard (https://swagtron.com/product/recertified-swagtron-turbo-five-hoverboard-classic/) - maximumMicromobilitySpeed = 2.8 // ~= 6mph - weight = TYPICAL_RIDER_WEIGHT + 9 - break - case 250: - // exemplar: Xiaomi M365 (https://www.gearbest.com/skateboard/pp_596618.html) - maximumMicromobilitySpeed = 5 // ~= 11.5mph - weight = TYPICAL_RIDER_WEIGHT + 12.5 - break - case 500: - // exemplar: Razor EcoSmart Metro (https://www.amazon.com/Razor-EcoSmart-Metro-Electric-Scooter/dp/B002ZDAEIS?SubscriptionId=AKIAJMXJ2YFJTEDLQMUQ&tag=digitren08-20&linkCode=xm2&camp=2025&creative=165953&creativeASIN=B002ZDAEIS&ascsubtag=15599460143449ocb) - maximumMicromobilitySpeed = 8 // ~= 18mph - weight = TYPICAL_RIDER_WEIGHT + 30 - break - case 1000: - // exemplar: Boosted Rev (https://boostedboards.com/vehicles/scooters/boosted-rev) - maximumMicromobilitySpeed = 11 // ~= 24mph - weight = TYPICAL_RIDER_WEIGHT + 21 - break - } - return {maximumMicromobilitySpeed, watts, weight} - } - }, - - { /* ignoreRealtimeUpdates -- if true, do not use realtime updates in routing */ - name: 'ignoreRealtimeUpdates', - routingTypes: [ 'ITINERARY' ], - default: false - }, - - { /* companies -- tnc companies to query */ - name: 'companies', - routingTypes: [ 'ITINERARY' ], - default: null - }, - - { /* wheelchair -- whether the user requires a wheelchair-accessible trip */ - name: 'wheelchair', - routingTypes: [ 'ITINERARY' ], - default: false, - selector: 'CHECKBOX', - label: 'Wheelchair Accessible', - applicable: (query, config) => { - if (!query.mode || !config.modes) return false - const configModes = (config.modes.accessModes || []).concat(config.modes.transitModes || []) - for (const mode of query.mode.split(',')) { - const configMode = configModes.find(m => m.mode === mode) - if (!configMode || !configMode.showWheelchairSetting) continue - if (configMode.company && (!query.companies || !query.companies.split(',').includes(configMode.company))) continue - return true - } - } - } -] -// Iterate over stored settings and update query param defaults. -// FIXME: this does not get updated if the user defaults are cleared -queryParams.forEach(param => { - if (param.name in storedSettings) { - param.default = storedSettings[param.name] - param.userDefaultOverride = true - } -}) - -export default queryParams diff --git a/lib/util/query.js b/lib/util/query.js index 5779108c7..a667e1172 100644 --- a/lib/util/query.js +++ b/lib/util/query.js @@ -1,13 +1,13 @@ +import { coordsToString, matchLatLon, stringToCoords } from '@opentripplanner/core-utils/lib/map' +import queryParams from '@opentripplanner/core-utils/lib/query-params' +import { getCurrentTime, getCurrentDate } from '@opentripplanner/core-utils/lib/time' import qs from 'qs' import { getTransitModes, hasTransit, isAccessMode, toSentenceCase } from './itinerary' -import { coordsToString, matchLatLon, stringToCoords } from './map' -import queryParams from './query-params' import { getActiveSearch } from './state' -import { getCurrentTime, getCurrentDate } from './time' /* The list of default parameters considered in the settings panel */ - +// Good to go. export const defaultParams = [ 'wheelchair', 'maxWalkDistance', @@ -22,6 +22,7 @@ export const defaultParams = [ 'watts' ] +// Good to go. /* A function to retrieve a property value from an entry in the query-params * table, checking for either a static value or a function */ @@ -31,6 +32,7 @@ export function getQueryParamProperty (paramInfo, property, query) { : paramInfo[property] } +// Good to go. export function ensureSingleAccessMode (queryModes) { // Count the number of access modes const accessCount = queryModes.filter(m => isAccessMode(m)).length @@ -48,19 +50,23 @@ export function ensureSingleAccessMode (queryModes) { return queryModes } +// Good to go. export function getUrlParams () { return qs.parse(window.location.href.split('?')[1]) } +// Good to go. export function getOtpUrlParams () { return Object.keys(getUrlParams()).filter(key => !key.startsWith('ui_')) } +// Good to go. function findLocationType (location, locations = [], types = ['home', 'work', 'suggested']) { const match = locations.find(l => matchLatLon(l, location)) return match && types.indexOf(match.type) !== -1 ? match.type : null } +// Good to go. export function summarizeQuery (query, locations = []) { const from = findLocationType(query.from, locations) || query.from.name.split(',')[0] const to = findLocationType(query.to, locations) || query.to.name.split(',')[0] @@ -70,6 +76,7 @@ export function summarizeQuery (query, locations = []) { return `${mode} from ${from} to ${to}` } +// CHECK THIS ONE => Used in handleBackButtonPRess /** * Assemble any UI-state properties to be tracked via URL into a single object * TODO: Expand to include additional UI properties @@ -84,6 +91,7 @@ export function getUiUrlParams (otpState) { return uiParams } +// Good to go. export function getTripOptionsFromQuery (query, keepPlace = false) { const options = Object.assign({}, query) // Delete time/date options and from/to @@ -97,6 +105,7 @@ export function getTripOptionsFromQuery (query, keepPlace = false) { return options } +// Good to go. /** * Gets the default query param by executing the default value function with the * provided otp config if the default value is a function. @@ -105,6 +114,7 @@ function getDefaultQueryParamValue (param, config) { return typeof param.default === 'function' ? param.default(config) : param.default } +// Good to go. /** * Determines whether the specified query differs from the default query, i.e., * whether the user has modified any trip options (including mode) from their @@ -135,6 +145,7 @@ export function isNotDefaultQuery (query, config) { return queryIsDifferent } +// Good to go. /** * Get the default query to OTP based on the given config. * @@ -148,6 +159,7 @@ export function getDefaultQuery (config) { return defaultQuery } +// Good to go. /** * Create a otp query based on a the url params. * @@ -186,6 +198,7 @@ export function planParamsToQuery (params, config) { return query } +// Good to go. /** * OTP allows passing a location in the form '123 Main St::lat,lon', so we check * for the double colon and parse the coordinates accordingly. From 66ad883afca29891dac97fa071709fc250643cf6 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 8 Apr 2020 11:30:58 -0400 Subject: [PATCH 19/31] refactor(util/ui): Replace util/ui with @opentripplanner/core-utils/ui (except getTitle(state)) --- lib/actions/form.js | 2 +- lib/components/app/responsive-webapp.js | 15 ++++--- lib/components/form/plan-trip-button.js | 4 +- lib/components/mobile/results-screen.js | 2 +- lib/components/narrative/default/tnc-leg.js | 2 +- lib/util/ui.js | 50 +-------------------- 6 files changed, 14 insertions(+), 61 deletions(-) diff --git a/lib/actions/form.js b/lib/actions/form.js index 5d73405f1..faf9750fb 100644 --- a/lib/actions/form.js +++ b/lib/actions/form.js @@ -3,6 +3,7 @@ import isEqual from 'lodash.isequal' import moment from 'moment' import { getItem, randId } from '@opentripplanner/core-utils/lib/storage' import { OTP_API_TIME_FORMAT } from '@opentripplanner/core-utils/lib/time' +import { isMobile } from '@opentripplanner/core-utils/lib/ui' import { createAction } from 'redux-actions' import { @@ -12,7 +13,6 @@ import { planParamsToQuery } from '../util/query' import { queryIsValid } from '../util/state' -import { isMobile } from '../util/ui' import { MobileScreens, setMainPanelContent, diff --git a/lib/components/app/responsive-webapp.js b/lib/components/app/responsive-webapp.js index 284200975..7c52b70a3 100644 --- a/lib/components/app/responsive-webapp.js +++ b/lib/components/app/responsive-webapp.js @@ -1,19 +1,20 @@ -import React, { Component } from 'react' +import { ConnectedRouter } from 'connected-react-router' +import { createHashHistory } from 'history' +import isEqual from 'lodash.isequal' +import { isMobile } from '@opentripplanner/core-utils/lib/ui' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { connect } from 'react-redux' -import isEqual from 'lodash.isequal' import { Route, Switch, withRouter } from 'react-router' -import { createHashHistory } from 'history' -import { ConnectedRouter } from 'connected-react-router' import PrintLayout from './print-layout' import { setMapCenter, setMapZoom } from '../../actions/config' -import { setLocationToCurrent } from '../../actions/map' -import { getCurrentPosition, receivedPositionResponse } from '../../actions/location' import { formChanged, parseUrlQueryString } from '../../actions/form' +import { getCurrentPosition, receivedPositionResponse } from '../../actions/location' +import { setLocationToCurrent } from '../../actions/map' import { handleBackButtonPress, matchContentToUrl } from '../../actions/ui' import { getUrlParams } from '../../util/query' -import { getTitle, isMobile } from '../../util/ui' +import { getTitle } from '../../util/ui' import { getActiveItinerary } from '../../util/state' class ResponsiveWebapp extends Component { diff --git a/lib/components/form/plan-trip-button.js b/lib/components/form/plan-trip-button.js index b619e647b..73d2de18a 100644 --- a/lib/components/form/plan-trip-button.js +++ b/lib/components/form/plan-trip-button.js @@ -1,11 +1,11 @@ -import React, { Component } from 'react' +import { isMobile } from '@opentripplanner/core-utils/lib/ui' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { Button } from 'react-bootstrap' import { connect } from 'react-redux' import { routingQuery } from '../../actions/api' import { setMainPanelContent } from '../../actions/ui' -import { isMobile } from '../../util/ui' class PlanTripButton extends Component { static propTypes = { diff --git a/lib/components/mobile/results-screen.js b/lib/components/mobile/results-screen.js index b8c73cc47..fce9656cc 100644 --- a/lib/components/mobile/results-screen.js +++ b/lib/components/mobile/results-screen.js @@ -1,3 +1,4 @@ +import { enableScrollForSelector } from '@opentripplanner/core-utils/lib/ui' import LocationIcon from '@opentripplanner/location-icon' import PropTypes from 'prop-types' import React, { Component } from 'react' @@ -16,7 +17,6 @@ import { MobileScreens, setMobileScreen } from '../../actions/ui' import { setUseRealtimeResponse } from '../../actions/narrative' import { clearActiveSearch } from '../../actions/form' import { getActiveSearch, getRealtimeEffects } from '../../util/state' -import { enableScrollForSelector } from '../../util/ui' const LocationContainer = styled.div` font-weight: 300; diff --git a/lib/components/narrative/default/tnc-leg.js b/lib/components/narrative/default/tnc-leg.js index afd1ddb22..905f0da4e 100644 --- a/lib/components/narrative/default/tnc-leg.js +++ b/lib/components/narrative/default/tnc-leg.js @@ -1,5 +1,6 @@ import currencyFormatter from 'currency-formatter' import { formatDuration } from '@opentripplanner/core-utils/lib/time' +import { isMobile } from '@opentripplanner/core-utils/lib/ui' import PropTypes from 'prop-types' import React, { Component } from 'react' import { connect } from 'react-redux' @@ -9,7 +10,6 @@ import { getTransportationNetworkCompanyRideEstimate } from '../../../actions/api' import { toSentenceCase } from '../../../util/itinerary' -import { isMobile } from '../../../util/ui' class TransportationNetworkCompanyLeg extends Component { static propTypes = { diff --git a/lib/util/ui.js b/lib/util/ui.js index bf5be4753..b577f84fc 100644 --- a/lib/util/ui.js +++ b/lib/util/ui.js @@ -5,55 +5,7 @@ import { getActiveSearch } from './state' // Set default title to the original document title (on load) set in index.html const DEFAULT_TITLE = document.title -export function isMobile () { - // TODO: consider using 3rd-party library? - return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) -} - -/** - * Enables scrolling for a specified selector, while disabling scrolling for all - * other targets. This is adapted from https://stackoverflow.com/a/41601290/915811 - * and intended to fix issues with iOS elastic scrolling, e.g., - * https://github.com/conveyal/trimet-mod-otp/issues/92. - */ -export function enableScrollForSelector (selector) { - const _overlay = document.querySelector(selector) - let _clientY = null // remember Y position on touch start - - _overlay.addEventListener('touchstart', function (event) { - if (event.targetTouches.length === 1) { - // detect single touch - _clientY = event.targetTouches[0].clientY - } - }, false) - - _overlay.addEventListener('touchmove', function (event) { - if (event.targetTouches.length === 1) { - // detect single touch - disableRubberBand(event) - } - }, false) - - function disableRubberBand (event) { - const clientY = event.targetTouches[0].clientY - _clientY - - if (_overlay.scrollTop === 0 && clientY > 0) { - // element is at the top of its scroll - event.preventDefault() - } - - if (isOverlayTotallyScrolled() && clientY < 0) { - // element is at the top of its scroll - event.preventDefault() - } - } - - function isOverlayTotallyScrolled () { - // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions - return _overlay.scrollHeight - _overlay.scrollTop <= _overlay.clientHeight - } -} - +// This needs to stay. export function getTitle (state) { // Override title can optionally be provided in config.yml const { config, ui, user } = state.otp From b868238b68028dfc34c97f975debc32475df7140 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 8 Apr 2020 11:59:37 -0400 Subject: [PATCH 20/31] refactor(util/query): Replace util/query with @opentripplanner/core-utils/query (except getUiUrlPara --- lib/actions/api.js | 2 +- lib/actions/form.js | 12 +- lib/actions/narrative.js | 2 +- lib/actions/ui.js | 5 +- lib/components/app/responsive-webapp.js | 2 +- lib/components/form/settings-preview.js | 5 +- lib/components/form/user-settings.js | 2 +- lib/components/form/user-trip-settings.js | 2 +- lib/reducers/create-otp-reducer.js | 8 +- lib/util/query.js | 206 +--------------------- lib/util/ui.js | 4 +- 11 files changed, 23 insertions(+), 227 deletions(-) diff --git a/lib/actions/api.js b/lib/actions/api.js index b15855e40..4731a9353 100644 --- a/lib/actions/api.js +++ b/lib/actions/api.js @@ -3,6 +3,7 @@ import { push, replace } from 'connected-react-router' import haversine from 'haversine' import moment from 'moment' import hash from 'object-hash' +import { getTripOptionsFromQuery, getUrlParams } from '@opentripplanner/core-utils/lib/query' import queryParams from '@opentripplanner/core-utils/lib/query-params' import { randId } from '@opentripplanner/core-utils/lib/storage' import { OTP_API_DATE_FORMAT, OTP_API_TIME_FORMAT } from '@opentripplanner/core-utils/lib/time' @@ -11,7 +12,6 @@ import qs from 'qs' import { rememberPlace } from './map' import { hasCar } from '../util/itinerary' -import { getTripOptionsFromQuery, getUrlParams } from '../util/query' import { getStopViewerConfig, queryIsValid } from '../util/state' if (typeof (fetch) === 'undefined') require('isomorphic-fetch') diff --git a/lib/actions/form.js b/lib/actions/form.js index faf9750fb..b1a1011d8 100644 --- a/lib/actions/form.js +++ b/lib/actions/form.js @@ -1,17 +1,17 @@ import debounce from 'lodash.debounce' import isEqual from 'lodash.isequal' import moment from 'moment' -import { getItem, randId } from '@opentripplanner/core-utils/lib/storage' -import { OTP_API_TIME_FORMAT } from '@opentripplanner/core-utils/lib/time' -import { isMobile } from '@opentripplanner/core-utils/lib/ui' -import { createAction } from 'redux-actions' - import { getDefaultQuery, getTripOptionsFromQuery, getUrlParams, planParamsToQuery -} from '../util/query' +} from '@opentripplanner/core-utils/lib/query' +import { getItem, randId } from '@opentripplanner/core-utils/lib/storage' +import { OTP_API_TIME_FORMAT } from '@opentripplanner/core-utils/lib/time' +import { isMobile } from '@opentripplanner/core-utils/lib/ui' +import { createAction } from 'redux-actions' + import { queryIsValid } from '../util/state' import { MobileScreens, diff --git a/lib/actions/narrative.js b/lib/actions/narrative.js index 8f016a400..c69c78cc7 100644 --- a/lib/actions/narrative.js +++ b/lib/actions/narrative.js @@ -1,7 +1,7 @@ +import { getUrlParams } from '@opentripplanner/core-utils/lib/query' import { createAction } from 'redux-actions' import { setUrlSearch } from './api' -import { getUrlParams } from '../util/query' export function setActiveItinerary (payload) { return function (dispatch, getState) { diff --git a/lib/actions/ui.js b/lib/actions/ui.js index ad04d2805..02f7374a8 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -1,13 +1,14 @@ +import { push } from 'connected-react-router' +import { getUrlParams } from '@opentripplanner/core-utils/lib/query' import { createAction } from 'redux-actions' import { matchPath } from 'react-router' -import { push } from 'connected-react-router' import { findRoute } from './api' import { setMapCenter, setMapZoom, setRouterId } from './config' import { clearActiveSearch, parseUrlQueryString, setActiveSearch } from './form' import { clearLocation } from './map' import { setActiveItinerary } from './narrative' -import { getUiUrlParams, getUrlParams } from '../util/query' +import { getUiUrlParams } from '../util/query' /** * Wrapper function for history#push that preserves the current search or, if diff --git a/lib/components/app/responsive-webapp.js b/lib/components/app/responsive-webapp.js index 7c52b70a3..233596914 100644 --- a/lib/components/app/responsive-webapp.js +++ b/lib/components/app/responsive-webapp.js @@ -1,6 +1,7 @@ import { ConnectedRouter } from 'connected-react-router' import { createHashHistory } from 'history' import isEqual from 'lodash.isequal' +import { getUrlParams } from '@opentripplanner/core-utils/lib/query' import { isMobile } from '@opentripplanner/core-utils/lib/ui' import PropTypes from 'prop-types' import React, { Component } from 'react' @@ -13,7 +14,6 @@ import { formChanged, parseUrlQueryString } from '../../actions/form' import { getCurrentPosition, receivedPositionResponse } from '../../actions/location' import { setLocationToCurrent } from '../../actions/map' import { handleBackButtonPress, matchContentToUrl } from '../../actions/ui' -import { getUrlParams } from '../../util/query' import { getTitle } from '../../util/ui' import { getActiveItinerary } from '../../util/state' diff --git a/lib/components/form/settings-preview.js b/lib/components/form/settings-preview.js index 1cefa4d98..4a3c78575 100644 --- a/lib/components/form/settings-preview.js +++ b/lib/components/form/settings-preview.js @@ -1,10 +1,9 @@ -import React, { Component } from 'react' +import { isNotDefaultQuery } from '@opentripplanner/core-utils/lib/query' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { Button } from 'react-bootstrap' import { connect } from 'react-redux' -import { isNotDefaultQuery } from '../../util/query' - class SettingsPreview extends Component { static propTypes = { // component props diff --git a/lib/components/form/user-settings.js b/lib/components/form/user-settings.js index 159668ce5..c7878839b 100644 --- a/lib/components/form/user-settings.js +++ b/lib/components/form/user-settings.js @@ -1,5 +1,6 @@ import moment from 'moment' import { getDetailText, formatStoredPlaceName, matchLatLon } from '@opentripplanner/core-utils/lib/map' +import { summarizeQuery } from '@opentripplanner/core-utils/lib/query' import React, { Component } from 'react' import { Button } from 'react-bootstrap' import { connect } from 'react-redux' @@ -9,7 +10,6 @@ import { forgetSearch, toggleTracking } from '../../actions/api' import { setQueryParam } from '../../actions/form' import { forgetPlace, forgetStop, setLocation } from '../../actions/map' import { setViewedStop } from '../../actions/ui' -import { summarizeQuery } from '../../util/query' const BUTTON_WIDTH = 40 diff --git a/lib/components/form/user-trip-settings.js b/lib/components/form/user-trip-settings.js index 6e5ea2383..5d00778f8 100644 --- a/lib/components/form/user-trip-settings.js +++ b/lib/components/form/user-trip-settings.js @@ -1,3 +1,4 @@ +import { getTripOptionsFromQuery, isNotDefaultQuery } from '@opentripplanner/core-utils/lib/query' import React, { Component } from 'react' import { Button } from 'react-bootstrap' import { connect } from 'react-redux' @@ -8,7 +9,6 @@ import { resetForm, storeDefaultSettings } from '../../actions/form' -import { getTripOptionsFromQuery, isNotDefaultQuery } from '../../util/query' /** * This component contains the `Remember/Forget my trip options` and `Restore defaults` commands diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index 84d7358f0..065f90270 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -4,14 +4,14 @@ import isEqual from 'lodash.isequal' import objectPath from 'object-path' import { matchLatLon } from '@opentripplanner/core-utils/lib/map' import { filterProfileOptions } from '@opentripplanner/core-utils/lib/profile' -import { getItem, removeItem, storeItem } from '@opentripplanner/core-utils/lib/storage' -import { getUserTimezone } from '@opentripplanner/core-utils/lib/time' - import { ensureSingleAccessMode, getDefaultQuery, getTripOptionsFromQuery -} from '../util/query' +} from '@opentripplanner/core-utils/lib/query' +import { getItem, removeItem, storeItem } from '@opentripplanner/core-utils/lib/storage' +import { getUserTimezone } from '@opentripplanner/core-utils/lib/time' + import { isTransit, getTransitModes } from '../util/itinerary' import { MainPanelContent, MobileScreens } from '../actions/ui' diff --git a/lib/util/query.js b/lib/util/query.js index a667e1172..c864e91c0 100644 --- a/lib/util/query.js +++ b/lib/util/query.js @@ -1,87 +1,10 @@ -import { coordsToString, matchLatLon, stringToCoords } from '@opentripplanner/core-utils/lib/map' -import queryParams from '@opentripplanner/core-utils/lib/query-params' -import { getCurrentTime, getCurrentDate } from '@opentripplanner/core-utils/lib/time' -import qs from 'qs' - -import { getTransitModes, hasTransit, isAccessMode, toSentenceCase } from './itinerary' import { getActiveSearch } from './state' -/* The list of default parameters considered in the settings panel */ -// Good to go. -export const defaultParams = [ - 'wheelchair', - 'maxWalkDistance', - 'maxWalkTime', - 'walkSpeed', - 'maxBikeDistance', - 'maxBikeTime', - 'bikeSpeed', - 'optimize', - 'optimizeBike', - 'maxEScooterDistance', - 'watts' -] - -// Good to go. -/* A function to retrieve a property value from an entry in the query-params - * table, checking for either a static value or a function */ - -export function getQueryParamProperty (paramInfo, property, query) { - return typeof paramInfo[property] === 'function' - ? paramInfo[property](query) - : paramInfo[property] -} - -// Good to go. -export function ensureSingleAccessMode (queryModes) { - // Count the number of access modes - const accessCount = queryModes.filter(m => isAccessMode(m)).length - - // If multiple access modes are specified, keep only the first one - if (accessCount > 1) { - const firstAccess = queryModes.find(m => isAccessMode(m)) - queryModes = queryModes.filter(m => !isAccessMode(m) || m === firstAccess) - - // If no access modes are specified, add 'WALK' as the default - } else if (accessCount === 0) { - queryModes.push('WALK') - } - - return queryModes -} - -// Good to go. -export function getUrlParams () { - return qs.parse(window.location.href.split('?')[1]) -} - -// Good to go. -export function getOtpUrlParams () { - return Object.keys(getUrlParams()).filter(key => !key.startsWith('ui_')) -} - -// Good to go. -function findLocationType (location, locations = [], types = ['home', 'work', 'suggested']) { - const match = locations.find(l => matchLatLon(l, location)) - return match && types.indexOf(match.type) !== -1 ? match.type : null -} - -// Good to go. -export function summarizeQuery (query, locations = []) { - const from = findLocationType(query.from, locations) || query.from.name.split(',')[0] - const to = findLocationType(query.to, locations) || query.to.name.split(',')[0] - const mode = hasTransit(query.mode) - ? 'Transit' - : toSentenceCase(query.mode) - return `${mode} from ${from} to ${to}` -} - -// CHECK THIS ONE => Used in handleBackButtonPRess +// => Used in actions/ui/handleBackButtonPress() /** * Assemble any UI-state properties to be tracked via URL into a single object * TODO: Expand to include additional UI properties */ - export function getUiUrlParams (otpState) { const activeSearch = getActiveSearch(otpState) const uiParams = { @@ -90,130 +13,3 @@ export function getUiUrlParams (otpState) { } return uiParams } - -// Good to go. -export function getTripOptionsFromQuery (query, keepPlace = false) { - const options = Object.assign({}, query) - // Delete time/date options and from/to - delete options.time - delete options.departArrive - delete options.date - if (!keepPlace) { - delete options.from - delete options.to - } - return options -} - -// Good to go. -/** - * Gets the default query param by executing the default value function with the - * provided otp config if the default value is a function. - */ -function getDefaultQueryParamValue (param, config) { - return typeof param.default === 'function' ? param.default(config) : param.default -} - -// Good to go. -/** - * Determines whether the specified query differs from the default query, i.e., - * whether the user has modified any trip options (including mode) from their - * default values. - */ -export function isNotDefaultQuery (query, config) { - const activeModes = query.mode.split(',') - const defaultModes = getTransitModes(config).concat(['WALK']) - let queryIsDifferent = false - const modesEqual = (activeModes.length === defaultModes.length) && - activeModes.sort().every((value, index) => { return value === defaultModes.sort()[index] }) - - if (!modesEqual) { - queryIsDifferent = true - } else { - defaultParams.forEach(param => { - const paramInfo = queryParams.find(qp => qp.name === param) - // Check that the parameter applies to the specified routingType - if (!paramInfo.routingTypes.includes(query.routingType)) return - // Check that the applicability test (if provided) is satisfied - if (typeof paramInfo.applicable === 'function' && - !paramInfo.applicable(query, config)) return - if (query[param] !== getDefaultQueryParamValue(paramInfo, config)) { - queryIsDifferent = true - } - }) - } - return queryIsDifferent -} - -// Good to go. -/** - * Get the default query to OTP based on the given config. - * - * @param config the config in the otp-rr store. - */ -export function getDefaultQuery (config) { - const defaultQuery = { routingType: 'ITINERARY' } - queryParams.filter(qp => 'default' in qp).forEach(qp => { - defaultQuery[qp.name] = getDefaultQueryParamValue(qp, config) - }) - return defaultQuery -} - -// Good to go. -/** - * Create a otp query based on a the url params. - * - * @param {Object} params An object representing the parsed querystring of url - * params. - * @param config the config in the otp-rr store. - */ -export function planParamsToQuery (params, config) { - const query = {} - for (var key in params) { - switch (key) { - case 'fromPlace': - query.from = parseLocationString(params.fromPlace) - break - case 'toPlace': - query.to = parseLocationString(params.toPlace) - break - case 'arriveBy': - query.departArrive = params.arriveBy === 'true' - ? 'ARRIVE' - : params.arriveBy === 'false' - ? 'DEPART' - : 'NOW' - break - case 'date': - query.date = params.date || getCurrentDate(config) - break - case 'time': - query.time = params.time || getCurrentTime(config) - break - default: - if (!isNaN(params[key])) query[key] = parseFloat(params[key]) - else query[key] = params[key] - } - } - return query -} - -// Good to go. -/** - * OTP allows passing a location in the form '123 Main St::lat,lon', so we check - * for the double colon and parse the coordinates accordingly. - */ -function parseLocationString (value) { - const parts = value.split('::') - const coordinates = parts[1] - ? stringToCoords(parts[1]) - : stringToCoords(parts[0]) - const name = parts[1] - ? parts[0] - : coordsToString(coordinates) - return coordinates.length === 2 ? { - name: name || null, - lat: coordinates[0] || null, - lon: coordinates[1] || null - } : null -} diff --git a/lib/util/ui.js b/lib/util/ui.js index b577f84fc..47e452528 100644 --- a/lib/util/ui.js +++ b/lib/util/ui.js @@ -1,11 +1,11 @@ +import { summarizeQuery } from '@opentripplanner/core-utils/lib/query' + import { MainPanelContent } from '../actions/ui' -import { summarizeQuery } from './query' import { getActiveSearch } from './state' // Set default title to the original document title (on load) set in index.html const DEFAULT_TITLE = document.title -// This needs to stay. export function getTitle (state) { // Override title can optionally be provided in config.yml const { config, ui, user } = state.otp From 26155143c2330ffcc885dc6f6494e87cab4276dd Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 8 Apr 2020 14:53:26 -0400 Subject: [PATCH 21/31] refactor(util/itinerary): 1/... Replace util/itinerary with @opentripplanner/core-utils/itinerary. --- lib/actions/api.js | 2 +- lib/components/map/leg-diagram.js | 4 +- .../narrative/default/access-leg.js | 2 +- .../narrative/default/itinerary-details.js | 4 +- lib/components/narrative/default/tnc-leg.js | 2 +- .../narrative/default/transit-leg.js | 2 +- .../narrative/leg-diagram-preview.js | 4 +- .../narrative/line-itin/itin-summary.js | 3 +- .../narrative/line-itin/line-itinerary.js | 2 +- .../narrative/tabbed-itineraries.js | 2 +- lib/reducers/create-otp-reducer.js | 2 +- lib/util/itinerary.js | 43 +++++++++++++++++-- 12 files changed, 54 insertions(+), 18 deletions(-) diff --git a/lib/actions/api.js b/lib/actions/api.js index 4731a9353..c13dec433 100644 --- a/lib/actions/api.js +++ b/lib/actions/api.js @@ -3,6 +3,7 @@ import { push, replace } from 'connected-react-router' import haversine from 'haversine' import moment from 'moment' import hash from 'object-hash' +import { hasCar } from '@opentripplanner/core-utils/lib/itinerary' import { getTripOptionsFromQuery, getUrlParams } from '@opentripplanner/core-utils/lib/query' import queryParams from '@opentripplanner/core-utils/lib/query-params' import { randId } from '@opentripplanner/core-utils/lib/storage' @@ -11,7 +12,6 @@ import { createAction } from 'redux-actions' import qs from 'qs' import { rememberPlace } from './map' -import { hasCar } from '../util/itinerary' import { getStopViewerConfig, queryIsValid } from '../util/state' if (typeof (fetch) === 'undefined') require('isomorphic-fetch') diff --git a/lib/components/map/leg-diagram.js b/lib/components/map/leg-diagram.js index 901c49608..0f4ae9fd4 100644 --- a/lib/components/map/leg-diagram.js +++ b/lib/components/map/leg-diagram.js @@ -1,12 +1,12 @@ import memoize from 'lodash.memoize' -import React, {Component} from 'react' +import { getElevationProfile, getTextWidth, legElevationAtDistance } from '@opentripplanner/core-utils/lib/itinerary' import PropTypes from 'prop-types' +import React, {Component} from 'react' import { Button } from 'react-bootstrap' import { connect } from 'react-redux' import ReactResizeDetector from 'react-resize-detector' import { setElevationPoint, showLegDiagram } from '../../actions/map' -import { getElevationProfile, getTextWidth, legElevationAtDistance } from '../../util/itinerary' // Fixed dimensions for chart const height = 160 diff --git a/lib/components/narrative/default/access-leg.js b/lib/components/narrative/default/access-leg.js index 1101d3a38..e40157677 100644 --- a/lib/components/narrative/default/access-leg.js +++ b/lib/components/narrative/default/access-leg.js @@ -1,3 +1,4 @@ +import { getStepInstructions } from '@opentripplanner/core-utils/lib/itinerary' import { formatDuration } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' import React, {Component} from 'react' @@ -5,7 +6,6 @@ import React, {Component} from 'react' import Icon from '../icon' import LegDiagramPreview from '../leg-diagram-preview' import { distanceString } from '../../../util/distance' -import { getStepInstructions } from '../../../util/itinerary' /** * Default access leg component for narrative itinerary. diff --git a/lib/components/narrative/default/itinerary-details.js b/lib/components/narrative/default/itinerary-details.js index 2eec48805..b12d214b3 100644 --- a/lib/components/narrative/default/itinerary-details.js +++ b/lib/components/narrative/default/itinerary-details.js @@ -1,9 +1,9 @@ -import React, { Component } from 'react' +import { isTransit } from '@opentripplanner/core-utils/lib/itinerary' import PropTypes from 'prop-types' +import React, { Component } from 'react' import AccessLeg from './access-leg' import TransitLeg from './transit-leg' -import { isTransit } from '../../../util/itinerary' export default class ItineraryDetails extends Component { static propTypes = { diff --git a/lib/components/narrative/default/tnc-leg.js b/lib/components/narrative/default/tnc-leg.js index 905f0da4e..d7d712f51 100644 --- a/lib/components/narrative/default/tnc-leg.js +++ b/lib/components/narrative/default/tnc-leg.js @@ -1,4 +1,5 @@ import currencyFormatter from 'currency-formatter' +import { toSentenceCase } from '@opentripplanner/core-utils/lib/itinerary' import { formatDuration } from '@opentripplanner/core-utils/lib/time' import { isMobile } from '@opentripplanner/core-utils/lib/ui' import PropTypes from 'prop-types' @@ -9,7 +10,6 @@ import { getTransportationNetworkCompanyEtaEstimate, getTransportationNetworkCompanyRideEstimate } from '../../../actions/api' -import { toSentenceCase } from '../../../util/itinerary' class TransportationNetworkCompanyLeg extends Component { static propTypes = { diff --git a/lib/components/narrative/default/transit-leg.js b/lib/components/narrative/default/transit-leg.js index aef7c13eb..b6a6eefe0 100644 --- a/lib/components/narrative/default/transit-leg.js +++ b/lib/components/narrative/default/transit-leg.js @@ -1,3 +1,4 @@ +import { getMapColor } from '@opentripplanner/core-utils/lib/itinerary' import { formatDuration, formatTime } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' import React, { Component } from 'react' @@ -6,7 +7,6 @@ import Icon from '../icon' import ModeIcon from '../../icons/mode-icon' import ViewTripButton from '../../viewers/view-trip-button' import ViewStopButton from '../../viewers/view-stop-button' -import { getMapColor } from '../../../util/itinerary' export default class TransitLeg extends Component { static propTypes = { diff --git a/lib/components/narrative/leg-diagram-preview.js b/lib/components/narrative/leg-diagram-preview.js index 5a736e5b9..6ce1ed09d 100644 --- a/lib/components/narrative/leg-diagram-preview.js +++ b/lib/components/narrative/leg-diagram-preview.js @@ -1,10 +1,10 @@ -import React, {Component} from 'react' +import { getElevationProfile } from '@opentripplanner/core-utils/lib/itinerary' import PropTypes from 'prop-types' +import React, {Component} from 'react' import { connect } from 'react-redux' import ReactResizeDetector from 'react-resize-detector' import { showLegDiagram } from '../../actions/map' -import { getElevationProfile } from '../../util/itinerary' const METERS_TO_FEET = 3.28084 diff --git a/lib/components/narrative/line-itin/itin-summary.js b/lib/components/narrative/line-itin/itin-summary.js index 8766cedeb..20c69ebb7 100644 --- a/lib/components/narrative/line-itin/itin-summary.js +++ b/lib/components/narrative/line-itin/itin-summary.js @@ -1,8 +1,9 @@ +import { calculateFares, calculatePhysicalActivity, isTransit } from '@opentripplanner/core-utils/lib/itinerary' import { formatDuration, formatTime } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' import React, { Component } from 'react' -import { calculateFares, calculatePhysicalActivity, getLegIcon, isTransit } from '../../../util/itinerary' +import { getLegIcon } from '../../../util/itinerary' // TODO: make this a prop const defaultRouteColor = '#008' diff --git a/lib/components/narrative/line-itin/line-itinerary.js b/lib/components/narrative/line-itin/line-itinerary.js index 2ca69e267..d1257dae9 100644 --- a/lib/components/narrative/line-itin/line-itinerary.js +++ b/lib/components/narrative/line-itin/line-itinerary.js @@ -1,8 +1,8 @@ +import { getLegModeLabel, getTimeZoneOffset, isTransit } from '@opentripplanner/core-utils/lib/itinerary' import React from 'react' import NarrativeItinerary from '../narrative-itinerary' import SimpleRealtimeAnnotation from '../simple-realtime-annotation' -import { getLegModeLabel, getTimeZoneOffset, isTransit } from '../../../util/itinerary' import ItinerarySummary from './itin-summary' import ItineraryBody from './connected-itinerary-body' diff --git a/lib/components/narrative/tabbed-itineraries.js b/lib/components/narrative/tabbed-itineraries.js index b707b822a..9b440aa3a 100644 --- a/lib/components/narrative/tabbed-itineraries.js +++ b/lib/components/narrative/tabbed-itineraries.js @@ -1,3 +1,4 @@ +import { calculateFares, calculatePhysicalActivity, getTimeZoneOffset } from '@opentripplanner/core-utils/lib/itinerary' import { formatDuration, formatTime, getTimeFormat } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' import React, { Component } from 'react' @@ -7,7 +8,6 @@ import { connect } from 'react-redux' import { setActiveItinerary, setActiveLeg, setActiveStep, setUseRealtimeResponse } from '../../actions/narrative' import DefaultItinerary from './default/default-itinerary' import { getActiveSearch, getRealtimeEffects } from '../../util/state' -import { calculateFares, calculatePhysicalActivity, getTimeZoneOffset } from '../../util/itinerary' class TabbedItineraries extends Component { static propTypes = { diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index 065f90270..4008e2f01 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -2,6 +2,7 @@ import clone from 'clone' import update from 'immutability-helper' import isEqual from 'lodash.isequal' import objectPath from 'object-path' +import { isTransit, getTransitModes } from '@opentripplanner/core-utils/lib/itinerary' import { matchLatLon } from '@opentripplanner/core-utils/lib/map' import { filterProfileOptions } from '@opentripplanner/core-utils/lib/profile' import { @@ -12,7 +13,6 @@ import { import { getItem, removeItem, storeItem } from '@opentripplanner/core-utils/lib/storage' import { getUserTimezone } from '@opentripplanner/core-utils/lib/time' -import { isTransit, getTransitModes } from '../util/itinerary' import { MainPanelContent, MobileScreens } from '../actions/ui' const MAX_RECENT_STORAGE = 5 diff --git a/lib/util/itinerary.js b/lib/util/itinerary.js index df4196484..e145dd9fa 100644 --- a/lib/util/itinerary.js +++ b/lib/util/itinerary.js @@ -8,16 +8,17 @@ import ModeIcon from '../components/icons/mode-icon' // All OTP transit modes export const transitModes = ['TRAM', 'BUS', 'SUBWAY', 'FERRY', 'RAIL', 'GONDOLA'] +// Good to go. /** * @param {config} config OTP-RR configuration object * @return {Array} List of all transit modes defined in config; otherwise default mode list */ - export function getTransitModes (config) { if (!config || !config.modes || !config.modes.transitModes) return transitModes return config.modes.transitModes.map(tm => tm.mode) } +// Good to go. export function isTransit (mode) { return transitModes.includes(mode) || mode === 'TRANSIT' } @@ -33,6 +34,7 @@ export function hasTransit (modesStr) { return false } +// CHECK arg /** * @param {string} modesStr a comma-separated list of OTP modes * @return {boolean} whether any of the modes are car-based modes @@ -46,6 +48,7 @@ export function hasCar (modesStr) { return false } +// CHECK arg /** * @param {string} modesStr a comma-separated list of OTP modes * @return {boolean} whether any of the modes are bicycle-based modes @@ -59,6 +62,7 @@ export function hasBike (modesStr) { return false } +// CHECK arg /** * @param {string} modesStr a comma-separated list of OTP modes * @return {boolean} whether any of the modes are micromobility-based modes @@ -72,6 +76,7 @@ export function hasMicromobility (modesStr) { return false } +// CHECK arg /** * @param {string} modesStr a comma-separated list of OTP modes * @return {boolean} whether any of the modes is a hailing mode @@ -85,6 +90,7 @@ export function hasHail (modesStr) { return false } +// CHECK arg /** * @param {string} modesStr a comma-separated list of OTP modes * @return {boolean} whether any of the modes is a rental mode @@ -98,34 +104,40 @@ export function hasRental (modesStr) { return false } +// Good to go. export function isWalk (mode) { if (!mode) return false return mode === 'WALK' } +// Good to go. export function isBicycle (mode) { if (!mode) return false return mode === 'BICYCLE' } +// Good to go. export function isBicycleRent (mode) { if (!mode) return false return mode === 'BICYCLE_RENT' } +// Good to go. export function isCar (mode) { if (!mode) return false return mode.startsWith('CAR') } +// Good to go. export function isMicromobility (mode) { if (!mode) return false return mode.startsWith('MICROMOBILITY') } +// Good to go. export function isAccessMode (mode) { return isWalk(mode) || isBicycle(mode) || @@ -134,6 +146,7 @@ export function isAccessMode (mode) { isMicromobility(mode) } +// Good to go. export function getMapColor (mode) { mode = mode || this.get('mode') if (mode === 'WALK') return '#444' @@ -148,6 +161,7 @@ export function getMapColor (mode) { return '#aaa' } +// Good to go. // TODO: temporary code; handle via migrated OTP i18n language table export function getStepDirection (step) { switch (step.relativeDirection) { @@ -168,17 +182,20 @@ export function getStepDirection (step) { return step.relativeDirection } +// Good to go. export function getStepInstructions (step) { const conjunction = step.relativeDirection === 'ELEVATOR' ? 'to' : 'on' return `${getStepDirection(step)} ${conjunction} ${step.streetName}` } +// Good to go. export function getStepStreetName (step) { if (step.streetName === 'road') return 'Unnamed Road' if (step.streetName === 'path') return 'Unnamed Path' return step.streetName } +// Good to go. export function getLegModeLabel (leg) { switch (leg.mode) { case 'BICYCLE_RENT': return 'Biketown' @@ -217,6 +234,7 @@ export function getIcon (iconId, customIcons) { return } +// DIFFERENT!! export function getItineraryBounds (itinerary) { let coords = [] itinerary.legs.forEach(leg => { @@ -228,6 +246,7 @@ export function getItineraryBounds (itinerary) { return latLngBounds(coords) } +// DIFFERENT! /** * Return a leaflet LatLngBounds object that encloses the given leg's geometry. */ @@ -245,6 +264,7 @@ export function getLegBounds (leg) { return latLngBounds(coords) } +// CHECK THIS ONE /** * Gets the desired sort values according to an optional getter function. If the * getter function is not defined, the original sort values are returned. @@ -294,6 +314,7 @@ const routeTypeComparatorValue = { 12: modeComparatorValue.RAIL // - Monorail. } +// CHECK THIS ONE // Gets a comparator value for a given route's type (OTP mode). // Note: JSDoc format not used to avoid bug in documentationjs. // ttps://github.com/documentationjs/documentation/issues/372 @@ -314,6 +335,7 @@ function getRouteTypeComparatorValue (route) { } } +// CHANGED /** * Calculates the sort comparator value given two routes based off of route type * (OTP mode). @@ -334,7 +356,7 @@ function startsWithAlphabeticCharacter (val) { } return false } - +// Check this one => Replace with routeComparator. /** * Sorts routes based off of whether the shortName begins with an alphabetic * character. Routes with shortn that do start with an alphabetic character will @@ -420,6 +442,7 @@ function makeStringValueComparator (objGetterFn) { } } +// CHECK THIS ONE /** * OpenTripPlanner sets the routeSortOrder to -999 by default. So, if that value * is encountered, assume that it actually means that the routeSortOrder is not @@ -453,6 +476,7 @@ function makeMultiCriteriaSort (...criteria) { } } +// Move this and dependent functions to OTP-UI. /** * Compares routes for the purposes of sorting and displaying in a user * interface. Due to GTFS feeds having varying levels of data quality, a multi- @@ -484,8 +508,8 @@ export const routeComparator = makeMultiCriteriaSort( makeStringValueComparator(obj => obj.longName) ) +// Good to go. /* Returns an interpolated lat-lon at a specified distance along a leg */ - export function legLocationAtDistance (leg, distance) { if (!leg.legGeometry) return null @@ -503,8 +527,8 @@ export function legLocationAtDistance (leg, distance) { return null } +// Good to go. /* Returns an interpolated elevation at a specified distance along a leg */ - export function legElevationAtDistance (points, distance) { // Iterate through the combined elevation profile let traversed = 0 @@ -533,6 +557,7 @@ export function legElevationAtDistance (points, distance) { return null } +// Good to go. // Iterate through the steps, building the array of elevation points and // keeping track of the minimum and maximum elevations reached export function getElevationProfile (steps, unitConversion = 1) { @@ -574,6 +599,7 @@ export function getElevationProfile (steps, unitConversion = 1) { return { maxElev, minElev, points, traversed, gain, loss } } +// Good to go. /** * Uses canvas.measureText to compute and return the width of the given text of given font in pixels. * @@ -591,6 +617,7 @@ export function getTextWidth (text, font = '22px Arial') { return metrics.width } +// Good to go. export function toSentenceCase (str) { if (str == null) { return '' @@ -634,6 +661,7 @@ export function getLegIcon (leg, customIcons) { return getIcon(iconStr, customIcons) } +// Good to go. /** * Get the configured company object for the given network string if the company * has been defined in the provided companies array config. @@ -646,6 +674,7 @@ function getCompanyForNetwork (networkString, companies = []) { return company } +// Good to go. /** * Get a string label to display from a list of vehicle rental networks. * @@ -660,6 +689,7 @@ export function getCompaniesLabelFromNetworks (networks, companies = []) { .join('/') } +// Good to go. /** * Returns mode name by checking the vertex type (VertexType class in OTP) for * the provided place. NOTE: this is currently only intended for vehicles at @@ -684,6 +714,7 @@ export function getModeForPlace (place) { } } +// Good to go. export function getPlaceName (place, companies) { // If address is provided (i.e. for carshare station, use it) if (place.address) return place.address.split(',')[0] @@ -701,11 +732,13 @@ export function getPlaceName (place, companies) { return place.name } +// Good to go. export function getTNCLocation (leg, type) { const location = leg[type] return `${location.lat.toFixed(5)},${location.lon.toFixed(5)}` } +// Good to go. export function calculatePhysicalActivity (itinerary) { let walkDuration = 0 let bikeDuration = 0 @@ -723,6 +756,7 @@ export function calculatePhysicalActivity (itinerary) { } } +// Good to go. export function calculateFares (itinerary) { let transitFare = 0 let symbol = '$' // default to USD @@ -756,6 +790,7 @@ export function calculateFares (itinerary) { } } +// Good to go. export function getTimeZoneOffset (itinerary) { if (!itinerary.legs || !itinerary.legs.length) return 0 From 53ca03ced0db7f96989b12db83ecfbb2d480a37d Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 8 Apr 2020 15:29:09 -0400 Subject: [PATCH 22/31] refactor(util/itinerary): Remove unused code so far. --- __tests__/util/itinerary.js | 4 +- lib/util/itinerary.js | 471 +----------------------------------- 2 files changed, 5 insertions(+), 470 deletions(-) diff --git a/__tests__/util/itinerary.js b/__tests__/util/itinerary.js index 74e5d5a56..83b06f3db 100644 --- a/__tests__/util/itinerary.js +++ b/__tests__/util/itinerary.js @@ -1,4 +1,6 @@ -import {isTransit, routeComparator} from '../../lib/util/itinerary' +import { isTransit } from '@opentripplanner/core-utils/lib/itinerary' + +import { routeComparator } from '../../lib/util/itinerary' const { route1, diff --git a/lib/util/itinerary.js b/lib/util/itinerary.js index 1e7461604..e58f1626a 100644 --- a/lib/util/itinerary.js +++ b/lib/util/itinerary.js @@ -1,210 +1,5 @@ import { latLngBounds } from 'leaflet' import polyline from '@mapbox/polyline' -import turfAlong from '@turf/along' - -// All OTP transit modes -export const transitModes = ['TRAM', 'BUS', 'SUBWAY', 'FERRY', 'RAIL', 'GONDOLA'] - -// Good to go. -/** - * @param {config} config OTP-RR configuration object - * @return {Array} List of all transit modes defined in config; otherwise default mode list - */ -export function getTransitModes (config) { - if (!config || !config.modes || !config.modes.transitModes) return transitModes - return config.modes.transitModes.map(tm => tm.mode) -} - -// Good to go. -export function isTransit (mode) { - return transitModes.includes(mode) || mode === 'TRANSIT' -} - -/** - * @param {string} modesStr a comma-separated list of OTP modes - * @return {boolean} whether any of the modes are transit modes - */ -export function hasTransit (modesStr) { - for (const mode of modesStr.split(',')) { - if (isTransit(mode)) return true - } - return false -} - -// CHECK arg -/** - * @param {string} modesStr a comma-separated list of OTP modes - * @return {boolean} whether any of the modes are car-based modes - */ -export function hasCar (modesStr) { - if (modesStr) { - for (const mode of modesStr.split(',')) { - if (isCar(mode)) return true - } - } - return false -} - -// CHECK arg -/** - * @param {string} modesStr a comma-separated list of OTP modes - * @return {boolean} whether any of the modes are bicycle-based modes - */ -export function hasBike (modesStr) { - if (modesStr) { - for (const mode of modesStr.split(',')) { - if (isBicycle(mode) || isBicycleRent(mode)) return true - } - } - return false -} - -// CHECK arg -/** - * @param {string} modesStr a comma-separated list of OTP modes - * @return {boolean} whether any of the modes are micromobility-based modes - */ -export function hasMicromobility (modesStr) { - if (modesStr) { - for (const mode of modesStr.split(',')) { - if (isMicromobility(mode)) return true - } - } - return false -} - -// CHECK arg -/** - * @param {string} modesStr a comma-separated list of OTP modes - * @return {boolean} whether any of the modes is a hailing mode - */ -export function hasHail (modesStr) { - if (modesStr) { - for (const mode of modesStr.split(',')) { - if (mode.indexOf('_HAIL') > -1) return true - } - } - return false -} - -// CHECK arg -/** - * @param {string} modesStr a comma-separated list of OTP modes - * @return {boolean} whether any of the modes is a rental mode - */ -export function hasRental (modesStr) { - if (modesStr) { - for (const mode of modesStr.split(',')) { - if (mode.indexOf('_RENT') > -1) return true - } - } - return false -} - -// Good to go. -export function isWalk (mode) { - if (!mode) return false - - return mode === 'WALK' -} - -// Good to go. -export function isBicycle (mode) { - if (!mode) return false - - return mode === 'BICYCLE' -} - -// Good to go. -export function isBicycleRent (mode) { - if (!mode) return false - - return mode === 'BICYCLE_RENT' -} - -// Good to go. -export function isCar (mode) { - if (!mode) return false - return mode.startsWith('CAR') -} - -// Good to go. -export function isMicromobility (mode) { - if (!mode) return false - return mode.startsWith('MICROMOBILITY') -} - -// Good to go. -export function isAccessMode (mode) { - return isWalk(mode) || - isBicycle(mode) || - isBicycleRent(mode) || - isCar(mode) || - isMicromobility(mode) -} - -// Good to go. -export function getMapColor (mode) { - mode = mode || this.get('mode') - if (mode === 'WALK') return '#444' - if (mode === 'BICYCLE') return '#0073e5' - if (mode === 'SUBWAY') return '#f00' - if (mode === 'RAIL') return '#b00' - if (mode === 'BUS') return '#080' - if (mode === 'TRAM') return '#800' - if (mode === 'FERRY') return '#008' - if (mode === 'CAR') return '#444' - if (mode === 'MICROMOBILITY') return '#f5a729' - return '#aaa' -} - -// Good to go. -// TODO: temporary code; handle via migrated OTP i18n language table -export function getStepDirection (step) { - switch (step.relativeDirection) { - case 'DEPART': return 'Head ' + step.absoluteDirection.toLowerCase() - case 'LEFT': return 'Left' - case 'HARD_LEFT': return 'Hard left' - case 'SLIGHTLY_LEFT': return 'Slight left' - case 'CONTINUE': return 'Continue' - case 'SLIGHTLY_RIGHT': return 'Slight right' - case 'RIGHT': return 'Right' - case 'HARD_RIGHT': return 'Hard right' - case 'CIRCLE_CLOCKWISE': return 'Follow circle clockwise' - case 'CIRCLE_COUNTERCLOCKWISE': return 'Follow circle counterclockwise' - case 'ELEVATOR': return 'Take elevator' - case 'UTURN_LEFT': return 'Left U-turn' - case 'UTURN_RIGHT': return 'Right U-turn' - } - return step.relativeDirection -} - -// Good to go. -export function getStepInstructions (step) { - const conjunction = step.relativeDirection === 'ELEVATOR' ? 'to' : 'on' - return `${getStepDirection(step)} ${conjunction} ${step.streetName}` -} - -// Good to go. -export function getStepStreetName (step) { - if (step.streetName === 'road') return 'Unnamed Road' - if (step.streetName === 'path') return 'Unnamed Path' - return step.streetName -} - -// Good to go. -export function getLegModeLabel (leg) { - switch (leg.mode) { - case 'BICYCLE_RENT': return 'Biketown' - case 'CAR': return leg.hailedCar ? 'Ride' : 'Drive' - case 'GONDOLA': return 'Aerial Tram' - case 'TRAM': - if (leg.routeLongName.toLowerCase().indexOf('streetcar') !== -1) return 'Streetcar' - return 'Light Rail' - case 'MICROMOBILITY': return 'Ride' - } - return toSentenceCase(leg.mode) -} // DIFFERENT export function getItineraryBounds (itinerary) { @@ -218,7 +13,7 @@ export function getItineraryBounds (itinerary) { return latLngBounds(coords) } -// DIFFERENT! +// DIFFERENT /** * Return a leaflet LatLngBounds object that encloses the given leg's geometry. */ @@ -236,7 +31,6 @@ export function getLegBounds (leg) { return latLngBounds(coords) } -// CHECK THIS ONE /** * Gets the desired sort values according to an optional getter function. If the * getter function is not defined, the original sort values are returned. @@ -286,7 +80,6 @@ const routeTypeComparatorValue = { 12: modeComparatorValue.RAIL // - Monorail. } -// CHECK THIS ONE // Gets a comparator value for a given route's type (OTP mode). // Note: JSDoc format not used to avoid bug in documentationjs. // ttps://github.com/documentationjs/documentation/issues/372 @@ -307,7 +100,6 @@ function getRouteTypeComparatorValue (route) { } } -// CHANGED /** * Calculates the sort comparator value given two routes based off of route type * (OTP mode). @@ -328,7 +120,7 @@ function startsWithAlphabeticCharacter (val) { } return false } -// Check this one => Replace with routeComparator. + /** * Sorts routes based off of whether the shortName begins with an alphabetic * character. Routes with shortn that do start with an alphabetic character will @@ -414,7 +206,6 @@ function makeStringValueComparator (objGetterFn) { } } -// CHECK THIS ONE /** * OpenTripPlanner sets the routeSortOrder to -999 by default. So, if that value * is encountered, assume that it actually means that the routeSortOrder is not @@ -448,7 +239,6 @@ function makeMultiCriteriaSort (...criteria) { } } -// Move this and dependent functions to OTP-UI. /** * Compares routes for the purposes of sorting and displaying in a user * interface. Due to GTFS feeds having varying levels of data quality, a multi- @@ -479,260 +269,3 @@ export const routeComparator = makeMultiCriteriaSort( makeStringValueComparator(obj => obj.shortName), makeStringValueComparator(obj => obj.longName) ) - -// Good to go. -/* Returns an interpolated lat-lon at a specified distance along a leg */ -export function legLocationAtDistance (leg, distance) { - if (!leg.legGeometry) return null - - try { - const line = polyline.toGeoJSON(leg.legGeometry.points) - const pt = turfAlong(line, distance, 'meters') - if (pt && pt.geometry && pt.geometry.coordinates) { - return [ - pt.geometry.coordinates[1], - pt.geometry.coordinates[0] - ] - } - } catch (e) { } - - return null -} - -// Good to go. -/* Returns an interpolated elevation at a specified distance along a leg */ -export function legElevationAtDistance (points, distance) { - // Iterate through the combined elevation profile - let traversed = 0 - // If first point distance is not zero, insert starting point at zero with - // null elevation. Encountering this value should trigger the warning below. - if (points[0][0] > 0) { - points.unshift([0, null]) - } - for (let i = 1; i < points.length; i++) { - const start = points[i - 1] - const elevDistanceSpan = points[i][0] - start[0] - if (distance >= traversed && distance <= traversed + elevDistanceSpan) { - // Distance falls within this point and the previous one; - // compute & return iterpolated elevation value - if (start[1] === null) { - console.warn('Elevation value does not exist for distance.', distance, traversed) - return null - } - const pct = (distance - traversed) / elevDistanceSpan - const elevSpan = points[i][1] - start[1] - return start[1] + elevSpan * pct - } - traversed += elevDistanceSpan - } - console.warn('Elevation value does not exist for distance.', distance, traversed) - return null -} - -// Good to go. -// Iterate through the steps, building the array of elevation points and -// keeping track of the minimum and maximum elevations reached -export function getElevationProfile (steps, unitConversion = 1) { - let minElev = 100000 - let maxElev = -100000 - let traversed = 0 - let gain = 0 - let loss = 0 - let previous = null - const points = [] - steps.forEach((step, stepIndex) => { - if (!step.elevation || step.elevation.length === 0) { - traversed += step.distance - return - } - for (let i = 0; i < step.elevation.length; i++) { - const elev = step.elevation[i] - if (previous) { - const diff = (elev.second - previous.second) * unitConversion - if (diff > 0) gain += diff - else loss += diff - } - if (i === 0 && elev.first !== 0) { - // console.warn(`No elevation data available for step ${stepIndex}-${i} at beginning of segment`, elev) - } - const convertedElevation = elev.second * unitConversion - if (convertedElevation < minElev) minElev = convertedElevation - if (convertedElevation > maxElev) maxElev = convertedElevation - points.push([traversed + elev.first, elev.second]) - // Insert "filler" point if the last point in elevation profile does not - // reach the full distance of the step. - if (i === step.elevation.length - 1 && elev.first !== step.distance) { - // points.push([traversed + step.distance, elev.second]) - } - previous = elev - } - traversed += step.distance - }) - return { maxElev, minElev, points, traversed, gain, loss } -} - -// Good to go. -/** - * Uses canvas.measureText to compute and return the width of the given text of given font in pixels. - * - * @param {string} text The text to be rendered. - * @param {string} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana"). - * - * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393 - */ -export function getTextWidth (text, font = '22px Arial') { - // re-use canvas object for better performance - var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement('canvas')) - var context = canvas.getContext('2d') - context.font = font - var metrics = context.measureText(text) - return metrics.width -} - -// Good to go. -export function toSentenceCase (str) { - if (str == null) { - return '' - } - str = String(str) - return str.charAt(0).toUpperCase() + str.substr(1).toLowerCase() -} - -// Good to go. -/** - * Get the configured company object for the given network string if the company - * has been defined in the provided companies array config. - */ -function getCompanyForNetwork (networkString, companies = []) { - const company = companies.find(co => co.id === networkString) - if (!company) { - console.warn(`No company found in config.yml that matches rented vehicle network: ${networkString}`, companies) - } - return company -} - -// Good to go. -/** - * Get a string label to display from a list of vehicle rental networks. - * - * @param {Array} networks A list of network ids. - * @param {Array} [companies=[]] An optional list of the companies config. - * @return {string} A label for use in presentation on a website. - */ -export function getCompaniesLabelFromNetworks (networks, companies = []) { - return networks.map(network => getCompanyForNetwork(network, companies)) - .filter(co => !!co) - .map(co => co.label) - .join('/') -} - -// Good to go. -/** - * Returns mode name by checking the vertex type (VertexType class in OTP) for - * the provided place. NOTE: this is currently only intended for vehicles at - * the moment (not transit or walking). - * - * TODO: I18N - * @param {string} place place from itinerary leg - */ -export function getModeForPlace (place) { - switch (place.vertexType) { - case 'CARSHARE': - return 'car' - case 'VEHICLERENTAL': - return 'E-scooter' - // TODO: Should the type change depending on bike vertex type? - case 'BIKESHARE': - case 'BIKEPARK': - return 'bike' - // If company offers more than one mode, default to `vehicle` string. - default: - return 'vehicle' - } -} - -// Good to go. -export function getPlaceName (place, companies) { - // If address is provided (i.e. for carshare station, use it) - if (place.address) return place.address.split(',')[0] - if (place.networks && place.vertexType === 'VEHICLERENTAL') { - // For vehicle rental pick up, do not use the place name. Rather, use - // company name + vehicle type (e.g., SPIN E-scooter). Place name is often just - // a UUID that has no relevance to the actual vehicle. For bikeshare, however, - // there are often hubs or bikes that have relevant names to the user. - const company = getCompanyForNetwork(place.networks[0], companies) - if (company) { - return `${company.label} ${getModeForPlace(place)}` - } - } - // Default to place name - return place.name -} - -// Good to go. -export function getTNCLocation (leg, type) { - const location = leg[type] - return `${location.lat.toFixed(5)},${location.lon.toFixed(5)}` -} - -// Good to go. -export function calculatePhysicalActivity (itinerary) { - let walkDuration = 0 - let bikeDuration = 0 - for (const leg of itinerary.legs) { - if (leg.mode.startsWith('WALK')) walkDuration += leg.duration - if (leg.mode.startsWith('BICYCLE')) bikeDuration += leg.duration - } - const caloriesBurned = - walkDuration / 3600 * 280 + - bikeDuration / 3600 * 290 - return { - bikeDuration, - caloriesBurned, - walkDuration - } -} - -// Good to go. -export function calculateFares (itinerary) { - let transitFare = 0 - let symbol = '$' // default to USD - let dollarsToString = dollars => `${symbol}${dollars.toFixed(2)}` - let centsToString = cents => `${symbol}${(cents / Math.pow(10, 2)).toFixed(2)}` - if (itinerary.fare && itinerary.fare.fare && itinerary.fare.fare.regular) { - const reg = itinerary.fare.fare.regular - symbol = reg.currency.symbol - transitFare = reg.cents - centsToString = cents => `${symbol}${(cents / Math.pow(10, reg.currency.defaultFractionDigits)).toFixed(reg.currency.defaultFractionDigits)}` - dollarsToString = dollars => `${symbol}${dollars.toFixed(2)}` - } - - // Process any TNC fares - let minTNCFare = 0 - let maxTNCFare = 0 - for (const leg of itinerary.legs) { - if (leg.mode === 'CAR' && leg.hailedCar && leg.tncData) { - const { maxCost, minCost } = leg.tncData - // TODO: Support non-USD - minTNCFare += minCost - maxTNCFare += maxCost - } - } - return { - centsToString, - dollarsToString, - maxTNCFare, - minTNCFare, - transitFare - } -} - -// Good to go. -export function getTimeZoneOffset (itinerary) { - if (!itinerary.legs || !itinerary.legs.length) return 0 - - // Determine if there is a DST offset between now and the itinerary start date - const dstOffset = new Date(itinerary.startTime).getTimezoneOffset() - new Date().getTimezoneOffset() - - return itinerary.legs[0].agencyTimeZoneOffset + (new Date().getTimezoneOffset() + dstOffset) * 60000 -} From 119268136e13bbd0db7c528679044a7a3b3b86cf Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 24 Apr 2020 10:17:13 -0400 Subject: [PATCH 23/31] refactor(core-utils): Initial migration over to OTP-ui core-utils. --- lib/components/form/default-search-form.js | 12 +- .../narrative/default/transit-leg.js | 9 +- .../narrative/itinerary-carousel.js | 39 +- .../narrative/line-itin/itin-summary.js | 22 +- .../narrative/tabbed-itineraries.js | 8 +- lib/index.js | 2 + lib/util/itinerary.js | 437 +----------------- 7 files changed, 40 insertions(+), 489 deletions(-) diff --git a/lib/components/form/default-search-form.js b/lib/components/form/default-search-form.js index 1f2578231..7dfe3a266 100644 --- a/lib/components/form/default-search-form.js +++ b/lib/components/form/default-search-form.js @@ -1,11 +1,17 @@ -import React, { Component } from 'react' import PropTypes from 'prop-types' +import React, { Component } from 'react' import LocationField from './connected-location-field' -import SwitchButton from './switch-button' import TabbedFormPanel from './tabbed-form-panel' +import SwitchButton from './switch-button' import defaultIcons from '../icons' +// Use the icons props to override icons for certain modes in the mode selector panel. +// If no icon is provided for a specific mode, the default OTP-ui icon will be used. +// const customIcons = { +// TRANSIT: +// } + export default class DefaultSearchForm extends Component { static propTypes = { icons: PropTypes.object, @@ -13,7 +19,7 @@ export default class DefaultSearchForm extends Component { } static defaultProps = { - icons: defaultIcons, + icons: defaultIcons, // customIcons, showFrom: true, showTo: true } diff --git a/lib/components/narrative/default/transit-leg.js b/lib/components/narrative/default/transit-leg.js index 5809f3f1b..b6a6eefe0 100644 --- a/lib/components/narrative/default/transit-leg.js +++ b/lib/components/narrative/default/transit-leg.js @@ -1,14 +1,13 @@ -import Icon from '../icon' -import React, { Component } from 'react' +import { getMapColor } from '@opentripplanner/core-utils/lib/itinerary' +import { formatDuration, formatTime } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' +import React, { Component } from 'react' +import Icon from '../icon' import ModeIcon from '../../icons/mode-icon' import ViewTripButton from '../../viewers/view-trip-button' import ViewStopButton from '../../viewers/view-stop-button' -import { getMapColor } from '../../../util/itinerary' -import { formatDuration, formatTime } from '../../../util/time' - export default class TransitLeg extends Component { static propTypes = { itinerary: PropTypes.object diff --git a/lib/components/narrative/itinerary-carousel.js b/lib/components/narrative/itinerary-carousel.js index ce7a373fa..0b9c828d4 100644 --- a/lib/components/narrative/itinerary-carousel.js +++ b/lib/components/narrative/itinerary-carousel.js @@ -1,5 +1,6 @@ -import React, { Component } from 'react' +import { getTimeFormat } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { Button } from 'react-bootstrap' import { connect } from 'react-redux' import SwipeableViews from 'react-swipeable-views' @@ -8,10 +9,7 @@ import { setActiveItinerary, setActiveLeg, setActiveStep } from '../../actions/n import Icon from './icon' import DefaultItinerary from './default/default-itinerary' import Loading from './loading' -import NarrativeProfileSummary from './narrative-profile-summary' import { getActiveItineraries, getActiveSearch } from '../../util/state' -import { profileOptionsToItineraries } from '../../util/profile' -import { getTimeFormat } from '../../util/time' class ItineraryCarousel extends Component { state = {} @@ -26,8 +24,6 @@ class ItineraryCarousel extends Component { setActiveLeg: PropTypes.func, setActiveStep: PropTypes.func, expanded: PropTypes.bool, - showProfileSummary: PropTypes.bool, - profileOptions: PropTypes.array, companies: PropTypes.string } @@ -54,18 +50,11 @@ class ItineraryCarousel extends Component { } render () { - const { activeItinerary, itineraries, itineraryClass, hideHeader, pending, showProfileSummary } = this.props + const { activeItinerary, itineraries, itineraryClass, hideHeader, pending } = this.props if (pending) return if (!itineraries) return null - let views = [] - if (showProfileSummary) { - views.push(
-
Your Best Options (Swipe to View All)
- -
) - } - views = views.concat(itineraries.map((itinerary, index) => { + const views = itineraries.map((itinerary, index) => { return React.createElement(itineraryClass, { itinerary, index, @@ -74,7 +63,7 @@ class ItineraryCarousel extends Component { onClick: this._onItineraryClick, ...this.props }) - })) + }) return (
@@ -112,23 +101,13 @@ class ItineraryCarousel extends Component { const mapStateToProps = (state, ownProps) => { const activeSearch = getActiveSearch(state.otp) - let itineraries = null - let profileOptions = null - let showProfileSummary = false - if (activeSearch && activeSearch.response && activeSearch.response.plan) { - itineraries = getActiveItineraries(state.otp) - } else if (activeSearch && activeSearch.response && activeSearch.response.otp) { - profileOptions = activeSearch.response.otp.profile - itineraries = profileOptionsToItineraries(profileOptions) - showProfileSummary = true - } + const itineraries = activeSearch && activeSearch.response && activeSearch.response.plan + ? getActiveItineraries(state.otp) + : null - const pending = activeSearch && activeSearch.pending return { itineraries, - profileOptions, - pending, - showProfileSummary, + pending: activeSearch && activeSearch.pending, activeItinerary: activeSearch && activeSearch.activeItinerary, activeLeg: activeSearch && activeSearch.activeLeg, activeStep: activeSearch && activeSearch.activeStep, diff --git a/lib/components/narrative/line-itin/itin-summary.js b/lib/components/narrative/line-itin/itin-summary.js index 2e74575a7..955491975 100644 --- a/lib/components/narrative/line-itin/itin-summary.js +++ b/lib/components/narrative/line-itin/itin-summary.js @@ -4,12 +4,8 @@ import React, { Component } from 'react' import styled from 'styled-components' import { - calculateFares, - calculatePhysicalActivity, - getLegIcon, - isTransit + getLegIcon } from '../../../util/itinerary' -import { formatDuration, formatTime } from '../../../util/time' // TODO: make this a prop const defaultRouteColor = '#008' @@ -48,7 +44,7 @@ const NonTransitSpacer = styled.div` overflow: hidden ` -const RoutePreivew = styled.div` +const RoutePreview = styled.div` display: inline-block; margin-left: 8px; vertical-align: top; @@ -93,22 +89,22 @@ export default class ItinerarySummary extends Component { maxTNCFare, minTNCFare, transitFare - } = calculateFares(itinerary) + } = coreUtils.itinerary.calculateFares(itinerary) // TODO: support non-USD const minTotalFare = minTNCFare * 100 + transitFare const maxTotalFare = maxTNCFare * 100 + transitFare - const { caloriesBurned } = calculatePhysicalActivity(itinerary) + const { caloriesBurned } = coreUtils.itinerary.calculatePhysicalActivity(itinerary) return (
{/* Travel time in hrs/mins */} -
{formatDuration(itinerary.duration)}
+
{coreUtils.time.formatDuration(itinerary.duration)}
{/* Duration as time range */} - {formatTime(itinerary.startTime, timeOptions)} - {formatTime(itinerary.endTime, timeOptions)} + {coreUtils.time.formatTime(itinerary.startTime, timeOptions)} - {coreUtils.time.formatTime(itinerary.endTime, timeOptions)} {/* Fare / Calories */} @@ -134,9 +130,9 @@ export default class ItinerarySummary extends Component { return !(leg.mode === 'WALK' && itinerary.transitTime > 0) }).map((leg, k) => { return ( - + {getLegIcon(leg, customIcons)} - {isTransit(leg.mode) + {coreUtils.itinerary.isTransit(leg.mode) ? ( {getRouteNameForBadge(leg)} @@ -144,7 +140,7 @@ export default class ItinerarySummary extends Component { ) : () } - + ) })} diff --git a/lib/components/narrative/tabbed-itineraries.js b/lib/components/narrative/tabbed-itineraries.js index f3c4e2d76..9b440aa3a 100644 --- a/lib/components/narrative/tabbed-itineraries.js +++ b/lib/components/narrative/tabbed-itineraries.js @@ -1,13 +1,13 @@ -import React, { Component } from 'react' +import { calculateFares, calculatePhysicalActivity, getTimeZoneOffset } from '@opentripplanner/core-utils/lib/itinerary' +import { formatDuration, formatTime, getTimeFormat } from '@opentripplanner/core-utils/lib/time' import PropTypes from 'prop-types' -import { connect } from 'react-redux' +import React, { Component } from 'react' import { Button } from 'react-bootstrap' +import { connect } from 'react-redux' import { setActiveItinerary, setActiveLeg, setActiveStep, setUseRealtimeResponse } from '../../actions/narrative' import DefaultItinerary from './default/default-itinerary' import { getActiveSearch, getRealtimeEffects } from '../../util/state' -import { calculateFares, calculatePhysicalActivity, getTimeZoneOffset } from '../../util/itinerary' -import { formatDuration, formatTime, getTimeFormat } from '../../util/time' class TabbedItineraries extends Component { static propTypes = { diff --git a/lib/index.js b/lib/index.js index 5fb277044..4ed9f8b55 100644 --- a/lib/index.js +++ b/lib/index.js @@ -23,6 +23,7 @@ import SimpleRealtimeAnnotation from './components/narrative/simple-realtime-ann import TransportationNetworkCompanyLeg from './components/narrative/default/tnc-leg' import TripDetails from './components/narrative/connected-trip-details' import TripTools from './components/narrative/trip-tools' +import LineItinerary from './components/narrative/line-itin/line-itinerary' import MobileMain from './components/mobile/main' @@ -66,6 +67,7 @@ export { // narrative components LegDiagramPreview, + LineItinerary, NarrativeItineraries, NarrativeItinerary, NarrativeRoutingResults, diff --git a/lib/util/itinerary.js b/lib/util/itinerary.js index df4196484..7134f7d55 100644 --- a/lib/util/itinerary.js +++ b/lib/util/itinerary.js @@ -1,196 +1,10 @@ -import React from 'react' import { latLngBounds } from 'leaflet' import polyline from '@mapbox/polyline' -import turfAlong from '@turf/along' +import React from 'react' import ModeIcon from '../components/icons/mode-icon' -// All OTP transit modes -export const transitModes = ['TRAM', 'BUS', 'SUBWAY', 'FERRY', 'RAIL', 'GONDOLA'] - -/** - * @param {config} config OTP-RR configuration object - * @return {Array} List of all transit modes defined in config; otherwise default mode list - */ - -export function getTransitModes (config) { - if (!config || !config.modes || !config.modes.transitModes) return transitModes - return config.modes.transitModes.map(tm => tm.mode) -} - -export function isTransit (mode) { - return transitModes.includes(mode) || mode === 'TRANSIT' -} - -/** - * @param {string} modesStr a comma-separated list of OTP modes - * @return {boolean} whether any of the modes are transit modes - */ -export function hasTransit (modesStr) { - for (const mode of modesStr.split(',')) { - if (isTransit(mode)) return true - } - return false -} - -/** - * @param {string} modesStr a comma-separated list of OTP modes - * @return {boolean} whether any of the modes are car-based modes - */ -export function hasCar (modesStr) { - if (modesStr) { - for (const mode of modesStr.split(',')) { - if (isCar(mode)) return true - } - } - return false -} - -/** - * @param {string} modesStr a comma-separated list of OTP modes - * @return {boolean} whether any of the modes are bicycle-based modes - */ -export function hasBike (modesStr) { - if (modesStr) { - for (const mode of modesStr.split(',')) { - if (isBicycle(mode) || isBicycleRent(mode)) return true - } - } - return false -} - -/** - * @param {string} modesStr a comma-separated list of OTP modes - * @return {boolean} whether any of the modes are micromobility-based modes - */ -export function hasMicromobility (modesStr) { - if (modesStr) { - for (const mode of modesStr.split(',')) { - if (isMicromobility(mode)) return true - } - } - return false -} - -/** - * @param {string} modesStr a comma-separated list of OTP modes - * @return {boolean} whether any of the modes is a hailing mode - */ -export function hasHail (modesStr) { - if (modesStr) { - for (const mode of modesStr.split(',')) { - if (mode.indexOf('_HAIL') > -1) return true - } - } - return false -} - -/** - * @param {string} modesStr a comma-separated list of OTP modes - * @return {boolean} whether any of the modes is a rental mode - */ -export function hasRental (modesStr) { - if (modesStr) { - for (const mode of modesStr.split(',')) { - if (mode.indexOf('_RENT') > -1) return true - } - } - return false -} - -export function isWalk (mode) { - if (!mode) return false - - return mode === 'WALK' -} - -export function isBicycle (mode) { - if (!mode) return false - - return mode === 'BICYCLE' -} - -export function isBicycleRent (mode) { - if (!mode) return false - - return mode === 'BICYCLE_RENT' -} - -export function isCar (mode) { - if (!mode) return false - return mode.startsWith('CAR') -} - -export function isMicromobility (mode) { - if (!mode) return false - return mode.startsWith('MICROMOBILITY') -} - -export function isAccessMode (mode) { - return isWalk(mode) || - isBicycle(mode) || - isBicycleRent(mode) || - isCar(mode) || - isMicromobility(mode) -} - -export function getMapColor (mode) { - mode = mode || this.get('mode') - if (mode === 'WALK') return '#444' - if (mode === 'BICYCLE') return '#0073e5' - if (mode === 'SUBWAY') return '#f00' - if (mode === 'RAIL') return '#b00' - if (mode === 'BUS') return '#080' - if (mode === 'TRAM') return '#800' - if (mode === 'FERRY') return '#008' - if (mode === 'CAR') return '#444' - if (mode === 'MICROMOBILITY') return '#f5a729' - return '#aaa' -} - -// TODO: temporary code; handle via migrated OTP i18n language table -export function getStepDirection (step) { - switch (step.relativeDirection) { - case 'DEPART': return 'Head ' + step.absoluteDirection.toLowerCase() - case 'LEFT': return 'Left' - case 'HARD_LEFT': return 'Hard left' - case 'SLIGHTLY_LEFT': return 'Slight left' - case 'CONTINUE': return 'Continue' - case 'SLIGHTLY_RIGHT': return 'Slight right' - case 'RIGHT': return 'Right' - case 'HARD_RIGHT': return 'Hard right' - case 'CIRCLE_CLOCKWISE': return 'Follow circle clockwise' - case 'CIRCLE_COUNTERCLOCKWISE': return 'Follow circle counterclockwise' - case 'ELEVATOR': return 'Take elevator' - case 'UTURN_LEFT': return 'Left U-turn' - case 'UTURN_RIGHT': return 'Right U-turn' - } - return step.relativeDirection -} - -export function getStepInstructions (step) { - const conjunction = step.relativeDirection === 'ELEVATOR' ? 'to' : 'on' - return `${getStepDirection(step)} ${conjunction} ${step.streetName}` -} - -export function getStepStreetName (step) { - if (step.streetName === 'road') return 'Unnamed Road' - if (step.streetName === 'path') return 'Unnamed Path' - return step.streetName -} - -export function getLegModeLabel (leg) { - switch (leg.mode) { - case 'BICYCLE_RENT': return 'Biketown' - case 'CAR': return leg.hailedCar ? 'Ride' : 'Drive' - case 'GONDOLA': return 'Aerial Tram' - case 'TRAM': - if (leg.routeLongName.toLowerCase().indexOf('streetcar') !== -1) return 'Streetcar' - return 'Light Rail' - case 'MICROMOBILITY': return 'Ride' - } - return toSentenceCase(leg.mode) -} +// DIFFERENT /** * Returns a react element of the desired icon. If customIcons are defined, then @@ -228,6 +42,7 @@ export function getItineraryBounds (itinerary) { return latLngBounds(coords) } +// DIFFERENT /** * Return a leaflet LatLngBounds object that encloses the given leg's geometry. */ @@ -484,121 +299,6 @@ export const routeComparator = makeMultiCriteriaSort( makeStringValueComparator(obj => obj.longName) ) -/* Returns an interpolated lat-lon at a specified distance along a leg */ - -export function legLocationAtDistance (leg, distance) { - if (!leg.legGeometry) return null - - try { - const line = polyline.toGeoJSON(leg.legGeometry.points) - const pt = turfAlong(line, distance, 'meters') - if (pt && pt.geometry && pt.geometry.coordinates) { - return [ - pt.geometry.coordinates[1], - pt.geometry.coordinates[0] - ] - } - } catch (e) { } - - return null -} - -/* Returns an interpolated elevation at a specified distance along a leg */ - -export function legElevationAtDistance (points, distance) { - // Iterate through the combined elevation profile - let traversed = 0 - // If first point distance is not zero, insert starting point at zero with - // null elevation. Encountering this value should trigger the warning below. - if (points[0][0] > 0) { - points.unshift([0, null]) - } - for (let i = 1; i < points.length; i++) { - const start = points[i - 1] - const elevDistanceSpan = points[i][0] - start[0] - if (distance >= traversed && distance <= traversed + elevDistanceSpan) { - // Distance falls within this point and the previous one; - // compute & return iterpolated elevation value - if (start[1] === null) { - console.warn('Elevation value does not exist for distance.', distance, traversed) - return null - } - const pct = (distance - traversed) / elevDistanceSpan - const elevSpan = points[i][1] - start[1] - return start[1] + elevSpan * pct - } - traversed += elevDistanceSpan - } - console.warn('Elevation value does not exist for distance.', distance, traversed) - return null -} - -// Iterate through the steps, building the array of elevation points and -// keeping track of the minimum and maximum elevations reached -export function getElevationProfile (steps, unitConversion = 1) { - let minElev = 100000 - let maxElev = -100000 - let traversed = 0 - let gain = 0 - let loss = 0 - let previous = null - const points = [] - steps.forEach((step, stepIndex) => { - if (!step.elevation || step.elevation.length === 0) { - traversed += step.distance - return - } - for (let i = 0; i < step.elevation.length; i++) { - const elev = step.elevation[i] - if (previous) { - const diff = (elev.second - previous.second) * unitConversion - if (diff > 0) gain += diff - else loss += diff - } - if (i === 0 && elev.first !== 0) { - // console.warn(`No elevation data available for step ${stepIndex}-${i} at beginning of segment`, elev) - } - const convertedElevation = elev.second * unitConversion - if (convertedElevation < minElev) minElev = convertedElevation - if (convertedElevation > maxElev) maxElev = convertedElevation - points.push([traversed + elev.first, elev.second]) - // Insert "filler" point if the last point in elevation profile does not - // reach the full distance of the step. - if (i === step.elevation.length - 1 && elev.first !== step.distance) { - // points.push([traversed + step.distance, elev.second]) - } - previous = elev - } - traversed += step.distance - }) - return { maxElev, minElev, points, traversed, gain, loss } -} - -/** - * Uses canvas.measureText to compute and return the width of the given text of given font in pixels. - * - * @param {string} text The text to be rendered. - * @param {string} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana"). - * - * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393 - */ -export function getTextWidth (text, font = '22px Arial') { - // re-use canvas object for better performance - var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement('canvas')) - var context = canvas.getContext('2d') - context.font = font - var metrics = context.measureText(text) - return metrics.width -} - -export function toSentenceCase (str) { - if (str == null) { - return '' - } - str = String(str) - return str.charAt(0).toUpperCase() + str.substr(1).toLowerCase() -} - /** * Return an icon depending on the leg info * @@ -633,134 +333,3 @@ export function getLegIcon (leg, customIcons) { return getIcon(iconStr, customIcons) } - -/** - * Get the configured company object for the given network string if the company - * has been defined in the provided companies array config. - */ -function getCompanyForNetwork (networkString, companies = []) { - const company = companies.find(co => co.id === networkString) - if (!company) { - console.warn(`No company found in config.yml that matches rented vehicle network: ${networkString}`, companies) - } - return company -} - -/** - * Get a string label to display from a list of vehicle rental networks. - * - * @param {Array} networks A list of network ids. - * @param {Array} [companies=[]] An optional list of the companies config. - * @return {string} A label for use in presentation on a website. - */ -export function getCompaniesLabelFromNetworks (networks, companies = []) { - return networks.map(network => getCompanyForNetwork(network, companies)) - .filter(co => !!co) - .map(co => co.label) - .join('/') -} - -/** - * Returns mode name by checking the vertex type (VertexType class in OTP) for - * the provided place. NOTE: this is currently only intended for vehicles at - * the moment (not transit or walking). - * - * TODO: I18N - * @param {string} place place from itinerary leg - */ -export function getModeForPlace (place) { - switch (place.vertexType) { - case 'CARSHARE': - return 'car' - case 'VEHICLERENTAL': - return 'E-scooter' - // TODO: Should the type change depending on bike vertex type? - case 'BIKESHARE': - case 'BIKEPARK': - return 'bike' - // If company offers more than one mode, default to `vehicle` string. - default: - return 'vehicle' - } -} - -export function getPlaceName (place, companies) { - // If address is provided (i.e. for carshare station, use it) - if (place.address) return place.address.split(',')[0] - if (place.networks && place.vertexType === 'VEHICLERENTAL') { - // For vehicle rental pick up, do not use the place name. Rather, use - // company name + vehicle type (e.g., SPIN E-scooter). Place name is often just - // a UUID that has no relevance to the actual vehicle. For bikeshare, however, - // there are often hubs or bikes that have relevant names to the user. - const company = getCompanyForNetwork(place.networks[0], companies) - if (company) { - return `${company.label} ${getModeForPlace(place)}` - } - } - // Default to place name - return place.name -} - -export function getTNCLocation (leg, type) { - const location = leg[type] - return `${location.lat.toFixed(5)},${location.lon.toFixed(5)}` -} - -export function calculatePhysicalActivity (itinerary) { - let walkDuration = 0 - let bikeDuration = 0 - for (const leg of itinerary.legs) { - if (leg.mode.startsWith('WALK')) walkDuration += leg.duration - if (leg.mode.startsWith('BICYCLE')) bikeDuration += leg.duration - } - const caloriesBurned = - walkDuration / 3600 * 280 + - bikeDuration / 3600 * 290 - return { - bikeDuration, - caloriesBurned, - walkDuration - } -} - -export function calculateFares (itinerary) { - let transitFare = 0 - let symbol = '$' // default to USD - let dollarsToString = dollars => `${symbol}${dollars.toFixed(2)}` - let centsToString = cents => `${symbol}${(cents / Math.pow(10, 2)).toFixed(2)}` - if (itinerary.fare && itinerary.fare.fare && itinerary.fare.fare.regular) { - const reg = itinerary.fare.fare.regular - symbol = reg.currency.symbol - transitFare = reg.cents - centsToString = cents => `${symbol}${(cents / Math.pow(10, reg.currency.defaultFractionDigits)).toFixed(reg.currency.defaultFractionDigits)}` - dollarsToString = dollars => `${symbol}${dollars.toFixed(2)}` - } - - // Process any TNC fares - let minTNCFare = 0 - let maxTNCFare = 0 - for (const leg of itinerary.legs) { - if (leg.mode === 'CAR' && leg.hailedCar && leg.tncData) { - const { maxCost, minCost } = leg.tncData - // TODO: Support non-USD - minTNCFare += minCost - maxTNCFare += maxCost - } - } - return { - centsToString, - dollarsToString, - maxTNCFare, - minTNCFare, - transitFare - } -} - -export function getTimeZoneOffset (itinerary) { - if (!itinerary.legs || !itinerary.legs.length) return 0 - - // Determine if there is a DST offset between now and the itinerary start date - const dstOffset = new Date(itinerary.startTime).getTimezoneOffset() - new Date().getTimezoneOffset() - - return itinerary.legs[0].agencyTimeZoneOffset + (new Date().getTimezoneOffset() + dstOffset) * 60000 -} From d00e8c94cc475f373587106d25d464804f911060 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 24 Apr 2020 11:21:50 -0400 Subject: [PATCH 24/31] refactor(itinerary): Use OTP-ui itinerary getLegBounds and getItineraryBounds. --- lib/components/map/bounds-updating-overlay.js | 16 +++++----- lib/util/itinerary.js | 30 ++++--------------- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/lib/components/map/bounds-updating-overlay.js b/lib/components/map/bounds-updating-overlay.js index 710b7e2d6..6472f62df 100644 --- a/lib/components/map/bounds-updating-overlay.js +++ b/lib/components/map/bounds-updating-overlay.js @@ -1,11 +1,11 @@ import isEqual from 'lodash.isequal' -import { isMobile } from '@opentripplanner/core-utils/lib/ui' -import { connect } from 'react-redux' +import coreUtils from '@opentripplanner/core-utils' import { MapLayer, withLeaflet } from 'react-leaflet' +import { connect } from 'react-redux' import { - getItineraryBounds, - getLegBounds + getLeafletItineraryBounds, + getLeafletLegBounds } from '../../util/itinerary' import { getActiveItinerary, getActiveSearch } from '../../util/state' @@ -46,10 +46,10 @@ class BoundsUpdatingOverlay extends MapLayer { // Fit map to to entire itinerary if active itinerary bounds changed const newFrom = newProps.query && newProps.query.from - const newItinBounds = newProps.itinerary && getItineraryBounds(newProps.itinerary) + const newItinBounds = newProps.itinerary && getLeafletItineraryBounds(newProps.itinerary) const newTo = newProps.query && newProps.query.to const oldFrom = oldProps.query && oldProps.query.from - const oldItinBounds = oldProps.itinerary && getItineraryBounds(oldProps.itinerary) + const oldItinBounds = oldProps.itinerary && getLeafletItineraryBounds(oldProps.itinerary) const oldTo = oldProps.query && oldProps.query.to const fromChanged = !isEqual(oldFrom, newFrom) const toChanged = !isEqual(oldTo, newTo) @@ -65,7 +65,7 @@ class BoundsUpdatingOverlay extends MapLayer { newProps.activeLeg !== null ) { map.fitBounds( - getLegBounds(newProps.itinerary.legs[newProps.activeLeg]), + getLeafletLegBounds(newProps.itinerary.legs[newProps.activeLeg]), { padding } ) @@ -80,7 +80,7 @@ class BoundsUpdatingOverlay extends MapLayer { // more info. // TODO: Fix this so mobile devices will also update the bounds to the // from/to locations. - if (!isMobile()) { + if (!coreUtils.ui.isMobile()) { map.fitBounds([ [newFrom.lat, newFrom.lon], [newTo.lat, newTo.lon] diff --git a/lib/util/itinerary.js b/lib/util/itinerary.js index 7134f7d55..e2abbd442 100644 --- a/lib/util/itinerary.js +++ b/lib/util/itinerary.js @@ -1,11 +1,9 @@ import { latLngBounds } from 'leaflet' -import polyline from '@mapbox/polyline' +import coreUtils from '@opentripplanner/core-utils' import React from 'react' import ModeIcon from '../components/icons/mode-icon' -// DIFFERENT - /** * Returns a react element of the desired icon. If customIcons are defined, then * the icon will be attempted to be used from that lookup of icons. Otherwise, @@ -31,33 +29,15 @@ export function getIcon (iconId, customIcons) { return } -export function getItineraryBounds (itinerary) { - let coords = [] - itinerary.legs.forEach(leg => { - const legCoords = polyline - .toGeoJSON(leg.legGeometry.points) - .coordinates.map(c => [c[1], c[0]]) - coords = [...coords, ...legCoords] - }) - return latLngBounds(coords) +export function getLeafletItineraryBounds (itinerary) { + return latLngBounds(coreUtils.itinerary.getItineraryBounds(itinerary)) } -// DIFFERENT /** * Return a leaflet LatLngBounds object that encloses the given leg's geometry. */ -export function getLegBounds (leg) { - const coords = polyline - .toGeoJSON(leg.legGeometry.points) - .coordinates.map(c => [c[1], c[0]]) - - // in certain cases, there might be zero-length coordinates in the leg - // geometry. In these cases, build us an array of coordinates using the from - // and to data of the leg. - if (coords.length === 0) { - coords.push([leg.from.lat, leg.from.lon], [leg.to.lat, leg.to.lon]) - } - return latLngBounds(coords) +export function getLeafletLegBounds (leg) { + return latLngBounds(coreUtils.itinerary.getLegBounds(leg)) } /** From 966178cf1f3c3ed27c37fc726ae5dad686b1d74a Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 24 Apr 2020 12:58:30 -0400 Subject: [PATCH 25/31] refactor: Use `import coreUtils from '@opentripplanner/core-utils' as much as possible. --- __tests__/util/itinerary.js | 4 ++-- lib/actions/api.js | 10 +++++---- lib/actions/form.js | 20 ++++++++++-------- lib/actions/map.js | 4 ++-- lib/actions/narrative.js | 4 ++-- lib/actions/ui.js | 6 +++--- lib/components/app/responsive-webapp.js | 7 ++++--- lib/components/form/date-time-modal.js | 12 +++++------ lib/components/form/date-time-preview.js | 21 +++++++++++-------- lib/components/form/plan-trip-button.js | 4 ++-- lib/components/form/settings-preview.js | 4 ++-- lib/components/form/user-settings.js | 6 ++++-- lib/components/form/user-trip-settings.js | 6 +++--- .../map/connected-transitive-overlay.js | 4 ++-- lib/components/map/leg-diagram.js | 4 +++- lib/components/map/stylized-map.js | 6 +++--- lib/components/mobile/results-screen.js | 4 ++-- .../narrative/connected-trip-details.js | 6 +++--- .../narrative/default/access-leg.js | 7 +++---- .../narrative/default/default-itinerary.js | 4 +++- .../narrative/default/itinerary-details.js | 4 ++-- lib/components/narrative/default/tnc-leg.js | 8 ++++--- .../narrative/default/transit-leg.js | 6 ++++-- .../narrative/itinerary-carousel.js | 4 ++-- .../narrative/leg-diagram-preview.js | 4 ++-- .../narrative/line-itin/line-itinerary.js | 4 +++- .../narrative/realtime-annotation.js | 4 ++-- .../narrative/tabbed-itineraries.js | 6 ++++-- lib/components/viewers/stop-viewer.js | 9 +++++++- lib/components/viewers/trip-viewer.js | 6 +++--- lib/reducers/create-otp-reducer.js | 20 ++++++++++-------- lib/util/index.js | 2 -- lib/util/query.js | 15 ------------- lib/util/state.js | 13 ++++++++++++ lib/util/ui.js | 4 ++-- 35 files changed, 139 insertions(+), 113 deletions(-) delete mode 100644 lib/util/query.js diff --git a/__tests__/util/itinerary.js b/__tests__/util/itinerary.js index 83b06f3db..fcc0e709c 100644 --- a/__tests__/util/itinerary.js +++ b/__tests__/util/itinerary.js @@ -1,4 +1,4 @@ -import { isTransit } from '@opentripplanner/core-utils/lib/itinerary' +import coreUtils from '@opentripplanner/core-utils' import { routeComparator } from '../../lib/util/itinerary' @@ -27,7 +27,7 @@ function sortRoutes (...routes) { describe('util > itinerary', () => { it('isTransit should work', () => { - expect(isTransit('CAR')).toBeFalsy() + expect(coreUtils.itinerary.isTransit('CAR')).toBeFalsy() }) describe('routeComparator', () => { diff --git a/lib/actions/api.js b/lib/actions/api.js index c13dec433..988e9900e 100644 --- a/lib/actions/api.js +++ b/lib/actions/api.js @@ -3,11 +3,8 @@ import { push, replace } from 'connected-react-router' import haversine from 'haversine' import moment from 'moment' import hash from 'object-hash' -import { hasCar } from '@opentripplanner/core-utils/lib/itinerary' -import { getTripOptionsFromQuery, getUrlParams } from '@opentripplanner/core-utils/lib/query' +import coreUtils from '@opentripplanner/core-utils' import queryParams from '@opentripplanner/core-utils/lib/query-params' -import { randId } from '@opentripplanner/core-utils/lib/storage' -import { OTP_API_DATE_FORMAT, OTP_API_TIME_FORMAT } from '@opentripplanner/core-utils/lib/time' import { createAction } from 'redux-actions' import qs from 'qs' @@ -15,6 +12,11 @@ import { rememberPlace } from './map' import { getStopViewerConfig, queryIsValid } from '../util/state' if (typeof (fetch) === 'undefined') require('isomorphic-fetch') +const { hasCar } = coreUtils.itinerary +const { getTripOptionsFromQuery, getUrlParams } = coreUtils.query +const { randId } = coreUtils.storage +const { OTP_API_DATE_FORMAT, OTP_API_TIME_FORMAT } = coreUtils.time + // Generic API actions export const nonRealtimeRoutingResponse = createAction('NON_REALTIME_ROUTING_RESPONSE') diff --git a/lib/actions/form.js b/lib/actions/form.js index b1a1011d8..1916a2e3b 100644 --- a/lib/actions/form.js +++ b/lib/actions/form.js @@ -1,15 +1,7 @@ import debounce from 'lodash.debounce' import isEqual from 'lodash.isequal' import moment from 'moment' -import { - getDefaultQuery, - getTripOptionsFromQuery, - getUrlParams, - planParamsToQuery -} from '@opentripplanner/core-utils/lib/query' -import { getItem, randId } from '@opentripplanner/core-utils/lib/storage' -import { OTP_API_TIME_FORMAT } from '@opentripplanner/core-utils/lib/time' -import { isMobile } from '@opentripplanner/core-utils/lib/ui' +import coreUtils from '@opentripplanner/core-utils' import { createAction } from 'redux-actions' import { queryIsValid } from '../util/state' @@ -21,6 +13,16 @@ import { import { routingQuery } from './api' +const { + getDefaultQuery, + getTripOptionsFromQuery, + getUrlParams, + planParamsToQuery +} = coreUtils.query +const { getItem, randId } = coreUtils.storage +const { OTP_API_TIME_FORMAT } = coreUtils.time +const { isMobile } = coreUtils.ui + export const settingQueryParam = createAction('SET_QUERY_PARAM') export const clearActiveSearch = createAction('CLEAR_ACTIVE_SEARCH') export const setActiveSearch = createAction('SET_ACTIVE_SEARCH') diff --git a/lib/actions/map.js b/lib/actions/map.js index 6e0e2bdc1..f3ec18447 100644 --- a/lib/actions/map.js +++ b/lib/actions/map.js @@ -1,4 +1,4 @@ -import { constructLocation } from '@opentripplanner/core-utils/lib/map' +import coreUtils from '@opentripplanner/core-utils' import getGeocoder from '@opentripplanner/geocoder' import { createAction } from 'redux-actions' @@ -116,7 +116,7 @@ export const setElevationPoint = createAction('SET_ELEVATION_POINT') export const setMapPopupLocation = createAction('SET_MAP_POPUP_LOCATION') export function setMapPopupLocationAndGeocode (mapEvent) { - const location = constructLocation(mapEvent.latlng) + const location = coreUtils.map.constructLocation(mapEvent.latlng) return function (dispatch, getState) { dispatch(setMapPopupLocation({ location })) getGeocoder(getState().otp.config.geocoder) diff --git a/lib/actions/narrative.js b/lib/actions/narrative.js index c69c78cc7..56cc5279c 100644 --- a/lib/actions/narrative.js +++ b/lib/actions/narrative.js @@ -1,4 +1,4 @@ -import { getUrlParams } from '@opentripplanner/core-utils/lib/query' +import coreUtils from '@opentripplanner/core-utils' import { createAction } from 'redux-actions' import { setUrlSearch } from './api' @@ -8,7 +8,7 @@ export function setActiveItinerary (payload) { // Trigger change in store. dispatch(settingActiveitinerary(payload)) // Update URL params. - const urlParams = getUrlParams() + const urlParams = coreUtils.query.getUrlParams() urlParams.ui_activeItinerary = payload.index dispatch(setUrlSearch(urlParams)) } diff --git a/lib/actions/ui.js b/lib/actions/ui.js index 02f7374a8..9dc8decfe 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -1,5 +1,5 @@ import { push } from 'connected-react-router' -import { getUrlParams } from '@opentripplanner/core-utils/lib/query' +import coreUtils from '@opentripplanner/core-utils' import { createAction } from 'redux-actions' import { matchPath } from 'react-router' @@ -8,7 +8,7 @@ import { setMapCenter, setMapZoom, setRouterId } from './config' import { clearActiveSearch, parseUrlQueryString, setActiveSearch } from './form' import { clearLocation } from './map' import { setActiveItinerary } from './narrative' -import { getUiUrlParams } from '../util/query' +import { getUiUrlParams } from '../util/state' /** * Wrapper function for history#push that preserves the current search or, if @@ -104,7 +104,7 @@ export function handleBackButtonPress (e) { const uiUrlParams = getUiUrlParams(otpState) // Get new search ID from URL after back button pressed. // console.log('back button pressed', e) - const urlParams = getUrlParams() + const urlParams = coreUtils.query.getUrlParams() const previousSearchId = urlParams.ui_activeSearch const previousItinIndex = +urlParams.ui_activeItinerary || 0 const previousSearch = otpState.searches[previousSearchId] diff --git a/lib/components/app/responsive-webapp.js b/lib/components/app/responsive-webapp.js index 233596914..464b10d18 100644 --- a/lib/components/app/responsive-webapp.js +++ b/lib/components/app/responsive-webapp.js @@ -1,8 +1,7 @@ import { ConnectedRouter } from 'connected-react-router' import { createHashHistory } from 'history' import isEqual from 'lodash.isequal' -import { getUrlParams } from '@opentripplanner/core-utils/lib/query' -import { isMobile } from '@opentripplanner/core-utils/lib/ui' +import coreUtils from '@opentripplanner/core-utils' import PropTypes from 'prop-types' import React, { Component } from 'react' import { connect } from 'react-redux' @@ -17,6 +16,8 @@ import { handleBackButtonPress, matchContentToUrl } from '../../actions/ui' import { getTitle } from '../../util/ui' import { getActiveItinerary } from '../../util/state' +const { isMobile } = coreUtils.ui + class ResponsiveWebapp extends Component { static propTypes = { desktopView: PropTypes.element, @@ -30,7 +31,7 @@ class ResponsiveWebapp extends Component { componentDidUpdate (prevProps) { const { currentPosition, location, query, title } = this.props document.title = title - const urlParams = getUrlParams() + const urlParams = coreUtils.query.getUrlParams() const newSearchId = urlParams.ui_activeSearch // Determine if trip is being replanned by checking the active search ID // against the ID found in the URL params. If they are different, a new one diff --git a/lib/components/form/date-time-modal.js b/lib/components/form/date-time-modal.js index 6b54176c4..0d934e8e5 100644 --- a/lib/components/form/date-time-modal.js +++ b/lib/components/form/date-time-modal.js @@ -1,8 +1,7 @@ -// import necessary React/Redux libraries -import React, { Component } from 'react' +import coreUtils from '@opentripplanner/core-utils' import PropTypes from 'prop-types' +import React, { Component } from 'react' import { connect } from 'react-redux' -import { getTimeFormat, getDateFormat } from '@opentripplanner/core-utils/lib/time' import { setQueryParam } from '../../actions/form' @@ -41,14 +40,15 @@ class DateTimeModal extends Component { const mapStateToProps = (state, ownProps) => { const { departArrive, date, time } = state.otp.currentQuery + const config = state.otp.config return { - config: state.otp.config, + config, departArrive, date, time, // These props below are for legacy browsers (see render method above). - timeFormatLegacy: getTimeFormat(state.otp.config), - dateFormatLegacy: getDateFormat(state.otp.config) + timeFormatLegacy: coreUtils.time.getTimeFormat(config), + dateFormatLegacy: coreUtils.time.getDateFormat(config) } } diff --git a/lib/components/form/date-time-preview.js b/lib/components/form/date-time-preview.js index 44754d044..846c8b974 100644 --- a/lib/components/form/date-time-preview.js +++ b/lib/components/form/date-time-preview.js @@ -1,15 +1,17 @@ import moment from 'moment' -import { - OTP_API_DATE_FORMAT, - OTP_API_TIME_FORMAT, - getTimeFormat, - getDateFormat -} from '@opentripplanner/core-utils/lib/time' +import coreUtils from '@opentripplanner/core-utils' import PropTypes from 'prop-types' import React, { Component } from 'react' import { Button } from 'react-bootstrap' import { connect } from 'react-redux' +const { + OTP_API_DATE_FORMAT, + OTP_API_TIME_FORMAT, + getTimeFormat, + getDateFormat +} = coreUtils.time + class DateTimePreview extends Component { static propTypes = { caret: PropTypes.string, @@ -81,16 +83,17 @@ class DateTimePreview extends Component { const mapStateToProps = (state, ownProps) => { const { departArrive, date, time, routingType, startTime, endTime } = state.otp.currentQuery + const config = state.otp.config return { - config: state.otp.config, + config, routingType, departArrive, date, time, startTime, endTime, - timeFormat: getTimeFormat(state.otp.config), - dateFormat: getDateFormat(state.otp.config) + timeFormat: getTimeFormat(config), + dateFormat: getDateFormat(config) } } diff --git a/lib/components/form/plan-trip-button.js b/lib/components/form/plan-trip-button.js index 73d2de18a..e2a091d0c 100644 --- a/lib/components/form/plan-trip-button.js +++ b/lib/components/form/plan-trip-button.js @@ -1,4 +1,4 @@ -import { isMobile } from '@opentripplanner/core-utils/lib/ui' +import coreUtils from '@opentripplanner/core-utils' import PropTypes from 'prop-types' import React, { Component } from 'react' import { Button } from 'react-bootstrap' @@ -23,7 +23,7 @@ class PlanTripButton extends Component { _onClick = () => { this.props.routingQuery() if (typeof this.props.onClick === 'function') this.props.onClick() - if (!isMobile()) this.props.setMainPanelContent(null) + if (!coreUtils.ui.isMobile()) this.props.setMainPanelContent(null) } render () { diff --git a/lib/components/form/settings-preview.js b/lib/components/form/settings-preview.js index 4a3c78575..337c5d2b6 100644 --- a/lib/components/form/settings-preview.js +++ b/lib/components/form/settings-preview.js @@ -1,4 +1,4 @@ -import { isNotDefaultQuery } from '@opentripplanner/core-utils/lib/query' +import coreUtils from '@opentripplanner/core-utils' import PropTypes from 'prop-types' import React, { Component } from 'react' import { Button } from 'react-bootstrap' @@ -27,7 +27,7 @@ class SettingsPreview extends Component { render () { const { config, query, caret, editButtonText } = this.props // Show dot indicator if the current query differs from the default query. - let showDot = isNotDefaultQuery(query, config) + let showDot = coreUtils.query.isNotDefaultQuery(query, config) const button = (
@@ -62,7 +61,7 @@ export default class AccessLeg extends Component { className={`step ${stepIsActive ? 'active' : ''}`} onClick={(e) => this._onStepClick(e, step, stepIndex)}> {distanceString(step.distance)} - {getStepInstructions(step)} + {coreUtils.itinerary.getStepInstructions(step)} ) })} diff --git a/lib/components/narrative/default/default-itinerary.js b/lib/components/narrative/default/default-itinerary.js index 3eae4c049..efc740c3a 100644 --- a/lib/components/narrative/default/default-itinerary.js +++ b/lib/components/narrative/default/default-itinerary.js @@ -1,4 +1,4 @@ -import { formatDuration, formatTime } from '@opentripplanner/core-utils/lib/time' +import coreUtils from '@opentripplanner/core-utils' import React from 'react' import NarrativeItinerary from '../narrative-itinerary' @@ -7,6 +7,8 @@ import ItineraryDetails from './itinerary-details' import TripDetails from '../connected-trip-details' import TripTools from '../trip-tools' +const { formatDuration, formatTime } = coreUtils.time + export default class DefaultItinerary extends NarrativeItinerary { render () { const { diff --git a/lib/components/narrative/default/itinerary-details.js b/lib/components/narrative/default/itinerary-details.js index b12d214b3..91537b8b6 100644 --- a/lib/components/narrative/default/itinerary-details.js +++ b/lib/components/narrative/default/itinerary-details.js @@ -1,4 +1,4 @@ -import { isTransit } from '@opentripplanner/core-utils/lib/itinerary' +import coreUtils from '@opentripplanner/core-utils' import PropTypes from 'prop-types' import React, { Component } from 'react' @@ -16,7 +16,7 @@ export default class ItineraryDetails extends Component {
{itinerary.legs.map((leg, index) => { const legIsActive = activeLeg === index - return isTransit(leg.mode) + return coreUtils.itinerary.isTransit(leg.mode) ? { activeLeg: activeSearch && activeSearch.activeLeg, activeStep: activeSearch && activeSearch.activeStep, companies: state.otp.currentQuery.companies, - timeFormat: getTimeFormat(state.otp.config) + timeFormat: coreUtils.time.getTimeFormat(state.otp.config) } } diff --git a/lib/components/narrative/leg-diagram-preview.js b/lib/components/narrative/leg-diagram-preview.js index 6ce1ed09d..fd624d807 100644 --- a/lib/components/narrative/leg-diagram-preview.js +++ b/lib/components/narrative/leg-diagram-preview.js @@ -1,4 +1,4 @@ -import { getElevationProfile } from '@opentripplanner/core-utils/lib/itinerary' +import coreUtils from '@opentripplanner/core-utils' import PropTypes from 'prop-types' import React, {Component} from 'react' import { connect } from 'react-redux' @@ -45,7 +45,7 @@ class LegDiagramPreview extends Component { render () { const { leg, showElevationProfile } = this.props if (!showElevationProfile) return null - const profile = getElevationProfile(leg.steps) + const profile = coreUtils.itinerary.getElevationProfile(leg.steps) // Don't show for very short legs if (leg.distance < 500 || leg.mode === 'CAR') return null diff --git a/lib/components/narrative/line-itin/line-itinerary.js b/lib/components/narrative/line-itin/line-itinerary.js index 8876d43ad..baf6034bd 100644 --- a/lib/components/narrative/line-itin/line-itinerary.js +++ b/lib/components/narrative/line-itin/line-itinerary.js @@ -1,4 +1,4 @@ -import { getLegModeLabel, getTimeZoneOffset, isTransit } from '@opentripplanner/core-utils/lib/itinerary' +import coreUtils from '@opentripplanner/core-utils' import React from 'react' import styled from 'styled-components' @@ -7,6 +7,8 @@ import ItinerarySummary from './itin-summary' import NarrativeItinerary from '../narrative-itinerary' import SimpleRealtimeAnnotation from '../simple-realtime-annotation' +const { getLegModeLabel, getTimeZoneOffset, isTransit } = coreUtils.itinerary + export const LineItineraryContainer = styled.div` margin-bottom: 20px; ` diff --git a/lib/components/narrative/realtime-annotation.js b/lib/components/narrative/realtime-annotation.js index d0ecd93d5..31ed7cd0e 100644 --- a/lib/components/narrative/realtime-annotation.js +++ b/lib/components/narrative/realtime-annotation.js @@ -1,4 +1,4 @@ -import { formatDuration } from '@opentripplanner/core-utils/lib/time' +import coreUtils from '@opentripplanner/core-utils' import PropTypes from 'prop-types' import React, { Component } from 'react' import { Button, OverlayTrigger, Popover } from 'react-bootstrap' @@ -32,7 +32,7 @@ export default class RealtimeAnnotation extends Component { ? Your trip results have been adjusted based on real-time information. Under normal conditions, this trip would take{' '} - {formatDuration(realtimeEffects.normalDuration)} + {coreUtils.time.formatDuration(realtimeEffects.normalDuration)} using the following routes:{' '} {filteredRoutes .map((route, idx) => ( diff --git a/lib/components/narrative/tabbed-itineraries.js b/lib/components/narrative/tabbed-itineraries.js index 9b440aa3a..94f38eeb8 100644 --- a/lib/components/narrative/tabbed-itineraries.js +++ b/lib/components/narrative/tabbed-itineraries.js @@ -1,5 +1,4 @@ -import { calculateFares, calculatePhysicalActivity, getTimeZoneOffset } from '@opentripplanner/core-utils/lib/itinerary' -import { formatDuration, formatTime, getTimeFormat } from '@opentripplanner/core-utils/lib/time' +import coreUtils from '@opentripplanner/core-utils' import PropTypes from 'prop-types' import React, { Component } from 'react' import { Button } from 'react-bootstrap' @@ -9,6 +8,9 @@ import { setActiveItinerary, setActiveLeg, setActiveStep, setUseRealtimeResponse import DefaultItinerary from './default/default-itinerary' import { getActiveSearch, getRealtimeEffects } from '../../util/state' +const { calculateFares, calculatePhysicalActivity, getTimeZoneOffset } = coreUtils.itinerary +const { formatDuration, formatTime, getTimeFormat } = coreUtils.time + class TabbedItineraries extends Component { static propTypes = { itineraries: PropTypes.array, diff --git a/lib/components/viewers/stop-viewer.js b/lib/components/viewers/stop-viewer.js index 1c04a1d80..07c9128ee 100644 --- a/lib/components/viewers/stop-viewer.js +++ b/lib/components/viewers/stop-viewer.js @@ -1,6 +1,6 @@ import moment from 'moment' import 'moment-timezone' -import { formatDuration, formatSecondsAfterMidnight, getTimeFormat, getUserTimezone } from '@opentripplanner/core-utils/lib/time' +import coreUtils from '@opentripplanner/core-utils' import FromToLocationPicker from '@opentripplanner/from-to-location-picker' import PropTypes from 'prop-types' import React, { Component } from 'react' @@ -15,6 +15,13 @@ import { forgetStop, rememberStop, setLocation } from '../../actions/map' import { routeComparator } from '../../util/itinerary' import { getShowUserSettings, getStopViewerConfig } from '../../util/state' +const { + formatDuration, + formatSecondsAfterMidnight, + getTimeFormat, + getUserTimezone +} = coreUtils.time + class StopViewer extends Component { state = {} diff --git a/lib/components/viewers/trip-viewer.js b/lib/components/viewers/trip-viewer.js index d517938aa..9ce414d29 100644 --- a/lib/components/viewers/trip-viewer.js +++ b/lib/components/viewers/trip-viewer.js @@ -1,4 +1,4 @@ -import { formatSecondsAfterMidnight, getTimeFormat } from '@opentripplanner/core-utils/lib/time' +import coreUtils from '@opentripplanner/core-utils' import PropTypes from 'prop-types' import React, { Component } from 'react' import { Button, Label } from 'react-bootstrap' @@ -100,7 +100,7 @@ class TripViewer extends Component {
{/* the departure time */}
- {formatSecondsAfterMidnight(tripData.stopTimes[i].scheduledDeparture, timeFormat)} + {coreUtils.time.formatSecondsAfterMidnight(tripData.stopTimes[i].scheduledDeparture, timeFormat)}
{/* the vertical strip map */} @@ -136,7 +136,7 @@ const mapStateToProps = (state, ownProps) => { const viewedTrip = state.otp.ui.viewedTrip return { languageConfig: state.otp.config.language, - timeFormat: getTimeFormat(state.otp.config), + timeFormat: coreUtils.time.getTimeFormat(state.otp.config), tripData: state.otp.transitIndex.trips[viewedTrip.tripId], viewedTrip } diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index 4008e2f01..d18fbdb20 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -2,18 +2,20 @@ import clone from 'clone' import update from 'immutability-helper' import isEqual from 'lodash.isequal' import objectPath from 'object-path' -import { isTransit, getTransitModes } from '@opentripplanner/core-utils/lib/itinerary' -import { matchLatLon } from '@opentripplanner/core-utils/lib/map' -import { filterProfileOptions } from '@opentripplanner/core-utils/lib/profile' -import { +import coreUtils from '@opentripplanner/core-utils' + +import { MainPanelContent, MobileScreens } from '../actions/ui' + +const { isTransit, getTransitModes } = coreUtils.itinerary +const { matchLatLon } = coreUtils.map +const { filterProfileOptions } = coreUtils.profile +const { ensureSingleAccessMode, getDefaultQuery, getTripOptionsFromQuery -} from '@opentripplanner/core-utils/lib/query' -import { getItem, removeItem, storeItem } from '@opentripplanner/core-utils/lib/storage' -import { getUserTimezone } from '@opentripplanner/core-utils/lib/time' - -import { MainPanelContent, MobileScreens } from '../actions/ui' +} = coreUtils.query +const { getItem, removeItem, storeItem } = coreUtils.storage +const { getUserTimezone } = coreUtils.time const MAX_RECENT_STORAGE = 5 diff --git a/lib/util/index.js b/lib/util/index.js index 3cb72d8d0..4803c5931 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -1,13 +1,11 @@ import * as distance from './distance' import * as itinerary from './itinerary' -import * as query from './query' import * as state from './state' import * as ui from './ui' const OtpUtils = { distance, itinerary, - query, state, ui } diff --git a/lib/util/query.js b/lib/util/query.js deleted file mode 100644 index c864e91c0..000000000 --- a/lib/util/query.js +++ /dev/null @@ -1,15 +0,0 @@ -import { getActiveSearch } from './state' - -// => Used in actions/ui/handleBackButtonPress() -/** - * Assemble any UI-state properties to be tracked via URL into a single object - * TODO: Expand to include additional UI properties - */ -export function getUiUrlParams (otpState) { - const activeSearch = getActiveSearch(otpState) - const uiParams = { - ui_activeItinerary: activeSearch ? activeSearch.activeItinerary : 0, - ui_activeSearch: otpState.activeSearchId - } - return uiParams -} diff --git a/lib/util/state.js b/lib/util/state.js index 8019564e8..704c2e75f 100644 --- a/lib/util/state.js +++ b/lib/util/state.js @@ -135,3 +135,16 @@ export function getShowUserSettings (otpState) { export function getStopViewerConfig (otpState) { return otpState.config.stopViewer } + +/** + * Assemble any UI-state properties to be tracked via URL into a single object + * TODO: Expand to include additional UI properties + */ +export function getUiUrlParams (otpState) { + const activeSearch = getActiveSearch(otpState) + const uiParams = { + ui_activeItinerary: activeSearch ? activeSearch.activeItinerary : 0, + ui_activeSearch: otpState.activeSearchId + } + return uiParams +} diff --git a/lib/util/ui.js b/lib/util/ui.js index 47e452528..da40aa33e 100644 --- a/lib/util/ui.js +++ b/lib/util/ui.js @@ -1,4 +1,4 @@ -import { summarizeQuery } from '@opentripplanner/core-utils/lib/query' +import coreUtils from '@opentripplanner/core-utils' import { MainPanelContent } from '../actions/ui' import { getActiveSearch } from './state' @@ -23,7 +23,7 @@ export function getTitle (state) { default: const activeSearch = getActiveSearch(state.otp) if (activeSearch) { - title += ` | ${summarizeQuery(activeSearch.query, user.locations)}` + title += ` | ${coreUtils.query.summarizeQuery(activeSearch.query, user.locations)}` } break } From f54896eb74351a1de03d0a04e3e87f74e1f1de97 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 24 Apr 2020 13:12:10 -0400 Subject: [PATCH 26/31] refactor(line-itinerary/itinerary.css): Should not have brought back this file. --- .../narrative/line-itin/itinerary.css | 375 ------------------ 1 file changed, 375 deletions(-) delete mode 100644 lib/components/narrative/line-itin/itinerary.css diff --git a/lib/components/narrative/line-itin/itinerary.css b/lib/components/narrative/line-itin/itinerary.css deleted file mode 100644 index 89df3acf0..000000000 --- a/lib/components/narrative/line-itin/itinerary.css +++ /dev/null @@ -1,375 +0,0 @@ -.otp .options.profile .itin-body .place-row { - margin-left: 55px; -} - -.otp .line-itin { - margin-bottom: 20px; -} - -/* Itinerary summary */ - -.otp .line-itin .itin-summary { - padding-right: 5px; - height: 60px; - display: table; - width: 100%; - margin-bottom: 15px; -} - -.otp .desktop-narrative-container .options.itinerary .line-itin .itin-summary { - display: none; -} - -.otp .line-itin .itin-summary .details { - display: table-cell; - vertical-align: top; -} - -.otp .line-itin .itin-summary .header { - font-weight: bold; - font-size: 18px; - margin-top: -3px; -} - -.otp .line-itin .itin-summary .detail { - font-size: 13px; - color: #999999; -} - -.otp .line-itin .itin-summary .routes { - display: table-cell; - text-align: right; -} - -.otp .line-itin .itin-summary .routes .route-preview { - display: inline-block; - margin-left: 8px; - vertical-align: top; -} - -.otp .line-itin .itin-summary .routes .route-preview .mode-icon { - height: 30px; - width: 30px; -} - -.otp .line-itin .itin-summary .routes .route-preview .short-name { - color: white; - font-weight: 500; - text-align: center; - margin-top: 6px; - font-size: 15px; - padding-top: 2px; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - width: 30px; - height: 30px; - border-radius: 15px; - border: 2px solid white; - box-shadow: 0 0 0.5em #000; -} - -/* Itinerary main body */ - -.otp .line-itin .itin-body { - padding: 20px 0px; -} - -.otp .line-itin .place-row { - display: table; - width: 100%; -} - - -/* Departure/arrival time (1st column in table) */ - -.otp .line-itin .time { - display: table-cell; - width: 60px; - font-size: 14px; - color: #999999; - text-align: right; - padding-right: 4px; - padding-top: 1px; - vertical-align: top; -} - -/* The place icon and line itself (2nd column in table) */ -.otp .line-itin .line-container { - position: relative; - display: table-cell; - width: 20px; - max-width: 20px; -} - -.otp .line-itin .place-icon-group { - position: absolute; - font-size: 18px; - left: -8px; - top: -7px; - z-index: 20; -} - -.otp .line-itin .leg-line { - position: absolute; - top: 11px; - bottom: -11px; - z-index: 10; -} - -// Internet explorer specific media query to apply the below styling to fix -// rendering issues with table cell display with undefined heights. -/*@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { - .otp .line-itin .line-container { - overflow: hidden; // hack for IE to render table cell correctly. - } - - .otp .line-itin .leg-line { - height: 1000px; // hack for IE to render table cell correctly. - } -}*/ - -.otp .line-itin .leg-line-walk { - left: 6px; - right: 6px; - background: radial-gradient(ellipse at center, #87cefa 40%, transparent 10%); - background-size: 12px 12px; - background-repeat: repeat-y; - background-position: center -5px; -} - -.otp .line-itin .leg-line-bicycle { - left: 7.5px; - right: 7.5px; - background: repeating-linear-gradient( - 0deg, - red, - red 8px, - white 8px, - white 12.5px - ); -} - -.otp .line-itin .leg-line-car { - left: 7.5px; - right: 7.5px; - background: repeating-linear-gradient( - 0deg, - grey, - grey 8px, - white 8px, - white 12.5px - ); -} - -.otp .line-itin .leg-line-micromobility { - left: 7.5px; - right: 7.5px; - background: repeating-linear-gradient( - 0deg, - #f5a729, - #f5a729 8px, - white 8px, - white 12.5px - ); -} - -.otp .line-itin .leg-line-transit { - left: 5px; - right: 5px; - background-color: gray; -} - -/* Place/Leg details (3rd column in table) */ - -.otp .line-itin .place-details { - font-size: 13px; - display: table-cell; - padding-top: 1px; -} - -.otp .line-itin .place-name { - font-size: 18px; - line-height: 20px; - padding-left: 4px; - font-weight: 500; - color: black; -} - -.otp .line-itin .place-subheader { - font-size: 12px; - padding-left: 4px; - padding-top: 1px; - font-weight: 300; - color: gray; -} - -.otp .line-itin .interline-dot { - position: relative; - float: left; - margin-left: -13.5px; - z-index: 25; - color: #fff; -} - -.otp .line-itin .interline-name { - font-size: 14px; - font-weight: 400; - line-height: 16px; -} - -/* Leg body general */ - -.otp .line-itin .leg-body { - padding: 12px 0px 18px 4px; - font-size: 13px; - color: #999999; -} - -.otp .line-itin .summary { - cursor: pointer; -} - -.otp .line-itin .leg-body .icon { - height: 24px; - width: 24px; - float: left; - margin-right: 6px; -} - -.otp .line-itin .leg-body .leg-description { - display: table; -} - -.otp .line-itin .leg-body .leg-description > div { - display: table-cell; - vertical-align: middle; -} - -/* Leg steps (for turn-by-turn) */ - -.otp .line-itin .leg-body .steps-header { - font-size: 13px; - margin-top: 10px; - color: #999999; - font-style: normal; - display: inline-block; -} - -.otp .line-itin .leg-body .step-row { - font-size: 13px; - margin-top: 8px; - color: #999999; - font-style: normal; -} - -/* Transit leg details */ - -.otp .line-itin .leg-body .route-name { - color: #999999; - margin-top: 5px; -} - -.otp .line-itin .leg-body .route-short-name { - display: inline-block; - background-color: #0f6aac; - padding-top: 1px; - color: white; - font-weight: 500; - font-size: 14px; - margin-right: 6px; - text-align: center; - width: 24px; - height: 24px; - border-radius: 12px; - border: 1px solid white; - box-shadow: 0 0 0.25em #000; - margin-right: 8px; -} - -.otp .line-itin .leg-body .route-long-name { - font-size: 13px; - line-height: 16px; - font-weight: 500; -} - -.otp .line-itin .leg-body .transit-leg-details { - margin-top: 5px; -} - -.otp .line-itin .leg-body .agency-info { - margin-top: 5px; - -} - -.otp .line-itin .leg-body .transit-leg-details .header { - cursor: pointer; - color: #999999; - font-size: 13px; -} - -/* Intermediate stops */ - -.otp .line-itin .leg-body .transit-leg-details .intermediate-stops .stop-row { - z-index: 30; - position: relative; -} - -.otp .line-itin .leg-body .transit-leg-details .intermediate-stops .stop-marker { - float: left; - margin-left: -17px; - color: white; -} - -.otp .line-itin .leg-body .transit-leg-details .intermediate-stops .stop-name { - color: #999999; - font-size: 14px; - margin-top: 3px; -} - -/* Transit alerts */ - -.otp .line-itin .leg-body .transit-alerts-toggle { - display: inline-block; - margin-top: 8px; - color: #D14727; - font-weight: 400; - cursor: pointer; -} - -.otp .line-itin .leg-body .transit-alerts { - margin-top: 3px; -} - -.otp .line-itin .leg-body .transit-alerts .transit-alert { - margin-top: 5px; - background-color: #eee; - padding: 8px; - color: black; - border-radius: 4px; -} - -.otp .line-itin .leg-body .transit-alerts .transit-alert .alert-icon { - float: left; - font-size: 18px; -} - -.otp .line-itin .leg-body .transit-alerts .transit-alert .alert-header { - font-size: 14px; - margin-left: 30px; - font-weight: 600; -} - -.otp .line-itin .leg-body .transit-alerts .transit-alert .alert-body { - font-size: 12px; - margin-left: 30px; - /* white space pre-wrap is required to render line breaks correctly. */ - white-space: pre-wrap; -} - -.otp .line-itin .leg-body .transit-alerts .transit-alert .effective-date { - margin-top: 5px; - margin-left: 30px; - font-size: 12px; - font-style: italic; -} From 0ccc86f6c65bc36e9e7850e9c8f2d06bb7366295 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 24 Apr 2020 14:37:43 -0400 Subject: [PATCH 27/31] refactor(TriMetLegIcon): Import TriMetLegIcon from the root of icons package. --- lib/components/app/print-layout.js | 2 +- lib/components/narrative/line-itin/connected-itinerary-body.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/components/app/print-layout.js b/lib/components/app/print-layout.js index 2fda1815f..724db3ee3 100644 --- a/lib/components/app/print-layout.js +++ b/lib/components/app/print-layout.js @@ -1,4 +1,4 @@ -import TriMetLegIcon from '@opentripplanner/icons/lib/trimet-leg-icon' +import { TriMetLegIcon } from '@opentripplanner/icons' import PrintableItinerary from '@opentripplanner/printable-itinerary' import PropTypes from 'prop-types' import React, { Component } from 'react' diff --git a/lib/components/narrative/line-itin/connected-itinerary-body.js b/lib/components/narrative/line-itin/connected-itinerary-body.js index 0c8774da3..e4c92a4b6 100644 --- a/lib/components/narrative/line-itin/connected-itinerary-body.js +++ b/lib/components/narrative/line-itin/connected-itinerary-body.js @@ -1,5 +1,5 @@ import isEqual from 'lodash.isequal' -import TriMetLegIcon from '@opentripplanner/icons/lib/trimet-leg-icon' +import { TriMetLegIcon } from '@opentripplanner/icons' import TransitLegSummary from '@opentripplanner/itinerary-body/lib/defaults/transit-leg-summary' import ItineraryBody from '@opentripplanner/itinerary-body/lib/otp-react-redux/itinerary-body' import LineColumnContent from '@opentripplanner/itinerary-body/lib/otp-react-redux/line-column-content' From ca56c603d130df1aa9804cc06f78445b3a91179a Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 24 Apr 2020 18:02:30 -0400 Subject: [PATCH 28/31] refactor: Address PR comments. --- lib/actions/form.js | 16 ++++------ lib/components/app/responsive-webapp.js | 3 +- lib/components/form/default-search-form.js | 8 +---- .../narrative/narrative-routing-results.js | 6 ++-- lib/util/index.js | 2 ++ lib/util/state.js | 31 ++++++++++++++++++ lib/util/ui.js | 32 ------------------- 7 files changed, 45 insertions(+), 53 deletions(-) delete mode 100644 lib/util/ui.js diff --git a/lib/actions/form.js b/lib/actions/form.js index 1916a2e3b..abaa4ff00 100644 --- a/lib/actions/form.js +++ b/lib/actions/form.js @@ -19,9 +19,6 @@ const { getUrlParams, planParamsToQuery } = coreUtils.query -const { getItem, randId } = coreUtils.storage -const { OTP_API_TIME_FORMAT } = coreUtils.time -const { isMobile } = coreUtils.ui export const settingQueryParam = createAction('SET_QUERY_PARAM') export const clearActiveSearch = createAction('CLEAR_ACTIVE_SEARCH') @@ -37,7 +34,7 @@ export function resetForm () { dispatch(settingQueryParam(otpState.user.defaults)) } else { // Get user overrides and apply to default query - const userOverrides = getItem('defaultQuery', {}) + const userOverrides = coreUtils.storage.getItem('defaultQuery', {}) const defaultQuery = Object.assign( getDefaultQuery(otpState.config), userOverrides @@ -71,7 +68,7 @@ export function parseUrlQueryString (params = getUrlParams()) { Object.keys(params).forEach(key => { if (!key.startsWith('ui_')) planParams[key] = params[key] }) - const searchId = params.ui_activeSearch || randId() + const searchId = params.ui_activeSearch || coreUtils.storage.randId() // Convert strings to numbers/objects and dispatch dispatch( setQueryParam( @@ -91,10 +88,11 @@ let lastDebouncePlanTimeMs export function formChanged (oldQuery, newQuery) { return function (dispatch, getState) { const otpState = getState().otp + const isMobile = coreUtils.ui.isMobile() // If departArrive is set to 'NOW', update the query time to current if (otpState.currentQuery && otpState.currentQuery.departArrive === 'NOW') { - dispatch(settingQueryParam({ time: moment().format(OTP_API_TIME_FORMAT) })) + dispatch(settingQueryParam({ time: moment().format(coreUtils.time.OTP_API_TIME_FORMAT) })) } // Determine if either from/to location has changed @@ -113,7 +111,7 @@ export function formChanged (oldQuery, newQuery) { // either location changes only if not currently on welcome screen (otherwise // when the current position is auto-set the screen will change unexpectedly). if ( - isMobile() && + isMobile && (fromChanged || toChanged) && otpState.ui.mobileScreen !== MobileScreens.WELCOME_SCREEN ) { @@ -125,8 +123,8 @@ export function formChanged (oldQuery, newQuery) { const { autoPlan, debouncePlanTimeMs } = otpState.config const updatePlan = autoPlan || - (!isMobile() && oneLocationChanged) || // TODO: make autoplan configurable at the parameter level? - (isMobile() && fromChanged && toChanged) + (!isMobile && oneLocationChanged) || // TODO: make autoplan configurable at the parameter level? + (isMobile && fromChanged && toChanged) if (updatePlan && queryIsValid(otpState)) { // trip plan should be made // check if debouncing function needs to be (re)created if (!debouncedPlanTrip || lastDebouncePlanTimeMs !== debouncePlanTimeMs) { diff --git a/lib/components/app/responsive-webapp.js b/lib/components/app/responsive-webapp.js index 464b10d18..eea5f49a7 100644 --- a/lib/components/app/responsive-webapp.js +++ b/lib/components/app/responsive-webapp.js @@ -13,8 +13,7 @@ import { formChanged, parseUrlQueryString } from '../../actions/form' import { getCurrentPosition, receivedPositionResponse } from '../../actions/location' import { setLocationToCurrent } from '../../actions/map' import { handleBackButtonPress, matchContentToUrl } from '../../actions/ui' -import { getTitle } from '../../util/ui' -import { getActiveItinerary } from '../../util/state' +import { getActiveItinerary, getTitle } from '../../util/state' const { isMobile } = coreUtils.ui diff --git a/lib/components/form/default-search-form.js b/lib/components/form/default-search-form.js index 7dfe3a266..98be08f8a 100644 --- a/lib/components/form/default-search-form.js +++ b/lib/components/form/default-search-form.js @@ -6,12 +6,6 @@ import TabbedFormPanel from './tabbed-form-panel' import SwitchButton from './switch-button' import defaultIcons from '../icons' -// Use the icons props to override icons for certain modes in the mode selector panel. -// If no icon is provided for a specific mode, the default OTP-ui icon will be used. -// const customIcons = { -// TRANSIT: -// } - export default class DefaultSearchForm extends Component { static propTypes = { icons: PropTypes.object, @@ -19,7 +13,7 @@ export default class DefaultSearchForm extends Component { } static defaultProps = { - icons: defaultIcons, // customIcons, + icons: defaultIcons, showFrom: true, showTo: true } diff --git a/lib/components/narrative/narrative-routing-results.js b/lib/components/narrative/narrative-routing-results.js index 2047d4cfb..204cc245b 100644 --- a/lib/components/narrative/narrative-routing-results.js +++ b/lib/components/narrative/narrative-routing-results.js @@ -25,14 +25,14 @@ class NarrativeRoutingResults extends Component { } render () { - const { customIcons, error, itineraryClass, itineraryFooter, pending, routingType, itineraries, mainPanelContent } = this.props + const { customIcons, error, itineraryClass, itineraryFooter, pending, itineraries, mainPanelContent } = this.props if (pending) return if (error) return if (mainPanelContent) return null return ( - routingType === 'ITINERARY' && - + // TODO: If multiple routing types exist, do the check here. + ) } } diff --git a/lib/util/index.js b/lib/util/index.js index 4803c5931..2e060cdd3 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -1,9 +1,11 @@ +import coreUtils from '@opentripplanner/core-utils' import * as distance from './distance' import * as itinerary from './itinerary' import * as state from './state' import * as ui from './ui' const OtpUtils = { + ...coreUtils, distance, itinerary, state, diff --git a/lib/util/state.js b/lib/util/state.js index 704c2e75f..cc22fc24c 100644 --- a/lib/util/state.js +++ b/lib/util/state.js @@ -1,5 +1,8 @@ +import coreUtils from '@opentripplanner/core-utils' import isEqual from 'lodash.isequal' +import { MainPanelContent } from '../actions/ui' + /** * Get the active search object * @param {Object} otpState the OTP state object @@ -148,3 +151,31 @@ export function getUiUrlParams (otpState) { } return uiParams } + +// Set default title to the original document title (on load) set in index.html +const DEFAULT_TITLE = document.title + +export function getTitle (state) { + // Override title can optionally be provided in config.yml + const { config, ui, user } = state.otp + let title = config.title || DEFAULT_TITLE + const { mainPanelContent, viewedRoute, viewedStop } = ui + switch (mainPanelContent) { + case MainPanelContent.ROUTE_VIEWER: + title += ' | Route' + if (viewedRoute && viewedRoute.routeId) title += ` ${viewedRoute.routeId}` + break + case MainPanelContent.STOP_VIEWER: + title += ' | Stop' + if (viewedStop && viewedStop.stopId) title += ` ${viewedStop.stopId}` + break + default: + const activeSearch = getActiveSearch(state.otp) + if (activeSearch) { + title += ` | ${coreUtils.query.summarizeQuery(activeSearch.query, user.locations)}` + } + break + } + // if (printView) title += ' | Print' + return title +} diff --git a/lib/util/ui.js b/lib/util/ui.js deleted file mode 100644 index da40aa33e..000000000 --- a/lib/util/ui.js +++ /dev/null @@ -1,32 +0,0 @@ -import coreUtils from '@opentripplanner/core-utils' - -import { MainPanelContent } from '../actions/ui' -import { getActiveSearch } from './state' - -// Set default title to the original document title (on load) set in index.html -const DEFAULT_TITLE = document.title - -export function getTitle (state) { - // Override title can optionally be provided in config.yml - const { config, ui, user } = state.otp - let title = config.title || DEFAULT_TITLE - const { mainPanelContent, viewedRoute, viewedStop } = ui - switch (mainPanelContent) { - case MainPanelContent.ROUTE_VIEWER: - title += ' | Route' - if (viewedRoute && viewedRoute.routeId) title += ` ${viewedRoute.routeId}` - break - case MainPanelContent.STOP_VIEWER: - title += ' | Stop' - if (viewedStop && viewedStop.stopId) title += ` ${viewedStop.stopId}` - break - default: - const activeSearch = getActiveSearch(state.otp) - if (activeSearch) { - title += ` | ${coreUtils.query.summarizeQuery(activeSearch.query, user.locations)}` - } - break - } - // if (printView) title += ' | Print' - return title -} From 2690d3da16bcfa3d4685e006c67cc7b65adc8a5b Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 24 Apr 2020 18:14:08 -0400 Subject: [PATCH 29/31] refactor(util/index): Remove unused vars. --- lib/util/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/util/index.js b/lib/util/index.js index 2e060cdd3..fdeac261c 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -2,14 +2,12 @@ import coreUtils from '@opentripplanner/core-utils' import * as distance from './distance' import * as itinerary from './itinerary' import * as state from './state' -import * as ui from './ui' const OtpUtils = { ...coreUtils, distance, itinerary, - state, - ui + state } export default OtpUtils From f9910e5297e2aec10be775b8fedb6e623bae5d02 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 28 Apr 2020 10:01:10 -0400 Subject: [PATCH 30/31] fix(otp-ui version): Upgrade to OTP-ui 0.0.20. --- lib/util/index.js | 2 - package.json | 38 ++++---- yarn.lock | 221 +++++++++++++++++++++++----------------------- 3 files changed, 129 insertions(+), 132 deletions(-) diff --git a/lib/util/index.js b/lib/util/index.js index fdeac261c..70f3f81e0 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -1,10 +1,8 @@ -import coreUtils from '@opentripplanner/core-utils' import * as distance from './distance' import * as itinerary from './itinerary' import * as state from './state' const OtpUtils = { - ...coreUtils, distance, itinerary, state diff --git a/package.json b/package.json index b40050d7f..5a949fbd1 100644 --- a/package.json +++ b/package.json @@ -29,25 +29,25 @@ "dependencies": { "@conveyal/lonlat": "^1.1.0", "@mapbox/polyline": "^0.2.0", - "@opentripplanner/base-map": "^0.0.19", - "@opentripplanner/core-utils": "^0.0.19", - "@opentripplanner/endpoints-overlay": "^0.0.19", - "@opentripplanner/from-to-location-picker": "^0.0.19", - "@opentripplanner/geocoder": "^0.0.19", - "@opentripplanner/icons": "^0.0.19", - "@opentripplanner/itinerary-body": "^0.0.19", - "@opentripplanner/location-field": "^0.0.19", - "@opentripplanner/location-icon": "^0.0.19", - "@opentripplanner/park-and-ride-overlay": "^0.0.19", - "@opentripplanner/printable-itinerary": "^0.0.19", - "@opentripplanner/route-viewer-overlay": "^0.0.19", - "@opentripplanner/stop-viewer-overlay": "^0.0.19", - "@opentripplanner/stops-overlay": "^0.0.19", - "@opentripplanner/transitive-overlay": "^0.0.19", - "@opentripplanner/trip-details": "^0.0.19", - "@opentripplanner/trip-form": "^0.0.19", - "@opentripplanner/trip-viewer-overlay": "^0.0.19", - "@opentripplanner/vehicle-rental-overlay": "^0.0.19", + "@opentripplanner/base-map": "^0.0.20", + "@opentripplanner/core-utils": "^0.0.20", + "@opentripplanner/endpoints-overlay": "^0.0.20", + "@opentripplanner/from-to-location-picker": "^0.0.20", + "@opentripplanner/geocoder": "^0.0.20", + "@opentripplanner/icons": "^0.0.20", + "@opentripplanner/itinerary-body": "^0.0.20", + "@opentripplanner/location-field": "^0.0.20", + "@opentripplanner/location-icon": "^0.0.20", + "@opentripplanner/park-and-ride-overlay": "^0.0.20", + "@opentripplanner/printable-itinerary": "^0.0.20", + "@opentripplanner/route-viewer-overlay": "^0.0.20", + "@opentripplanner/stop-viewer-overlay": "^0.0.20", + "@opentripplanner/stops-overlay": "^0.0.20", + "@opentripplanner/transitive-overlay": "^0.0.20", + "@opentripplanner/trip-details": "^0.0.20", + "@opentripplanner/trip-form": "^0.0.20", + "@opentripplanner/trip-viewer-overlay": "^0.0.20", + "@opentripplanner/vehicle-rental-overlay": "^0.0.20", "@turf/along": "^6.0.1", "bootstrap": "^3.3.7", "bowser": "^1.9.3", diff --git a/yarn.lock b/yarn.lock index b477c125d..e2ad479a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1331,18 +1331,18 @@ universal-user-agent "^3.0.0" url-template "^2.0.8" -"@opentripplanner/base-map@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/base-map/-/base-map-0.0.19.tgz#71b0d77c7e8265f742f5383c943663436eb3369b" - integrity sha512-X/OtKsCoVOfJ4GrkDfvW5zufDE2dTzdGsagojrrTk0mzJTh1Kv+x+Oe3iXJfllg5U61yt06PDunFdsZE5kHr2w== +"@opentripplanner/base-map@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/base-map/-/base-map-0.0.20.tgz#35f1b81179080e5b2573a67b9ce9408dde390bd2" + integrity sha512-FR9C93sou4d+3b7wytDsMRZQ/Zeu7ZJGAoQ8WiHlPnCIS/grRTWeihQysK1zga9JAerpBocvIvLc2pF3DyJAIw== dependencies: - "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" prop-types "^15.7.2" -"@opentripplanner/core-utils@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-0.0.19.tgz#152991efb07d578abf40649ce0dde1c62a8ec4e3" - integrity sha512-9j0j8pGba+af5kz0K8+6bNLy/34BL7CEIHkLQyyLv3H30GpzJWQ+r9z6zIcwndVcoBqEcrn6gLBr1S2KrBscZA== +"@opentripplanner/core-utils@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-0.0.20.tgz#057914abe4f395cc9bb5468a97b620ad6e46efe4" + integrity sha512-nwUk/FCJ2X68SM8zN53SFGD2x3ooMirJ2JRcA22U2v8hsEU6NywaSiDDvG1/kjC+WWHbHSNLP5VACSnFc7UAWA== dependencies: "@mapbox/polyline" "^1.1.0" "@turf/along" "^6.0.1" @@ -1353,173 +1353,172 @@ prop-types "^15.7.2" qs "^6.9.1" -"@opentripplanner/endpoints-overlay@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/endpoints-overlay/-/endpoints-overlay-0.0.19.tgz#667a70b8b0526cc3fd01e4f69e4839cf04a85ca5" - integrity sha512-0Cd4dd5yYXP42udDAKGNHj00WuCXwoWThj03IWagKOpbr7T5g7ag7B1RkbMJQICYsFvqg9stQJ3AnOmNrwBBPw== +"@opentripplanner/endpoints-overlay@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/endpoints-overlay/-/endpoints-overlay-0.0.20.tgz#386835b7ab0fb18d68eb5035ab4984f55717363d" + integrity sha512-1miuJBESWRBXR5TH2EUrUyn/zKeTngcoitWfWUbX322rRv3D6soNd7BdOSqQ1waxKHobqWKC1Tq/Az2RbfZhIg== dependencies: - "@opentripplanner/core-utils" "^0.0.19" - "@opentripplanner/location-icon" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" + "@opentripplanner/location-icon" "^0.0.20" prop-types "^15.7.2" styled-icons "^9.1.0" -"@opentripplanner/from-to-location-picker@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/from-to-location-picker/-/from-to-location-picker-0.0.19.tgz#5dd78fff068c84c256fa504dd163ca70e51b2cd9" - integrity sha512-ZNPJVRO2OIOKjdRLOX4fnOvWTX07ccTXEuUfXPpITOf8QBHnJZPTFBgBSMmfqxMfcuLf6NRD4+YzFcllIO4opQ== +"@opentripplanner/from-to-location-picker@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/from-to-location-picker/-/from-to-location-picker-0.0.20.tgz#e945a286ae39077b3fbd3ddf1f31cbb38ea4a067" + integrity sha512-b+bLWcDu9y5jYUz078isXGnyhTlO0gSXBTtYoEOsm0Y7RNDIfvrytKDxSKAyPnaRnaftkbsUcXTmUfUTXCw2PQ== dependencies: - "@opentripplanner/core-utils" "^0.0.19" - "@opentripplanner/location-icon" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" + "@opentripplanner/location-icon" "^0.0.20" prop-types "^15.7.2" -"@opentripplanner/geocoder@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/geocoder/-/geocoder-0.0.19.tgz#4ff8b1baf3c322491f472db31c3fbd7ed576b2a1" - integrity sha512-BOkOY85fj6E2CAFemlMH3qu/e4t9xG3llKrvasm8Vh23PI3FwQRtDQoN8qz3vK4s/pjZsd0D/TpS8+bQGI9HQQ== +"@opentripplanner/geocoder@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/geocoder/-/geocoder-0.0.20.tgz#479eed134f4bb68cde9442c049b2630e5eaa499c" + integrity sha512-EkTLmdrBzw0ABFiOrJCEM6ajK5D2T+KFD3FcTowTi7e1esGWpXzEDb7VvbobMaMhwDCztenv1s0APd9J9o0B0g== dependencies: "@conveyal/geocoder-arcgis-geojson" "^0.0.2" "@conveyal/lonlat" "^1.4.0" isomorphic-mapzen-search "^1.4.1" lodash.memoize "^4.1.2" -"@opentripplanner/humanize-distance@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/humanize-distance/-/humanize-distance-0.0.19.tgz#30d445e9f586afcf0488a8fe27157d62b29fd563" - integrity sha512-gEDH3oB8XZoraPWfsmVfEZYqPN/aqh+Z3Pjc35asJmdOCVKuWWnSem/W4qyK6hBnpMV3RUl9TBKl3B5ubnqBLA== +"@opentripplanner/humanize-distance@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/humanize-distance/-/humanize-distance-0.0.20.tgz#06eab437c92b8eea5d9596957d7fbcc8c2bd4500" + integrity sha512-m+3gpoSDGY13f8WRIx8fSkVi6Qhv23fG3zbY6oymFQ+j5bMPow4f6XJO2nIY+pI0EbipPEKWbpeRwmXZWlWspg== -"@opentripplanner/icons@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/icons/-/icons-0.0.19.tgz#7681ad2b202e18f361cc4ae32b9c14e162500e50" - integrity sha512-yGNBamoCzvdzcc86RFfZGW5SsktOsXV2G+USPr9goWoP1Hw1IukSn2gEWP8s8qvBCulAMr7HZdLHSXAT9TXk9w== +"@opentripplanner/icons@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/icons/-/icons-0.0.20.tgz#951c75ed093fb9d74e4b4fc7d2af45ced446b856" + integrity sha512-vV81bsB7b3XN8lED3A55Qrjx84lUJyiVY99f13W1oqRlaP1xFIk4MSspg6hHoyanLiZBGp+vdcwWYnKJk4LLtw== dependencies: - "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" prop-types "^15.7.2" -"@opentripplanner/itinerary-body@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/itinerary-body/-/itinerary-body-0.0.19.tgz#44c7858f848a625c7327de38850adc2f0a086667" - integrity sha512-f7H8eXWUAArlqBUkvg9IqN+hbQgtVSqcLtwu+EMjNmO9/grU6qR1z9guyXudNi5BS+jsngIla8mNNC60+8l7tA== +"@opentripplanner/itinerary-body@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/itinerary-body/-/itinerary-body-0.0.20.tgz#0f24a4ac0ccd4de21451c5255ec673450bc8198c" + integrity sha512-u3MQUg3SAYs5hMHf2sIPZDF8VLHmeWpXaDW33Pi7WJyn5fJ5AYXsdisEwc87+inOoG0zB6d5usTUkB8Ldlx/rA== dependencies: - "@opentripplanner/core-utils" "^0.0.19" - "@opentripplanner/humanize-distance" "^0.0.19" - "@opentripplanner/icons" "^0.0.19" - "@opentripplanner/location-icon" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" + "@opentripplanner/humanize-distance" "^0.0.20" + "@opentripplanner/icons" "^0.0.20" + "@opentripplanner/location-icon" "^0.0.20" currency-formatter "^1.5.5" moment "^2.24.0" prop-types "^15.7.2" react-resize-detector "^4.2.1" velocity-react "^1.4.3" -"@opentripplanner/location-field@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/location-field/-/location-field-0.0.19.tgz#70db6c081139320cd29b58b2580a6e3178f5c844" - integrity sha512-yCq+VnwaJLPy9CC623I6sq08dEUxJHb/x0wWPXN6A7ZmiltAZdDIPmYnLJAkvRzEfLi1jv8pt1PO4nSYxctI0A== +"@opentripplanner/location-field@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/location-field/-/location-field-0.0.20.tgz#f2b019bde39dfdb570789afc078e1bb1808941d0" + integrity sha512-+SC9IHpJ49KzvAw03IIH8HlvRcGx9kNGdIdJ6PtGSNCsOd/91/LkfKTdLeI/Z24axWQpdSPM6BhGCyilIoVqfg== dependencies: - "@opentripplanner/core-utils" "^0.0.19" - "@opentripplanner/geocoder" "^0.0.19" - "@opentripplanner/humanize-distance" "^0.0.19" - "@opentripplanner/location-icon" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" + "@opentripplanner/geocoder" "^0.0.20" + "@opentripplanner/humanize-distance" "^0.0.20" + "@opentripplanner/location-icon" "^0.0.20" prop-types "^15.7.2" styled-icons "^9.1.0" throttle-debounce "^2.1.0" -"@opentripplanner/location-icon@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/location-icon/-/location-icon-0.0.19.tgz#19d121cd7b0d0d1588321912fe0674ac6641046e" - integrity sha512-vtqTMORk0xLBVbBLFUzXqPf4acQSDIe0kxbA+VbpYc2NQD8vZDcnIVFL9e73zXAaKNj/uATAolubZjX6Jvyh3Q== +"@opentripplanner/location-icon@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/location-icon/-/location-icon-0.0.20.tgz#8f88522cec37e0c37b051421ba315e2065d464d7" + integrity sha512-TTtoV7s4moNQ++h1TRQhFFTRueRfoNgVnV8fnLhE77T0rf6UXHDhbZNlt1nm/wyHcYF1MbTynQmZmID85V9UMg== dependencies: styled-icons "^9.1.0" -"@opentripplanner/park-and-ride-overlay@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/park-and-ride-overlay/-/park-and-ride-overlay-0.0.19.tgz#6f448eccb8ffb86bf9844ec778a6d9111f7575d7" - integrity sha512-9+ia0f2rl2Rkln46tmIGuyb19lYwUPTLGPSOSRRXr1/Qe9OiZD7dNrf4Zif/HnW6l+1Z4fexYA5nN6TN/clOaA== +"@opentripplanner/park-and-ride-overlay@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/park-and-ride-overlay/-/park-and-ride-overlay-0.0.20.tgz#0342a798f9a147143f8821062d477751d6145999" + integrity sha512-SiJMZnhD8mG8771pbryfYa+a8T+UnBoFMocu6SB4S911CnRwoFDnaMNosIH6hVrzZquisBWnrsTdJHRMnCkI3w== dependencies: - "@opentripplanner/core-utils" "^0.0.19" - "@opentripplanner/from-to-location-picker" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" + "@opentripplanner/from-to-location-picker" "^0.0.20" prop-types "^15.7.2" -"@opentripplanner/printable-itinerary@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/printable-itinerary/-/printable-itinerary-0.0.19.tgz#a224cae07e1459c455f7684eaebbac2eca66ea48" - integrity sha512-9Ow6YXKuxDJ5dnwlDBXXMEmyLriEtZtetk7IkRiAOXUNgo00un9uGbb0Zu6gYBbTVuLpjLMsZNpzUsnO27DbYw== +"@opentripplanner/printable-itinerary@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/printable-itinerary/-/printable-itinerary-0.0.20.tgz#d48b9f78ba41827d820bc496c0300a7cd2839c64" + integrity sha512-KuZA0Z6S3Nn35uPoZGChlEOjRSmgBmIcG+bdSBscHo5TbdoX3D1WLfGtIYeph7adLnFljSMHIyATvS1d6Hn7Mw== dependencies: - "@opentripplanner/core-utils" "^0.0.19" - "@opentripplanner/humanize-distance" "^0.0.19" - moment "^2.24.0" + "@opentripplanner/core-utils" "^0.0.20" + "@opentripplanner/humanize-distance" "^0.0.20" prop-types "^15.7.2" -"@opentripplanner/route-viewer-overlay@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/route-viewer-overlay/-/route-viewer-overlay-0.0.19.tgz#d6f8080ec4106ae8d90cfebcd02edd39a7ae2d27" - integrity sha512-6DUumUOC33X4FauUVJNucaHoZBvCDVLizZLbU2hjDuS+ZrdCfckEy6wJz8j5x4lfi0pr8jUZEWK+pl3P+sFjaw== +"@opentripplanner/route-viewer-overlay@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/route-viewer-overlay/-/route-viewer-overlay-0.0.20.tgz#61e9fdbfc248d84b77733a31fbb7c5d5a0c27f1e" + integrity sha512-Vrejj8DFUfLWt/AN8ogepEH+wRlEyfLQosBA5ynEg/xaExSSDaiRA7Ite2CHvpuJfbDKZfTtWLrP6k4Gf0bL/A== dependencies: "@mapbox/polyline" "^1.1.0" - "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" prop-types "^15.7.2" -"@opentripplanner/stop-viewer-overlay@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/stop-viewer-overlay/-/stop-viewer-overlay-0.0.19.tgz#1484864fcad81b9fa3ea9726845b3a7383e78db5" - integrity sha512-fdfBiUOrcKiDFDVo5upj7tl2jN6CPxIF7OoYtR/EoSf4A8pCSkCY4pxRL3VE0z7O103fJFP67BCu8axKptaEsA== +"@opentripplanner/stop-viewer-overlay@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/stop-viewer-overlay/-/stop-viewer-overlay-0.0.20.tgz#8448757238081feace0c0d4852fd39247cd795e2" + integrity sha512-UmBs0xLW4/rTOCrasg53vqIZZM/EVNrCDJJmbhGN/y8ZWZpzpWowlpzo2S/qZiSrLhc00+KdluBwRVcnA+Lx4A== dependencies: - "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" prop-types "^15.7.2" -"@opentripplanner/stops-overlay@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/stops-overlay/-/stops-overlay-0.0.19.tgz#b3cc17bbbb7d64fd44da17c01955b4069f0868a6" - integrity sha512-tlr/2S2E7zYjEHla3hIMQc0tpdgJMolY4jUqBPEFOohP1NCf4VQWyO8cY0D2CyjPI9eQkW96eU2be8WuUtwyXQ== +"@opentripplanner/stops-overlay@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/stops-overlay/-/stops-overlay-0.0.20.tgz#1fcbce27a0d9438616cb861169d4a6ec3d5ea0e9" + integrity sha512-3m7p7eSWYu8mil7qy/DqqK7eOK26ccQEuvbIhur/btyYaKpKYLodIXoO3mH3UarJfTpQzRMdeQ+W3YTobMYgZg== dependencies: - "@opentripplanner/core-utils" "^0.0.19" - "@opentripplanner/from-to-location-picker" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" + "@opentripplanner/from-to-location-picker" "^0.0.20" -"@opentripplanner/transitive-overlay@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/transitive-overlay/-/transitive-overlay-0.0.19.tgz#096c30b08e94161cc8522f3a01ff147f473a40d7" - integrity sha512-+UYbDcGlODHxAZQuoX6WeiDVTZsCcrzCfphQmkoQERdd3xukJpPwzjSSjeIEbP+8gTGPtlM+i7qu00mu3C0ynw== +"@opentripplanner/transitive-overlay@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/transitive-overlay/-/transitive-overlay-0.0.20.tgz#93f30afcd248e1705feac08e0a385683dcdc84fd" + integrity sha512-lOuWjwb6LlhjMRKjSVeNmBjrsdNewwNe3pnLJjpAnWhSTeunLATiv8aYQk8TYGJz2AclSgYYzySgBWHXnF6Pzw== dependencies: - "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" lodash.isequal "^4.5.0" transitive-js "^0.13.0" -"@opentripplanner/trip-details@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-details/-/trip-details-0.0.19.tgz#3379a13188b7fb12387be5abd0b0d071231b366a" - integrity sha512-ItRywhXMikvOv+13/qWwYueX1eC57IZnzukDy4Gukd/MtJheQOzwDU2W5pWwK1MmMEikxjdsSCw4vt2m6biTyw== +"@opentripplanner/trip-details@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-details/-/trip-details-0.0.20.tgz#ef80eee732e24d6c17d771f6f7d9f9d83deb27cd" + integrity sha512-nZDXHyBgghQZAdu2mLg51Lc5M6OraK6Vp5aYesXVa1ARBwug2hr0W2R/N6F4nQmU5SI0RQAN/b5irGplsKnnXw== dependencies: - "@opentripplanner/core-utils" "^0.0.19" - "@opentripplanner/humanize-distance" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" + "@opentripplanner/humanize-distance" "^0.0.20" moment "^2.24.0" prop-types "^15.7.2" styled-icons "^9.1.0" velocity-react "^1.4.3" -"@opentripplanner/trip-form@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-form/-/trip-form-0.0.19.tgz#f34995eee51401012d9fa81a1138de9627d2bc78" - integrity sha512-x2zRAUDYPZlZF1hsVH4wVEdjRspBNU+P1d5a5m0J4OYfS7prnWlVKQl5+b5ktcLiY3mbdGtDHtYv+2knQt0gnQ== +"@opentripplanner/trip-form@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-form/-/trip-form-0.0.20.tgz#823e23ef8e332f5aa3fe85306ee65a0bee17f914" + integrity sha512-GZOZj4mCyPWx64OO1klZpsJ0JEggxmG5Qx264fwue9l8pun4D9DuCJFbclw51Tu/vLJ0VUuFZJBlECRCKeussg== dependencies: - "@opentripplanner/core-utils" "^0.0.19" - "@opentripplanner/icons" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" + "@opentripplanner/icons" "^0.0.20" moment "^2.17.1" -"@opentripplanner/trip-viewer-overlay@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-viewer-overlay/-/trip-viewer-overlay-0.0.19.tgz#ab608699720f3ab090f64aadae01a12434188f18" - integrity sha512-Nyta8qagZib/tQE/h57HB+O1nUzHi2ougDCEn6I4upqM6OUn6bYSC+V048VGXUHwa8rnnFT9jf8ryKUd6tVEfQ== +"@opentripplanner/trip-viewer-overlay@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-viewer-overlay/-/trip-viewer-overlay-0.0.20.tgz#f65ed3527edea2e2304b5e02394b62ac69de7c0e" + integrity sha512-neLU+iO1dTYSXefW00IltdO0vdxtkEJpMG9GiFOUCgVAvLhyHuJca1aofgKZmcsArLhRo7eV2T8YzEHf3EiOCQ== dependencies: "@mapbox/polyline" "^1.1.0" - "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" prop-types "^15.7.2" -"@opentripplanner/vehicle-rental-overlay@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@opentripplanner/vehicle-rental-overlay/-/vehicle-rental-overlay-0.0.19.tgz#d0efc0d653c45da8e2fd8f835eb059f7173aa3fe" - integrity sha512-gZnqoZlZznGeCykfynVkC9pMq4AAotV0DJaNkX4z9sHVGUFgk+mcaiAkrIhPLWLyOiOTnLQnnuPw8T26yOqzng== +"@opentripplanner/vehicle-rental-overlay@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@opentripplanner/vehicle-rental-overlay/-/vehicle-rental-overlay-0.0.20.tgz#ef30b97f8c1080497056246a59f371f96efabc11" + integrity sha512-7ujSgc2mG/hZpZEL3DW+v3fIWSBUY3BOxoyqPlE0KhoE805KQi99LMxUYre+ovcEyIhcr0OKGSig0Br0X6banw== dependencies: - "@opentripplanner/core-utils" "^0.0.19" - "@opentripplanner/from-to-location-picker" "^0.0.19" + "@opentripplanner/core-utils" "^0.0.20" + "@opentripplanner/from-to-location-picker" "^0.0.20" lodash.memoize "^4.1.2" prop-types "^15.7.2" styled-icons "^9.1.0" From 5404f9822a109a04ed69c1d716c8353a37404583 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 28 Apr 2020 10:49:33 -0400 Subject: [PATCH 31/31] refactor(routeComparator): Use routeComparator from OTP-UI. --- __tests__/util/__mocks__/itinerary.json | 91 ------- .../util/__snapshots__/itinerary.js.snap | 247 ------------------ __tests__/util/itinerary.js | 90 ------- lib/components/viewers/route-viewer.js | 4 +- lib/components/viewers/stop-viewer.js | 3 +- lib/util/itinerary.js | 239 ----------------- 6 files changed, 3 insertions(+), 671 deletions(-) delete mode 100644 __tests__/util/__mocks__/itinerary.json delete mode 100644 __tests__/util/__snapshots__/itinerary.js.snap delete mode 100644 __tests__/util/itinerary.js diff --git a/__tests__/util/__mocks__/itinerary.json b/__tests__/util/__mocks__/itinerary.json deleted file mode 100644 index c00a605a4..000000000 --- a/__tests__/util/__mocks__/itinerary.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "route1": { - "longName": "Across town", - "mode": "BUS", - "shortName": "10", - "sortOrder": 10 - }, - "route2": { - "longName": "Around town", - "mode": "BUS", - "shortName": "20", - "sortOrder": 2 - }, - "route3": { - "longName": "Around another town", - "shortName": "3", - "sortOrder": -999, - "type": 3 - }, - "route4": { - "longName": "Loop route", - "mode": "BUS", - "shortName": "2", - "sortOrder": -999 - }, - "route5": { - "longName": "A-line", - "mode": "BUS", - "shortName": "A", - "sortOrder": -999 - }, - "route6": { - "longName": "B-line", - "mode": "BUS", - "shortName": "B", - "sortOrder": -999 - }, - "route7": { - "longName": "A meandering route", - "mode": "BUS", - "sortOrder": -999 - }, - "route8": { - "longName": "Zig-zagging route", - "mode": "BUS", - "shortName": "30", - "sortOrder": 2 - }, - "route9": { - "longName": "Express route", - "mode": "BUS", - "shortName": "30", - "sortOrder": 2 - }, - "route10": { - "longName": "Variation of express route", - "mode": "BUS", - "shortName": "30", - "sortOrder": 2 - }, - "route11": { - "longName": "Local route", - "mode": "BUS", - "shortName": "6", - "sortOrder": 2 - }, - "route12": { - "longName": "Intercity Train", - "mode": "RAIL", - "shortName": "IC", - "sortOrder": 2 - }, - "route13": { - "longName": "Yellow line Subway", - "mode": "SUBWAY", - "shortName": "Yellow", - "sortOrder": 2 - }, - "route14": { - "longName": "Xpress route C", - "mode": "BUS", - "shortName": "30C", - "sortOrder": 2 - }, - "route15": { - "longName": "Express route X", - "mode": "BUS", - "shortName": "30X", - "sortOrder": 2 - } -} diff --git a/__tests__/util/__snapshots__/itinerary.js.snap b/__tests__/util/__snapshots__/itinerary.js.snap deleted file mode 100644 index 6887d0868..000000000 --- a/__tests__/util/__snapshots__/itinerary.js.snap +++ /dev/null @@ -1,247 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`util > itinerary routeComparator should prioritize routes with integer shortNames over alphabetic shortNames 1`] = ` -Array [ - Object { - "longName": "A-line", - "mode": "BUS", - "shortName": "A", - "sortOrder": -999, - }, - Object { - "longName": "Loop route", - "mode": "BUS", - "shortName": "2", - "sortOrder": -999, - }, -] -`; - -exports[`util > itinerary routeComparator should prioritize routes with shortNames over those with just longNames 1`] = ` -Array [ - Object { - "longName": "B-line", - "mode": "BUS", - "shortName": "B", - "sortOrder": -999, - }, - Object { - "longName": "A meandering route", - "mode": "BUS", - "sortOrder": -999, - }, -] -`; - -exports[`util > itinerary routeComparator should prioritize routes with valid sortOrder 1`] = ` -Array [ - Object { - "longName": "Around town", - "mode": "BUS", - "shortName": "20", - "sortOrder": 2, - }, - Object { - "longName": "Around another town", - "shortName": "3", - "sortOrder": -999, - "type": 3, - }, -] -`; - -exports[`util > itinerary routeComparator should sort based off of route type 1`] = ` -Array [ - Object { - "longName": "Yellow line Subway", - "mode": "SUBWAY", - "shortName": "Yellow", - "sortOrder": 2, - }, - Object { - "longName": "Intercity Train", - "mode": "RAIL", - "shortName": "IC", - "sortOrder": 2, - }, -] -`; - -exports[`util > itinerary routeComparator should sort routes based off of integer shortName 1`] = ` -Array [ - Object { - "longName": "Loop route", - "mode": "BUS", - "shortName": "2", - "sortOrder": -999, - }, - Object { - "longName": "Around another town", - "shortName": "3", - "sortOrder": -999, - "type": 3, - }, -] -`; - -exports[`util > itinerary routeComparator should sort routes based off of longNames 1`] = ` -Array [ - Object { - "longName": "Express route", - "mode": "BUS", - "shortName": "30", - "sortOrder": 2, - }, - Object { - "longName": "Variation of express route", - "mode": "BUS", - "shortName": "30", - "sortOrder": 2, - }, -] -`; - -exports[`util > itinerary routeComparator should sort routes based off of shortNames 1`] = ` -Array [ - Object { - "longName": "A-line", - "mode": "BUS", - "shortName": "A", - "sortOrder": -999, - }, - Object { - "longName": "B-line", - "mode": "BUS", - "shortName": "B", - "sortOrder": -999, - }, -] -`; - -exports[`util > itinerary routeComparator should sort routes based off of sortOrder 1`] = ` -Array [ - Object { - "longName": "Around town", - "mode": "BUS", - "shortName": "20", - "sortOrder": 2, - }, - Object { - "longName": "Across town", - "mode": "BUS", - "shortName": "10", - "sortOrder": 10, - }, -] -`; - -exports[`util > itinerary routeComparator should sort routes on all of the criteria at once 1`] = ` -Array [ - Object { - "longName": "Yellow line Subway", - "mode": "SUBWAY", - "shortName": "Yellow", - "sortOrder": 2, - }, - Object { - "longName": "Intercity Train", - "mode": "RAIL", - "shortName": "IC", - "sortOrder": 2, - }, - Object { - "longName": "Local route", - "mode": "BUS", - "shortName": "6", - "sortOrder": 2, - }, - Object { - "longName": "Around town", - "mode": "BUS", - "shortName": "20", - "sortOrder": 2, - }, - Object { - "longName": "Express route", - "mode": "BUS", - "shortName": "30", - "sortOrder": 2, - }, - Object { - "longName": "Variation of express route", - "mode": "BUS", - "shortName": "30", - "sortOrder": 2, - }, - Object { - "longName": "Zig-zagging route", - "mode": "BUS", - "shortName": "30", - "sortOrder": 2, - }, - Object { - "longName": "Xpress route C", - "mode": "BUS", - "shortName": "30C", - "sortOrder": 2, - }, - Object { - "longName": "Express route X", - "mode": "BUS", - "shortName": "30X", - "sortOrder": 2, - }, - Object { - "longName": "Across town", - "mode": "BUS", - "shortName": "10", - "sortOrder": 10, - }, - Object { - "longName": "A-line", - "mode": "BUS", - "shortName": "A", - "sortOrder": -999, - }, - Object { - "longName": "B-line", - "mode": "BUS", - "shortName": "B", - "sortOrder": -999, - }, - Object { - "longName": "Loop route", - "mode": "BUS", - "shortName": "2", - "sortOrder": -999, - }, - Object { - "longName": "Around another town", - "shortName": "3", - "sortOrder": -999, - "type": 3, - }, - Object { - "longName": "A meandering route", - "mode": "BUS", - "sortOrder": -999, - }, -] -`; - -exports[`util > itinerary routeComparator should sort routes with alphanumeric shortNames 1`] = ` -Array [ - Object { - "longName": "Xpress route C", - "mode": "BUS", - "shortName": "30C", - "sortOrder": 2, - }, - Object { - "longName": "Express route X", - "mode": "BUS", - "shortName": "30X", - "sortOrder": 2, - }, -] -`; diff --git a/__tests__/util/itinerary.js b/__tests__/util/itinerary.js deleted file mode 100644 index fcc0e709c..000000000 --- a/__tests__/util/itinerary.js +++ /dev/null @@ -1,90 +0,0 @@ -import coreUtils from '@opentripplanner/core-utils' - -import { routeComparator } from '../../lib/util/itinerary' - -const { - route1, - route2, - route3, - route4, - route5, - route6, - route7, - route8, - route9, - route10, - route11, - route12, - route13, - route14, - route15 -} = require('./__mocks__/itinerary.json') - -function sortRoutes (...routes) { - routes.sort(routeComparator) - return routes -} - -describe('util > itinerary', () => { - it('isTransit should work', () => { - expect(coreUtils.itinerary.isTransit('CAR')).toBeFalsy() - }) - - describe('routeComparator', () => { - it('should sort routes based off of sortOrder', () => { - expect(sortRoutes(route1, route2)).toMatchSnapshot() - }) - - it('should prioritize routes with valid sortOrder', () => { - expect(sortRoutes(route2, route3)).toMatchSnapshot() - }) - - it('should sort routes based off of integer shortName', () => { - expect(sortRoutes(route3, route4)).toMatchSnapshot() - }) - - it('should prioritize routes with integer shortNames over alphabetic shortNames', () => { - expect(sortRoutes(route4, route5)).toMatchSnapshot() - }) - - it('should sort routes based off of shortNames', () => { - expect(sortRoutes(route5, route6)).toMatchSnapshot() - }) - - it('should sort routes with alphanumeric shortNames', () => { - expect(sortRoutes(route14, route15)).toMatchSnapshot() - }) - - it('should prioritize routes with shortNames over those with just longNames', () => { - expect(sortRoutes(route6, route7)).toMatchSnapshot() - }) - - it('should sort routes based off of longNames', () => { - expect(sortRoutes(route9, route10)).toMatchSnapshot() - }) - - it('should sort routes on all of the criteria at once', () => { - expect(sortRoutes( - route1, - route2, - route3, - route4, - route5, - route6, - route7, - route8, - route9, - route10, - route11, - route12, - route13, - route14, - route15 - )).toMatchSnapshot() - }) - - it('should sort based off of route type', () => { - expect(sortRoutes(route12, route13)).toMatchSnapshot() - }) - }) -}) diff --git a/lib/components/viewers/route-viewer.js b/lib/components/viewers/route-viewer.js index c298d5a50..3cb3e6df5 100644 --- a/lib/components/viewers/route-viewer.js +++ b/lib/components/viewers/route-viewer.js @@ -1,3 +1,4 @@ +import coreUtils from '@opentripplanner/core-utils' import React, { Component, PureComponent } from 'react' import PropTypes from 'prop-types' import { Label, Button } from 'react-bootstrap' @@ -8,7 +9,6 @@ import Icon from '../narrative/icon' import { setMainPanelContent, setViewedRoute } from '../../actions/ui' import { findRoutes, findRoute } from '../../actions/api' -import { routeComparator } from '../../util/itinerary' function operatorIndexForRoute (transitOperators, route) { if (!route.agency) return 0 @@ -58,7 +58,7 @@ class RouteViewer extends Component { viewedRoute } = this.props const sortedRoutes = routes - ? Object.values(routes).sort(routeComparator) + ? Object.values(routes).sort(coreUtils.itinerary.routeComparator) : [] const agencySortedRoutes = transitOperators.length > 0 ? sortedRoutes.sort((a, b) => { diff --git a/lib/components/viewers/stop-viewer.js b/lib/components/viewers/stop-viewer.js index 07c9128ee..ac668f353 100644 --- a/lib/components/viewers/stop-viewer.js +++ b/lib/components/viewers/stop-viewer.js @@ -12,7 +12,6 @@ import Icon from '../narrative/icon' import { setMainPanelContent, toggleAutoRefresh } from '../../actions/ui' import { findStop, findStopTimesForStop } from '../../actions/api' import { forgetStop, rememberStop, setLocation } from '../../actions/map' -import { routeComparator } from '../../util/itinerary' import { getShowUserSettings, getStopViewerConfig } from '../../util/state' const { @@ -231,7 +230,7 @@ class StopViewer extends Component { {stopData.stopTimes && stopData.routes && (
{Object.values(stopTimesByPattern) - .sort((a, b) => routeComparator(a.route, b.route)) + .sort((a, b) => coreUtils.itinerary.routeComparator(a.route, b.route)) .map(patternTimes => { // Only add pattern row if route is found. // FIXME: there is currently a bug with the alernative transit index diff --git a/lib/util/itinerary.js b/lib/util/itinerary.js index e2abbd442..6a65f5f13 100644 --- a/lib/util/itinerary.js +++ b/lib/util/itinerary.js @@ -40,245 +40,6 @@ export function getLeafletLegBounds (leg) { return latLngBounds(coreUtils.itinerary.getLegBounds(leg)) } -/** - * Gets the desired sort values according to an optional getter function. If the - * getter function is not defined, the original sort values are returned. - */ -function getSortValues (getterFn, a, b) { - let aVal - let bVal - if (typeof getterFn === 'function') { - aVal = getterFn(a) - bVal = getterFn(b) - } else { - aVal = a - bVal = b - } - return { aVal, bVal } -} - -// Lookup for the sort values associated with various OTP modes. -// Note: JSDoc format not used to avoid bug in documentationjs. -// https://github.com/documentationjs/documentation/issues/372 -const modeComparatorValue = { - SUBWAY: 1, - TRAM: 2, - RAIL: 3, - GONDOLA: 4, - FERRY: 5, - CABLE_CAR: 6, - FUNICULAR: 7, - BUS: 8 -} - -// Lookup that maps route types to the OTP mode sort values. -// Note: JSDoc format not used to avoid bug in documentationjs. -// https://github.com/documentationjs/documentation/issues/372 -const routeTypeComparatorValue = { - 0: modeComparatorValue.TRAM, // - Tram, Streetcar, Light rail. - 1: modeComparatorValue.SUBWAY, // - Subway, Metro. - 2: modeComparatorValue.RAIL, // - Rail. Used for intercity or long-distance travel. - 3: modeComparatorValue.BUS, // - Bus. - 4: modeComparatorValue.FERRY, // - Ferry. - 5: modeComparatorValue.CABLE_CAR, // - Cable tram. - 6: modeComparatorValue.GONDOLA, // - Gondola, etc. - 7: modeComparatorValue.FUNICULAR, // - Funicular. - // TODO: 11 and 12 are not a part of OTP as of 2019-02-14, but for now just - // associate them with bus/rail. - 11: modeComparatorValue.BUS, // - Trolleybus. - 12: modeComparatorValue.RAIL // - Monorail. -} - -// Gets a comparator value for a given route's type (OTP mode). -// Note: JSDoc format not used to avoid bug in documentationjs. -// ttps://github.com/documentationjs/documentation/issues/372 -function getRouteTypeComparatorValue (route) { - // For some strange reason, the short route response in OTP returns the - // string-based modes, but the long route response returns the - // integer route type. This attempts to account for both of those cases. - if (!route) throw new Error('Route is undefined.', route) - if (typeof modeComparatorValue[route.mode] !== 'undefined') { - return modeComparatorValue[route.mode] - } else if (typeof routeTypeComparatorValue[route.type] !== 'undefined') { - return routeTypeComparatorValue[route.type] - } else { - // Default the comparator value to a large number (placing the route at the - // end of the list). - console.warn('no mode/route type found for route', route) - return 9999 - } -} - -/** - * Calculates the sort comparator value given two routes based off of route type - * (OTP mode). - */ -function routeTypeComparator (a, b) { - return getRouteTypeComparatorValue(a) - getRouteTypeComparatorValue(b) -} - -/** - * Determines whether a value is a string that starts with an alphabetic - * ascii character. - */ -function startsWithAlphabeticCharacter (val) { - if (typeof val === 'string' && val.length > 0) { - const firstCharCode = val.charCodeAt(0) - return (firstCharCode >= 65 && firstCharCode <= 90) || - (firstCharCode >= 97 && firstCharCode <= 122) - } - return false -} - -/** - * Sorts routes based off of whether the shortName begins with an alphabetic - * character. Routes with shortn that do start with an alphabetic character will - * be prioritized over those that don't. - */ -function alphabeticShortNameComparator (a, b) { - const aStartsWithAlphabeticCharacter = startsWithAlphabeticCharacter( - a.shortName - ) - const bStartsWithAlphabeticCharacter = startsWithAlphabeticCharacter( - b.shortName - ) - - if (aStartsWithAlphabeticCharacter && bStartsWithAlphabeticCharacter) { - // both start with an alphabetic character, return equivalence - return 0 - } - // a does start with an alphabetic character, but b does not. Prioritize a - if (aStartsWithAlphabeticCharacter) return -1 - // b does start with an alphabetic character, but a does not. Prioritize b - if (bStartsWithAlphabeticCharacter) return 1 - // neither route has a shortName that starts with an alphabetic character. - // Return equivalence - return 0 -} - -/** - * Checks whether an appropriate comparison of numeric values can be made for - * sorting purposes. If both values are not valid numbers according to the - * isNaN check, then this function returns undefined which indicates that a - * secondary sorting criteria should be used instead. If one value is valid and - * the other is not, then the valid value will be given sorting priority. If - * both values are valid numbers, the difference is obtained as the sort value. - * - * An optional argument can be provided which will be used to obtain the - * comparison value from the comparison function arguments. - * - * IMPORTANT: the comparison values must be numeric values or at least be - * attempted to be converted to numeric values! If one of the arguments is - * something crazy like an empty string, unexpected behavior will occur because - * JavaScript. - * - * @param {function} [objGetterFn] An optional function to obtain the - * comparison value from the comparator function arguments - */ -function makeNumericValueComparator (objGetterFn) { - return (a, b) => { - const { aVal, bVal } = getSortValues(objGetterFn, a, b) - // if both values aren't valid numbers, use the next sort criteria - if (isNaN(aVal) && isNaN(bVal)) return 0 - // b is a valid number, b gets priority - if (isNaN(aVal)) return 1 - // a is a valid number, a gets priority - if (isNaN(bVal)) return -1 - // a and b are valid numbers, return the sort value - return aVal - bVal - } -} - -/** - * Create a comparator function that compares string values. The comparison - * values feed to the sort comparator function are assumed to be objects that - * will have either undefined, null or string values at the given key. If one - * object has undefined, null or an empty string, but the other does have a - * string with length > 0, then that string will get priority. - * - * @param {function} [objGetterFn] An optional function to obtain the - * comparison value from the comparator function arguments - */ -function makeStringValueComparator (objGetterFn) { - return (a, b) => { - const { aVal, bVal } = getSortValues(objGetterFn, a, b) - // both a and b are uncomparable strings, return equivalent value - if (!aVal && !bVal) return 0 - // a is not a comparable string, b gets priority - if (!aVal) return 1 - // b is not a comparable string, a gets priority - if (!bVal) return -1 - // a and b are comparable strings, return the sort value - if (aVal < bVal) return -1 - if (aVal > bVal) return 1 - return 0 - } -} - -/** - * OpenTripPlanner sets the routeSortOrder to -999 by default. So, if that value - * is encountered, assume that it actually means that the routeSortOrder is not - * set in the GTFS. - * - * See https://github.com/opentripplanner/OpenTripPlanner/issues/2938 - * Also see https://github.com/opentripplanner/otp-react-redux/issues/122 - */ -function getRouteSortOrderValue (val) { - return val === -999 ? undefined : val -} - -/** - * Create a multi-criteria sort comparator function composed of other sort - * comparator functions. Each comparator function will be ran in the order given - * until a non-zero comparison value is obtained which is then immediately - * returned. If all comparison functions return equivalance, then the values - * are assumed to be equivalent. - */ -function makeMultiCriteriaSort (...criteria) { - return (a, b) => { - for (let i = 0; i < criteria.length; i++) { - const curCriteriaComparatorValue = criteria[i](a, b) - // if the comparison objects are not equivalent, return the value obtained - // in this current criteria comparison - if (curCriteriaComparatorValue !== 0) { - return curCriteriaComparatorValue - } - } - return 0 - } -} - -/** - * Compares routes for the purposes of sorting and displaying in a user - * interface. Due to GTFS feeds having varying levels of data quality, a multi- - * criteria sort is needed to account for various differences. The criteria - * included here are each applied to the routes in the order listed. If a given - * sort criterion yields equivalence (e.g., two routes have the short name - * "20"), the comparator falls back onto the next sort criterion (e.g., long - * name). If desired, the criteria of sorting based off of integer shortName can - * be disabled. The sort operates on the following values (in order): - * - * 1. sortOrder. Routes that do not have a valid sortOrder will be placed - * beneath those that do. - * 2. route type (OTP mode). See routeTypeComparator code for prioritization of - * route types. - * 3. shortNames that begin with alphabetic characters. shortNames that do not - * start with alphabetic characters will be place beneath those that do. - * 4. shortName as integer. shortNames that cannot be parsed as integers will - * be placed beneath those that are valid. - * 5. shortName as string. Routes without shortNames will be placed beneath - * those with shortNames. - * 6. longName as string. - */ -export const routeComparator = makeMultiCriteriaSort( - makeNumericValueComparator(obj => getRouteSortOrderValue(obj.sortOrder)), - routeTypeComparator, - alphabeticShortNameComparator, - makeNumericValueComparator(obj => parseInt(obj.shortName)), - makeStringValueComparator(obj => obj.shortName), - makeStringValueComparator(obj => obj.longName) -) - /** * Return an icon depending on the leg info *