From fe46050a52a6c98b65f01e058dc2d035324c68e9 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 30 Apr 2020 16:05:20 -0400 Subject: [PATCH 1/4] fix(routerId): persist routerId across sessions fix #161 --- lib/reducers/create-otp-reducer.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index d18fbdb20..88e272058 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -121,6 +121,16 @@ export function getInitialState (userDefinedConfig, initialQuery) { if (config.locations) { locations.push(...config.locations.map(l => ({ ...l, type: 'suggested' }))) } + // Check for alternative routerId in session storage. This is generally used + // for testing single GTFS feed OTP graphs that are deployed to feed-specific + // routers (e.g., https://otp.server.com/otp/routers/non_default_router). + // This routerId session value is initially set by visiting otp-rr + // with the path /start/:latLonZoomRouter, which dispatches the SET_ROUTER_ID + // action and stores the value in sessionStorage. + const routerId = window.sessionStorage.getItem('routerId') + if (routerId) { + config.api.path = `/otp/routers/${routerId}` + } let queryModes = currentQuery.mode.split(',') // If 'TRANSIT' is included in the mode list, replace it with individual modes @@ -543,6 +553,9 @@ function createOtpReducer (config, initialQuery) { }) case 'SET_ROUTER_ID': const routerId = action.payload || 'default' + // Store routerId in session storage (persists through page reloads but + // not when a new tab/window is opened). + window.sessionStorage.setItem('routerId', routerId) return update(state, { config: { api: { From 98b4790c83ed078ad3cea0c434f42fe7d65f39c9 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 1 May 2020 10:44:11 -0400 Subject: [PATCH 2/4] refactor(routerId): add confirm message to notify user about alt router --- lib/actions/ui.js | 12 +++++++++++- lib/reducers/create-otp-reducer.js | 19 ++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/actions/ui.js b/lib/actions/ui.js index 9dc8decfe..6fa4f3a7b 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -78,7 +78,17 @@ export function matchContentToUrl (location) { // Update map location/zoom and optionally override router ID. dispatch(setMapCenter({ lat, lon })) dispatch(setMapZoom({ zoom })) - if (routerId) dispatch(setRouterId(routerId)) + // If router ID is provided, override the default routerId. + if (routerId) { + // This is a somewhat hidden element of the trip planner, so show a + // confirmation message to the user to ensure they are aware of what + // is happening. + const useAltRouterId = window.confirm( + `The trip planner's router is set to ${routerId}.\n\nClick OK to confirm or Cancel to use the default router.\n\nNote: new sessions opened with URLs copied from this tab's address bar will not reference the correct router (must contain the #/start/lat/lon/zoom/router prefix).` + ) + if (useAltRouterId) dispatch(setRouterId(routerId)) + else dispatch(setRouterId(null)) + } dispatch(setMainPanelContent(null)) break // For any other route path, just revert to default panel. diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index 88e272058..56ad7077e 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -127,8 +127,12 @@ export function getInitialState (userDefinedConfig, initialQuery) { // This routerId session value is initially set by visiting otp-rr // with the path /start/:latLonZoomRouter, which dispatches the SET_ROUTER_ID // action and stores the value in sessionStorage. + // Note: this mechanism assumes that the OTP API path is otp/routers/default. const routerId = window.sessionStorage.getItem('routerId') + // If routerId is found, update the config.api.path (keep the original config + // value at _path in case it needs to be reverted.) if (routerId) { + config.api._path = userDefinedConfig.api.path config.api.path = `/otp/routers/${routerId}` } let queryModes = currentQuery.mode.split(',') @@ -552,14 +556,23 @@ function createOtpReducer (config, initialQuery) { } }) case 'SET_ROUTER_ID': - const routerId = action.payload || 'default' + const routerId = action.payload + // Store original path value in _path variable. + const _path = config.api._path || config.api.path || '/otp/routers/default' + const path = routerId + ? `/otp/routers/${routerId}` + // If routerId is null, revert to the original config's API path (or + // the standard path if that is not found). + : _path // Store routerId in session storage (persists through page reloads but // not when a new tab/window is opened). - window.sessionStorage.setItem('routerId', routerId) + if (routerId) window.sessionStorage.setItem('routerId', routerId) + else window.sessionStorage.removeItem('routerId') return update(state, { config: { api: { - path: { $set: `/otp/routers/${routerId}` } + path: { $set: path }, + _path: { $set: _path } } } }) From 22c40ae0ea69840396f9dc80c55e0ac32020d26f Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 4 Jun 2020 13:41:40 -0400 Subject: [PATCH 3/4] refactor: address PR #162 comments --- lib/actions/ui.js | 11 +---------- lib/reducers/create-otp-reducer.js | 12 ++++++------ 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/lib/actions/ui.js b/lib/actions/ui.js index 6fa4f3a7b..d0fa8dcd6 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -79,16 +79,7 @@ export function matchContentToUrl (location) { dispatch(setMapCenter({ lat, lon })) dispatch(setMapZoom({ zoom })) // If router ID is provided, override the default routerId. - if (routerId) { - // This is a somewhat hidden element of the trip planner, so show a - // confirmation message to the user to ensure they are aware of what - // is happening. - const useAltRouterId = window.confirm( - `The trip planner's router is set to ${routerId}.\n\nClick OK to confirm or Cancel to use the default router.\n\nNote: new sessions opened with URLs copied from this tab's address bar will not reference the correct router (must contain the #/start/lat/lon/zoom/router prefix).` - ) - if (useAltRouterId) dispatch(setRouterId(routerId)) - else dispatch(setRouterId(null)) - } + if (routerId) dispatch(setRouterId(routerId)) dispatch(setMainPanelContent(null)) break // For any other route path, just revert to default panel. diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index 56ad7077e..0b5c6218d 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -130,9 +130,9 @@ export function getInitialState (userDefinedConfig, initialQuery) { // Note: this mechanism assumes that the OTP API path is otp/routers/default. const routerId = window.sessionStorage.getItem('routerId') // If routerId is found, update the config.api.path (keep the original config - // value at _path in case it needs to be reverted.) + // value at originalPath in case it needs to be reverted.) if (routerId) { - config.api._path = userDefinedConfig.api.path + config.api.originalPath = userDefinedConfig.api.path config.api.path = `/otp/routers/${routerId}` } let queryModes = currentQuery.mode.split(',') @@ -557,13 +557,13 @@ function createOtpReducer (config, initialQuery) { }) case 'SET_ROUTER_ID': const routerId = action.payload - // Store original path value in _path variable. - const _path = config.api._path || config.api.path || '/otp/routers/default' + // Store original path value in originalPath variable. + const originalPath = config.api.originalPath || config.api.path || '/otp/routers/default' const path = routerId ? `/otp/routers/${routerId}` // If routerId is null, revert to the original config's API path (or // the standard path if that is not found). - : _path + : originalPath // Store routerId in session storage (persists through page reloads but // not when a new tab/window is opened). if (routerId) window.sessionStorage.setItem('routerId', routerId) @@ -572,7 +572,7 @@ function createOtpReducer (config, initialQuery) { config: { api: { path: { $set: path }, - _path: { $set: _path } + originalPath: { $set: originalPath } } } }) From ea7e634bd4455e93c460f383d80b264a2f6e754d Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 19 Jun 2020 14:00:22 -0400 Subject: [PATCH 4/4] fix(routerId): if routerId present, include in copied URL --- lib/actions/ui.js | 13 +++++++++---- lib/components/app/responsive-webapp.js | 10 +++++----- lib/components/narrative/trip-tools.js | 18 ++++++++++++++++-- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/lib/actions/ui.js b/lib/actions/ui.js index d0fa8dcd6..ad29cb6b8 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -71,13 +71,14 @@ export function matchContentToUrl (location) { // Parse comma separated params (ensuring numbers are parsed correctly). let [lat, lon, zoom, routerId] = id ? idToParams(id) : [] if (!lat || !lon) { - // Attempt to parse path. (Legacy UI otp.js used slashes in the - // pathname to specify lat, lon, etc.) + // Attempt to parse path if lat/lon not found. (Legacy UI otp.js used + // slashes in the pathname to specify lat, lon, etc.) [,, lat, lon, zoom, routerId] = idToParams(location.pathname, '/') } + console.log(lat, lon, zoom, routerId) // Update map location/zoom and optionally override router ID. - dispatch(setMapCenter({ lat, lon })) - dispatch(setMapZoom({ zoom })) + if (+lat && +lon) dispatch(setMapCenter({ lat, lon })) + if (+zoom) dispatch(setMapZoom({ zoom })) // If router ID is provided, override the default routerId. if (routerId) dispatch(setRouterId(routerId)) dispatch(setMainPanelContent(null)) @@ -90,6 +91,10 @@ export function matchContentToUrl (location) { } } +/** + * Split the path id into its parts (according to specified delimiter). Parse + * numbers if detected. + */ function idToParams (id, delimiter = ',') { return id.split(delimiter).map(s => isNaN(s) ? s : +s) } diff --git a/lib/components/app/responsive-webapp.js b/lib/components/app/responsive-webapp.js index eea5f49a7..45b130c41 100644 --- a/lib/components/app/responsive-webapp.js +++ b/lib/components/app/responsive-webapp.js @@ -106,20 +106,20 @@ class ResponsiveWebapp extends Component { { enableHighAccuracy: true } ) } - + // Handle routing to a specific part of the app (e.g. stop viewer) on page + // load. (This happens prior to routing request in case special routerId is + // set from URL.) + this.props.matchContentToUrl(this.props.location) if (location && location.search) { // Set search params and plan trip if routing enabled and a query exists // in the URL. this.props.parseUrlQueryString() } - // Handle routing to a specific part of the app (e.g. stop viewer) on page - // load. - this.props.matchContentToUrl(this.props.location) } componentWillUnmount () { // Remove on back button press listener. - window.removeEventListener('popstate') + window.removeEventListener('popstate', this.props.handleBackButtonPress) } render () { diff --git a/lib/components/narrative/trip-tools.js b/lib/components/narrative/trip-tools.js index 150dfa1fe..2717bf26f 100644 --- a/lib/components/narrative/trip-tools.js +++ b/lib/components/narrative/trip-tools.js @@ -77,10 +77,24 @@ class CopyUrlButton extends Component { this.state = { showCopied: false } } + _resetState = () => this.setState({ showCopied: false }) + _onClick = () => { - copyToClipboard(window.location.href) + // If special routerId has been set in session storage, construct copy URL + // for itinerary with #/start/ prefix to set routerId on page load. + const routerId = window.sessionStorage.getItem('routerId') + let url = window.location.href + if (routerId) { + const parts = url.split('#') + if (parts.length === 2) { + url = `${parts[0]}#/start/x/x/x/${routerId}${parts[1]}` + } else { + console.warn('URL not formatted as expected, copied URL will not contain session routerId.', routerId) + } + } + copyToClipboard(url) this.setState({ showCopied: true }) - setTimeout(() => { this.setState({ showCopied: false }) }, 2000) + window.setTimeout(this._resetState, 2000) } render () {