Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
b378eb2
feat(route-viewer): support filtering routes by agency or mode
miles-grant-ibigroup Aug 18, 2021
0196e59
improvement(route-viewer): allow filtering routes via search
miles-grant-ibigroup Aug 19, 2021
e4e246b
improvement(app-menu): move route viewer link to main navbar
miles-grant-ibigroup Aug 19, 2021
b5dc791
refactor: adjust component behavior to match new route viewer link
miles-grant-ibigroup Aug 19, 2021
1843b83
refactor(desktop-nav): allow hamburger menu to be clicked
miles-grant-ibigroup Aug 19, 2021
bc7e27e
feat(route-viewer): route details viewer prototype
miles-grant-ibigroup Aug 19, 2021
9258e4c
refactor: resolve react/redux errors
miles-grant-ibigroup Aug 20, 2021
e886cfc
refactor(route-viewer): move filtering logic to redux
miles-grant-ibigroup Aug 20, 2021
ba5c1d6
test: update snapshots
miles-grant-ibigroup Aug 20, 2021
f638c7f
refactor(route-viewer): css and a11y adjustments
miles-grant-ibigroup Aug 20, 2021
b0b505e
chore: make css cross-browser compatible
miles-grant-ibigroup Aug 20, 2021
95b844c
revert: adjust webkit specific rules to favor chrome over safari
miles-grant-ibigroup Aug 20, 2021
ae4bf35
refactor(route-viewer): internationalize route-viewer component
miles-grant-ibigroup Aug 20, 2021
b046f09
refactor: fully remove languageConfig among new code
miles-grant-ibigroup Aug 20, 2021
428ff8b
refactor(route-viewer): replace superflous react state with redux state
miles-grant-ibigroup Aug 23, 2021
0478aac
Merge branch 'dev' into route-viewer-filter-and-search
miles-grant-ibigroup Aug 23, 2021
67f840b
revert(route-viewer): never hide mode selector
miles-grant-ibigroup Aug 23, 2021
6518355
refactor(route-viewer): fix localization id
miles-grant-ibigroup Aug 24, 2021
ec7e885
refactor(route-viewer): move route sorting to redux
miles-grant-ibigroup Aug 24, 2021
3dc9b27
improvement: have both nav bar buttons be verbs
miles-grant-ibigroup Aug 24, 2021
1c0655b
improvement(route-viewer): improve color styling using tinycolor
miles-grant-ibigroup Aug 24, 2021
3011751
improvement(route-viewer): route details functionality + fill full si…
miles-grant-ibigroup Aug 24, 2021
2b85bbc
revert(app-menu): show route viewer in app menu on mobile
miles-grant-ibigroup Aug 25, 2021
7a0f5ae
Merge branch 'dev' into route-details-viewer
miles-grant-ibigroup Aug 25, 2021
0942770
improvement(route-details): show stops of pattern
miles-grant-ibigroup Aug 25, 2021
6e61767
improvement(route-viewer): resolve route details stop list edge cases
miles-grant-ibigroup Aug 26, 2021
ced75d8
feat(route-viewer): show realtime vehicle positions
miles-grant-ibigroup Aug 26, 2021
583982f
refactor: avoid crash on missing route data
miles-grant-ibigroup Aug 26, 2021
516d21a
improvement(map): when viewing pattern, show only pattern and its stops
miles-grant-ibigroup Aug 26, 2021
c0ccde2
improvement(route-details-viewer): sort headsigns by number of vehicl…
miles-grant-ibigroup Aug 26, 2021
747e651
style(route-viewer): cleanup
miles-grant-ibigroup Aug 26, 2021
99ce62c
improvement(map): realtime vehicles show icon based on their mode
miles-grant-ibigroup Aug 26, 2021
9df6f56
improvement(route-details): support routing directly to pattern viewer
miles-grant-ibigroup Aug 26, 2021
fef76d1
improvement(route-details): resolve duplicate headsigns based on geom…
miles-grant-ibigroup Aug 27, 2021
f99cd2c
improvement(api): reduce queries to OTP by receiving all pattern geom…
miles-grant-ibigroup Aug 27, 2021
9baddca
refactor(connected-stops-overlay): fix regression and show pattern stops
miles-grant-ibigroup Aug 27, 2021
f1d6f5c
improvement(route-details): highlight hovered stops on map
miles-grant-ibigroup Aug 27, 2021
0bef23b
improvement(route-details): show realtime vehicle position info on hover
miles-grant-ibigroup Aug 27, 2021
10baa92
refactor(connected-transit-vehicle-overlay): adjust tooltip to server…
miles-grant-ibigroup Aug 27, 2021
2f07d6b
revert: match OTP vehicle position schema
miles-grant-ibigroup Aug 27, 2021
85bcb56
revert: don't show view switcher on mobile
miles-grant-ibigroup Aug 30, 2021
511c242
refactor(route-viewer): complete internationalization
miles-grant-ibigroup Aug 30, 2021
0e2363a
refactor(route-viewer): add back button on desktop
miles-grant-ibigroup Aug 30, 2021
237eeac
refactor: address pr comments
miles-grant-ibigroup Aug 30, 2021
dbf66c7
refactor: more robust path generation
miles-grant-ibigroup Aug 30, 2021
ac380e1
Merge branch 'route-viewer-filter-and-search' into route-details-viewer
miles-grant-ibigroup Aug 30, 2021
78686a5
refactor: polish and cleanup
miles-grant-ibigroup Aug 30, 2021
62843e8
improvement(route-details): add internationalization
miles-grant-ibigroup Aug 30, 2021
8e45300
refactor(route-details-viewer): avoid white-screen crashes
miles-grant-ibigroup Aug 31, 2021
a52e1df
refactor(route-details-viewer): avoid white-screen crashes when updat…
miles-grant-ibigroup Sep 1, 2021
bfb7698
improvement(route-viewer): scroll to active route on initial render
miles-grant-ibigroup Sep 1, 2021
a981abe
refactor: address PR feedback
miles-grant-ibigroup Sep 2, 2021
712c2a1
Merge branch 'route-viewer-filter-and-search' into route-details-viewer
miles-grant-ibigroup Sep 7, 2021
93cdd78
Merge branch 'dev' into route-viewer-filter-and-search
miles-grant-ibigroup Sep 13, 2021
b33516c
chore(i18n): Tweak translation files.
binh-dam-ibigroup Sep 13, 2021
c55bc53
Merge branch 'route-viewer-filter-and-search' into route-details-viewer
miles-grant-ibigroup Sep 14, 2021
cc444a4
refactor(route-details): update icon import
miles-grant-ibigroup Sep 14, 2021
2d07989
refactor: correct typo
miles-grant-ibigroup Sep 14, 2021
3bcaf13
refactor: address pr comments
miles-grant-ibigroup Sep 14, 2021
59bdf8f
refactor(route-viewer): address pr comments
miles-grant-ibigroup Sep 14, 2021
a922e8f
revert(ViewSwitcher): restore focus outline
miles-grant-ibigroup Sep 15, 2021
25aa724
refactor(route-details): address pr feedback
miles-grant-ibigroup Sep 15, 2021
ac6e90d
Merge branch 'route-viewer-filter-and-search' into route-details-viewer
miles-grant-ibigroup Sep 15, 2021
d5080c9
refactor(connected-transit-vehicle-overlay): adjust for latest OTP ch…
miles-grant-ibigroup Sep 15, 2021
c672387
revert(RouteDetails): remove color; remove headsign buttons
miles-grant-ibigroup Sep 16, 2021
140c81e
refactor(RouteViewer): clean up, extract routeRow to new file
miles-grant-ibigroup Sep 16, 2021
30f4a83
improvement: show all route shapes when OTP instances don't support i…
miles-grant-ibigroup Sep 17, 2021
9a71395
refactor: address pr comments
miles-grant-ibigroup Sep 20, 2021
a5ea1e0
chore(i18n): Add matching FR strings.
binh-dam-ibigroup Sep 20, 2021
092b53f
refactor: correct typos and sort imports
miles-grant-ibigroup Sep 20, 2021
3483791
Merge pull request #441 from opentripplanner/route-details-viewer
miles-grant-ibigroup Sep 20, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions __tests__/reducers/__snapshots__/create-otp-reducer.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Object {
"rideEstimates": Object {},
},
"transitIndex": Object {
"routes": Object {},
"stops": Object {},
"trips": Object {},
},
Expand All @@ -102,6 +103,13 @@ Object {
"localizedMessages": Object {},
"mobileScreen": 1,
"printView": false,
"routeViewer": Object {
"filter": Object {
"agency": null,
"mode": null,
"search": "",
},
},
},
"useRealtime": true,
"user": Object {
Expand Down
2 changes: 1 addition & 1 deletion example.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

.sidebar {
height: 100%;
padding: 10px;
padding: 0;
box-shadow: 3px 0px 12px #00000052;
z-index: 1000;
}
Expand Down
37 changes: 36 additions & 1 deletion i18n/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,47 @@ _name: English
# - In contrast, some strings are common to multiple components,
# so it makes sense to group them by theme (e.g. accessModes) under the 'common' category.


# Component-specific messages (e.g. button captions)
# are defined for each component under the 'components' category.
components:
BatchRoutingPanel:
shortTitle: Plan Trip
DefaultItinerary:
clickDetails: Click to view details
# Use ordered placeholders when multiple modes are involved
# (this will accommodate right-to-left languages by swapping the order/separator in this string).
multiModeSummary: "{accessMode} to {transitMode}"
# If trip is less than one hour only display the minutes.
tripDurationFormatZeroHours: "{minutes, number} min"
# TODO: Distinguish between one hour (singular) and 2 hours or more?
tripDurationFormat: "{hours, number} hr {minutes, number} min"
RouteDetails:
operatedBy: "Operated by {agencyName}"
moreDetails: "More Details"
stopsTo: "Towards"
selectADirection: "Select a direction..."
RouteViewer:
allAgencies: All Agencies
allModes: All Modes # Note to translator: This text is width-constrained.
findARoute: Find A Route
noFilteredRoutesFound: No routes match your filter!
noRouteUrl: No route URL provided.
title: Route Viewer
shortTitle: View Routes
agencyFilter: Agency Filter
modeFilter: Mode Filter
details: " " # If the string is left blank, React-Intl renders the id
RouteRow:
operatorLogoAltText: '{operatorName} logo'
TransitVehicleOverlay:
# keys designed to match API output
incoming_at: "approaching {stop}"
stopped_at: "doors open at {stop}"
in_transit_to: "next stop {stop}"

vehicleName: "Vehicle {vehicleNumber}: "
realtimeVehicleInfo: "<b>{vehicleNameOrBlank}</b>{relativeTime}"
travelingAt: "traveling at {milesPerHour}"
ItinerarySummary:
fareCost: "{useMaxFare, select,
true {{minTotalFare} - {maxTotalFare}}
Expand Down Expand Up @@ -114,6 +146,9 @@ components:
# Common messages that appear in multiple components and modules
# are grouped below by topic.
common:
# Standard navigation
navigation:
back: Back
# OTP access modes
accessModes:
bike: Bike
Expand Down
36 changes: 36 additions & 0 deletions i18n/fr-FR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,42 @@ _id: fr-FR
_name: Unofficial French Translations!

components:
BatchRoutingPanel:
shortTitle: Planifier un trajet
DefaultItinerary:
clickDetails: Cliquez pour afficher les détails
multiModeSummary: "{accessMode} + {transitMode}"
# If trip is less than one hour only display the minutes.
tripDurationFormatZeroHours: "{minutes, number} mn"
# TODO: Distinguish between one hour (singular) and 2 hours or more?
tripDurationFormat: "{hours, number} h {minutes, number} mn"
RouteDetails:
operatedBy: "Exploité par {agencyName}"
moreDetails: "Plus d'infos"
stopsTo: "Direction"
selectADirection: "Choisissez une direction..."
RouteViewer:
allAgencies: Tous exploitants
allModes: Tous modes # Note to translator: This text is width-constrained.
findARoute: Chercher une ligne
noFilteredRoutesFound: Aucune ligne ne correspond à vos critères
noRouteUrl: Aucun lien fourni pour cette ligne.
title: Index des lignes
shortTitle: Index des lignes
agencyFilter: Filtre pour les exploitants
modeFilter: Filtre pour les modes
details: " " # If the string is left blank, React-Intl renders the id
RouteRow:
operatorLogoAltText: "Logo de {operatorName}"
TransitVehicleOverlay:
# keys designed to match API output
incoming_at: "Approchant {stop}"
stopped_at: "À quai à {stop}"
in_transit_to: "Prochain arrêt : {stop}"

vehicleName: "Véhicule {vehicleNumber}: "
realtimeVehicleInfo: "<b>{vehicleNameOrBlank}</b>{relativeTime}"
travelingAt: "Vitesse : {milesPerHour}"
ItinerarySummary:
fareCost: "{useMaxFare, select,
true {{minTotalFare} - {maxTotalFare}}
Expand Down Expand Up @@ -85,6 +118,9 @@ components:
startOver: Recommencer

common:
# Standard navigation
navigation:
back: Retour
accessModes:
bike: Vélo
bikeshare: Vélo en libre-service
Expand Down
61 changes: 56 additions & 5 deletions lib/actions/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -507,19 +507,25 @@ export function findRoute (params) {

export function findPatternsForRoute (params) {
return createQueryAction(
`index/routes/${params.routeId}/patterns`,
`index/routes/${params.routeId}/patterns?includeGeometry=true`,
findPatternsForRouteResponse,
findPatternsForRouteError,
{
noThrottle: true,
postprocess: (payload, dispatch) => {
// load geometry for each pattern
payload.forEach(ptn => {
dispatch(findGeometryForPattern({
patternId: ptn.id,
routeId: params.routeId
}))
// Some OTP instances don't support includeGeometry.
// We need to manually fetch geometry in these cases.
if (!ptn.geometry) {
dispatch(findGeometryForPattern({
patternId: ptn.id,
routeId: params.routeId
}))
}
})
},

rewritePayload: (payload) => {
// convert pattern array to ID-mapped object
const patterns = {}
Expand Down Expand Up @@ -556,6 +562,29 @@ export function findGeometryForPattern (params) {
)
}

// Stops for pattern query

export const findStopsForPatternResponse = createAction('FIND_STOPS_FOR_PATTERN_RESPONSE')
export const findStopsForPatternError = createAction('FIND_STOPS_FOR_PATTERN_ERROR')

export function findStopsForPattern (params) {
return createQueryAction(
`index/patterns/${params.patternId}/stops`,
findStopsForPatternResponse,
findStopsForPatternError,
{
noThrottle: true,
rewritePayload: (payload) => {
return {
patternId: params.patternId,
routeId: params.routeId,
stops: payload
}
}
}
)
}

// TNC ETA estimate lookup query

export const transportationNetworkCompanyEtaResponse = createAction('TNC_ETA_RESPONSE')
Expand Down Expand Up @@ -682,6 +711,27 @@ export function findStopsWithinBBox (params) {

export const clearStops = createAction('CLEAR_STOPS_OVERLAY')

// Realtime Vehicle positions query

const receivedVehiclePositions = createAction('REALTIME_VEHICLE_POSITIONS_RESPONSE')
const receivedVehiclePositionsError = createAction('REALTIME_VEHICLE_POSITIONS_ERROR')

export function getVehiclePositionsForRoute (routeId) {
return createQueryAction(
`index/routes/${routeId}/vehicles`,
receivedVehiclePositions,
receivedVehiclePositionsError,
{
rewritePayload: (payload) => {
return {
routeId: routeId,
vehicles: payload
}
}
}
)
}

const throttledUrls = {}

function now () {
Expand Down Expand Up @@ -720,6 +770,7 @@ window.setInterval(() => {
*/

function createQueryAction (endpoint, responseAction, errorAction, options = {}) {
/* eslint-disable-next-line complexity */
return async function (dispatch, getState) {
const state = getState()
const { config } = state.otp
Expand Down
59 changes: 49 additions & 10 deletions lib/actions/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import coreUtils from '@opentripplanner/core-utils'
import { createAction } from 'redux-actions'
import { matchPath } from 'react-router'

import { getUiUrlParams } from '../util/state'
import { getUiUrlParams, getModesForActiveAgencyFilter } from '../util/state'
import { getDefaultLocale, loadLocaleData } from '../util/i18n'
import { getPathFromParts } from '../util/ui'

import { findRoute, setUrlSearch } from './api'
import { setMapCenter, setMapZoom, setRouterId } from './config'
Expand Down Expand Up @@ -47,22 +48,31 @@ export function routeTo (url, replaceSearch, routingMethod = push) {
* route or stop).
*/
export function matchContentToUrl (location) {
// eslint-disable-next-line complexity
return function (dispatch, getState) {
// This is a bit of a hack to make up for the fact that react-router does
// not always provide the match params as expected.
// https://github.com/ReactTraining/react-router/issues/5870#issuecomment-394194338
const root = location.pathname.split('/')[1]
const match = matchPath(location.pathname, {
exact: true,
exact: false,
path: `/${root}/:id`,
strict: false
})
const id = match && match.params && match.params.id
const id = match?.params?.id
switch (root) {
case 'route':
if (id) {
dispatch(findRoute({ routeId: id }))
dispatch(setViewedRoute({ routeId: id }))
// Check for pattern "submatch"
const subMatch = matchPath(location.pathname, {
exact: true,
path: `/${root}/:id/pattern/:patternId`,
strict: false
})
const patternId = subMatch?.params?.patternId
// patternId may be undefined, which is OK as the route will still be routed
dispatch(setViewedRoute({ patternId, routeId: id }))
} else {
dispatch(setViewedRoute(null))
dispatch(setMainPanelContent(MainPanelContent.ROUTE_VIEWER))
Expand Down Expand Up @@ -196,23 +206,29 @@ export const clearPanel = createAction('CLEAR_MAIN_PANEL')
export function setViewedStop (payload) {
return function (dispatch, getState) {
dispatch(viewStop(payload))
const path = payload && payload.stopId
? `/stop/${payload.stopId}`
: '/stop'
// payload.stopId may be undefined, which is ok as will be ignored by getPathFromParts
const path = getPathFromParts('stop', payload?.stopId)
dispatch(routeTo(path))
}
}

const viewStop = createAction('SET_VIEWED_STOP')

export const setHoveredStop = createAction('SET_HOVERED_STOP')

export const setViewedTrip = createAction('SET_VIEWED_TRIP')

export function setViewedRoute (payload) {
return function (dispatch, getState) {
dispatch(viewRoute(payload))
const path = payload && payload.routeId
? `/route/${payload.routeId}`
: '/route'

const path = getPathFromParts(
'route',
payload?.routeId,
// If a pattern is supplied, include pattern in path
payload?.patternId && 'pattern',
payload?.patternId
)
dispatch(routeTo(path))
}
}
Expand Down Expand Up @@ -337,3 +353,26 @@ export function setLocale (locale) {
dispatch(updateLocale({ locale: effectiveLocale, messages }))
}
}

const updateRouteViewerFilter = createAction('UPDATE_ROUTE_VIEWER_FILTER')
/**
* Updates the route viewer filter
* @param {*} filter Object which includes either agency, mode, and/or search
*/
export function setRouteViewerFilter (filter) {
return async function (dispatch, getState) {
dispatch(updateRouteViewerFilter(filter))

// If we're changing agency, and have a mode selected,
// ensure that the mode filter doesn't select non-existent modes!
const activeModeFilter = getState().otp.ui.routeViewer.filter.mode
if (
filter.agency &&
activeModeFilter &&
!getModesForActiveAgencyFilter(getState()).includes(activeModeFilter.toUpperCase())
) {
// If invalid mode is selected, reset mode
dispatch(updateRouteViewerFilter({ mode: null }))
}
}
}
9 changes: 4 additions & 5 deletions lib/components/app/app-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ class AppMenu extends Component {
const {
callTakerEnabled,
fieldTripEnabled,
languageConfig,
mailablesEnabled,
resetAndToggleCallHistory,
resetAndToggleFieldTrips,
Expand All @@ -60,8 +59,10 @@ class AppMenu extends Component {
id='app-menu'
noCaret
title={(<Icon type='bars' />)}>
<MenuItem onClick={this._showRouteViewer}>
<Icon type='bus' /> {languageConfig.routeViewer || 'Route Viewer'}
{/* This item is duplicated by the view-switcher, but only shown on mobile
when the view switcher isn't shown (using css) */}
<MenuItem className='app-menu-route-viewer-link' onClick={this._showRouteViewer}>
<Icon type='bus' /> Route Viewer
</MenuItem>
{callTakerEnabled &&
<MenuItem onClick={resetAndToggleCallHistory}>
Expand Down Expand Up @@ -90,11 +91,9 @@ class AppMenu extends Component {
// connect to the redux store

const mapStateToProps = (state, ownProps) => {
const {language} = state.otp.config
return {
callTakerEnabled: isModuleEnabled(state, Modules.CALL_TAKER),
fieldTripEnabled: isModuleEnabled(state, Modules.FIELD_TRIP),
languageConfig: language,
mailablesEnabled: isModuleEnabled(state, Modules.MAILABLES)
}
}
Expand Down
Loading