diff --git a/lib/actions/ui.js b/lib/actions/ui.js index ad04d2805..c4f386244 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -70,13 +70,15 @@ 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)) break @@ -88,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 () { diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index 61ced6a35..90fd3f7b6 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -119,6 +119,20 @@ 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. + // 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 originalPath in case it needs to be reverted.) + if (routerId) { + config.api.originalPath = userDefinedConfig.api.path + config.api.path = `/otp/routers/${routerId}` + } let queryModes = currentQuery.mode.split(',') // If 'TRANSIT' is included in the mode list, replace it with individual modes @@ -540,11 +554,23 @@ function createOtpReducer (config, initialQuery) { } }) case 'SET_ROUTER_ID': - const routerId = action.payload || 'default' + const routerId = action.payload + // 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). + : 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) + else window.sessionStorage.removeItem('routerId') return update(state, { config: { api: { - path: { $set: `/otp/routers/${routerId}` } + path: { $set: path }, + originalPath: { $set: originalPath } } } })