diff --git a/craco.config.js b/craco.config.js index 283dc3350..074f3309e 100644 --- a/craco.config.js +++ b/craco.config.js @@ -48,6 +48,8 @@ module.exports = { findBackwardsCompatibleEnvVar('JS_CONFIG') backwardsCompatibleEnv.HTML_FILE = findBackwardsCompatibleEnvVar('HTML_FILE') + backwardsCompatibleEnv.PLAN_QUERY_RESOURCE_URI = + findBackwardsCompatibleEnvVar('PLAN_QUERY_RESOURCE_URI') backwardsCompatibleEnv.CUSTOM_CSS = findBackwardsCompatibleEnvVar('CUSTOM_CSS') @@ -65,6 +67,13 @@ module.exports = { } addBeforeLoader(webpackConfig, loaderByName('file-loader'), yamlLoader) + // Support import of raw GraphQL files + const graphqlLoader = { + loader: ['raw-loader'], + test: /\.graphql$/ + } + addBeforeLoader(webpackConfig, loaderByName('file-loader'), graphqlLoader) + // Support webfonts (for font awesome) const webfontLoader = { loader: ['url-loader'], @@ -82,7 +91,7 @@ module.exports = { loader.exclude = /node_modules/ }) - // Gather the CSS, HTML, YAML, and JS override files. + // Gather the CSS, HTML, YAML, GraphQL, and JS override files. const CUSTOM_CSS = (process.env && process.env.CUSTOM_CSS) || backwardsCompatibleEnv.CUSTOM_CSS || @@ -91,6 +100,22 @@ module.exports = { (process.env && process.env.HTML_FILE) || backwardsCompatibleEnv.HTML_FILE || 'lib/index.tpl.html' + // resolve the custom GraphQL file. If it is present, copy the file to a + // temporary folder within this project so that it can be bundled and loaded at runtime. + let customPlanGraphQLFile = './planQuery.graphql' + const PLAN_QUERY_RESOURCE_URI = + (process.env && process.env.PLAN_QUERY_RESOURCE_URI) || + backwardsCompatibleEnv.PLAN_QUERY_RESOURCE_URI || + 'node_modules/@opentripplanner/core-utils/src/planQuery.graphql' + if (PLAN_QUERY_RESOURCE_URI) { + const splitPath = PLAN_QUERY_RESOURCE_URI.split(path.sep) + customPlanGraphQLFile = `../tmp/${splitPath[splitPath.length - 1]}` + // copy location is relative to root, while js file for app is relative to lib + fs.copySync( + PLAN_QUERY_RESOURCE_URI, + `./tmp/${splitPath[splitPath.length - 1]}` + ) + } const YAML_CONFIG = (process.env && process.env.YAML_CONFIG) || backwardsCompatibleEnv.YAML_CONFIG || @@ -143,6 +168,7 @@ module.exports = { new webpack.DefinePlugin({ CSS: JSON.stringify(CUSTOM_CSS), JS_CONFIG: JSON.stringify(customJsFile), + PLAN_QUERY_RESOURCE: JSON.stringify(customPlanGraphQLFile), // Optionally override the default config files with some other // files. YAML_CONFIG: JSON.stringify(YAML_CONFIG) diff --git a/lib/actions/apiV2.js b/lib/actions/apiV2.js index a9e1deb57..6b4879fc8 100644 --- a/lib/actions/apiV2.js +++ b/lib/actions/apiV2.js @@ -964,6 +964,8 @@ export function routingQuery(searchId = null, updateSearchInReducer) { return function (dispatch, getState) { const state = getState() const { config, currentQuery, modeSettingDefinitions } = state.otp + const { planQuery } = config.api + const { loggedInUser } = state.user const persistenceMode = getPersistenceMode(config.persistence) const activeItinerary = getActiveItinerary(state) || @@ -1047,6 +1049,9 @@ export function routingQuery(searchId = null, updateSearchInReducer) { ...currentQuery, numItineraries: numItineraries || getDefaultNumItineraries(config) } + if (config.mobilityProfile) { + baseQuery.mobilityProfile = loggedInUser?.mobilityProfile?.mobilityMode + } // Generate combinations if the modes for query are not specified in the query // FIXME: BICYCLE_RENT does not appear in this list unless TRANSIT is also enabled. // This is likely due to the fact that BICYCLE_RENT is treated as a transit submode. @@ -1070,7 +1075,7 @@ export function routingQuery(searchId = null, updateSearchInReducer) { const query = generateOtp2Query(combo) dispatch( createGraphQLQueryAction( - query.query, + planQuery || query.query, query.variables, (response) => { const dispatchedRoutingResponse = routingResponse(response) diff --git a/lib/actions/user.js b/lib/actions/user.js index 1a1e9e993..837d7341e 100644 --- a/lib/actions/user.js +++ b/lib/actions/user.js @@ -270,7 +270,8 @@ function setUser(user, fetchTrips) { return function (dispatch, getState) { positionHomeAndWorkFirst(user) // If mobility profile is enabled, set a default selection for "no mobility devices". - if (getState().otp.config.mobilityProfile) { + const hasMobilityProfile = !!getState().otp.config.mobilityProfile + if (hasMobilityProfile) { setAtLeastNoMobilityDevice(user) } dispatch(setCurrentUser(user)) @@ -287,6 +288,23 @@ function setUser(user, fetchTrips) { if (!isBlank(preferredLocale)) { dispatch(setLocale(preferredLocale)) } + + // Also replan itinerary search for the current user profile. + if ( + hasMobilityProfile && + !getState().router.location.pathname.startsWith('/account') + ) { + // TODO: Refactor below. + // This prevents some kind of race condition whose origin I can't figure + // out. Unless this is called after redux catches up with routing to the '/' + // path, then the old path will be used and the screen won't change. + // Therefore, this timeout occurs so that the view of the homepage has time + // to render itself. + // FIXME: remove hack + setTimeout(() => { + dispatch(routingQuery()) + }, 300) + } } } diff --git a/lib/components/user/mobility-profile/mobility-pane.tsx b/lib/components/user/mobility-profile/mobility-pane.tsx index a45ea8b85..4004d0617 100644 --- a/lib/components/user/mobility-profile/mobility-pane.tsx +++ b/lib/components/user/mobility-profile/mobility-pane.tsx @@ -1,24 +1,17 @@ -import { Button } from 'react-bootstrap' -import { connect } from 'react-redux' import { FormattedList, FormattedMessage } from 'react-intl' import { FormikProps } from 'formik' -import React, { useCallback } from 'react' +import React from 'react' -import * as uiActions from '../../../actions/ui' import { EditedUser } from '../types' import { NONE_SINGLETON } from '../../../util/user' - -interface Props extends FormikProps { - routeTo: (url: string) => void -} +import Link from '../../util/link' /** * Renders a button to show the mobility profile settings. */ -const MobilityPane = ({ routeTo, values: userData }: Props): JSX.Element => { - const handleClick = useCallback(() => { - routeTo('/account/mobilityProfile/') - }, [routeTo]) +const MobilityPane = ({ + values: userData +}: FormikProps): JSX.Element => { const { isMobilityLimited, mobilityDevices = [], @@ -54,15 +47,11 @@ const MobilityPane = ({ routeTo, values: userData }: Props): JSX.Element => { id={`components.MobilityProfile.LimitationsPane.visionLimitations.${visionLimitation}`} />

- + ) } -const mapDispatchToProps = { - routeTo: uiActions.routeTo -} - -export default connect(null, mapDispatchToProps)(MobilityPane) +export default MobilityPane diff --git a/lib/components/user/types.ts b/lib/components/user/types.ts index dd866b1f2..44a281a3e 100644 --- a/lib/components/user/types.ts +++ b/lib/components/user/types.ts @@ -25,6 +25,7 @@ export type VisionLimitation = typeof visionLimitations[number] export interface MobilityProfile { isMobilityLimited: boolean mobilityDevices: string[] + mobilityMode: string visionLimitation: VisionLimitation } diff --git a/lib/main.js b/lib/main.js index 780584e27..03aeeb322 100644 --- a/lib/main.js +++ b/lib/main.js @@ -34,6 +34,18 @@ import(CSS) // eslint-disable-next-line no-undef const otpConfig = require(YAML_CONFIG) +// Loads a JavaScript file which is set in the webpack section of the craco.config.js file. +// This setting is defined from a custom environment setting passed into webpack or +// defaults to ./config.js +// defined in webpack config: +// The JS_CONFIG variable is passed to this file by webpack's `DefinePlugin` that replaces the variable +// with its content at compile time (like C's `#define` preprocessor directive). +// eslint-disable-next-line no-undef +const jsConfig = require(JS_CONFIG).configure(otpConfig) + +// If defined, plug custom plan query into the redux config, so it is available from actions. +otpConfig.api.planQuery = jsConfig.planQuery + const history = createHashHistory() const middleware = [ diff --git a/package.json b/package.json index 62073cc3d..8c7af7b2e 100644 --- a/package.json +++ b/package.json @@ -195,6 +195,7 @@ "pinst": "^2.1.6", "prettier": "^2.3.2", "puppeteer": "^10.2.0", + "raw-loader": "^4.0.2", "react-refresh": "^0.10.0", "react-scripts": "^4.0.3", "redux-mock-store": "^1.5.3", diff --git a/yarn.lock b/yarn.lock index d475f12a9..fae1e042f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15163,6 +15163,14 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +raw-loader@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" + integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + rc@^1.0.1, rc@^1.1.6, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"