From 3f68af9fd0633d4fa752352f51f3534865a3c0c1 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 61ced6a35..66206089e 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -119,6 +119,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 @@ -541,6 +551,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 6ecb299af4e50c07750711983bedca429c3c9f73 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 ad04d2805..dc186ddf2 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -77,7 +77,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 66206089e..50a28e1de 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -125,8 +125,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(',') @@ -550,14 +554,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 e97b58fc6e2ed206521eb15e3fa015095baa68df 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 dc186ddf2..bf28845b7 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -78,16 +78,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 50a28e1de..90fd3f7b6 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -128,9 +128,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(',') @@ -555,13 +555,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) @@ -570,7 +570,7 @@ function createOtpReducer (config, initialQuery) { config: { api: { path: { $set: path }, - _path: { $set: _path } + originalPath: { $set: originalPath } } } }) From 3d1bbb58a73db237fd1340f260da194f48566ea5 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 bf28845b7..c4f386244 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -70,13 +70,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)) @@ -89,6 +90,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 9705cd327..92d98be1f 100644 --- a/lib/components/app/responsive-webapp.js +++ b/lib/components/app/responsive-webapp.js @@ -105,20 +105,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 () {