diff --git a/lib/actions/user.js b/lib/actions/user.js index 8758fe1d4..3f446fd0d 100644 --- a/lib/actions/user.js +++ b/lib/actions/user.js @@ -24,6 +24,7 @@ import { isBlank } from '../util/ui' import { secureFetch } from '../util/middleware' import { TRIPS_PATH } from '../util/constants' +import { MobileScreens } from './ui-constants' import { routeTo, setLocale } from './ui' import { routingQuery, setUrlSearch } from './api' import { setQueryParam } from './form' @@ -270,6 +271,25 @@ export function fetchTripRequests() { } } +export function applyUserSavedTripDefaults(userSavedTripDefaults) { + return function (dispatch, getState) { + const state = getState() + const { mainPanelContent, mobileScreen } = state.otp.ui + /* Only apply when we're looking at the trip form and if the user + isn't already in the middle of a search */ + if ( + !window.location.href.includes('activeSearch') && + (coreUtils.ui.isMobile() + ? mobileScreen === MobileScreens.SEARCH_FORM + : !mainPanelContent) + ) { + const userDefaultParams = JSON.parse(userSavedTripDefaults) + dispatch(setQueryParam(userDefaultParams)) + dispatch(setUrlSearch(userDefaultParams, true)) + } + } +} + /** * Updates the redux state with the provided user data, including * placing the Home and Work locations at the beginning of the list @@ -297,11 +317,6 @@ function setUser(user, fetchTrips) { userSavedTripDefaults } = user - if (userSavedTripDefaults) { - const userDefaultParams = JSON.parse(userSavedTripDefaults) - dispatch(setQueryParam(userDefaultParams)) - dispatch(setUrlSearch(userDefaultParams, true)) - } if (fetchTrips) { dispatch(fetchMonitoredTrips()) dispatch(fetchTripRequests()) diff --git a/lib/components/form/advanced-settings-panel.tsx b/lib/components/form/advanced-settings-panel.tsx index 75747c125..4617525fa 100644 --- a/lib/components/form/advanced-settings-panel.tsx +++ b/lib/components/form/advanced-settings-panel.tsx @@ -32,7 +32,11 @@ import * as userActions from '../../actions/user' import { AppReduxState } from '../../util/state-types' import { blue, getBaseColor, grey } from '../util/colors' import { ComponentContext } from '../../util/contexts' -import { generateModeSettingValues } from '../../util/api' +import { + generateModeSettingValues, + getDefaultModeButtons, + getDefaultModeSettingValues +} from '../../util/api' import { getAuth0Config } from '../../util/auth' import { getDependentName } from '../../util/user' import { IconWithText } from '../util/styledIcon' @@ -395,10 +399,13 @@ const queryParamConfig = { modeButtons: DelimitedArrayParam } const mapStateToProps = (state: AppReduxState) => { const urlSearchParams = new URLSearchParams(state.router.location.search) const { modes } = state.otp.config + const defaultModeSettingValues = getDefaultModeSettingValues(state) + const defaultModeButtons = getDefaultModeButtons(state) + const modeSettingValues = generateModeSettingValues( urlSearchParams, - state.otp.modeSettingDefinitions || [], - modes?.initialState?.modeSettingValues || {} + state.otp.modeSettingDefinitions ?? [], + defaultModeSettingValues ) const user = state.user.loggedInUser @@ -412,9 +419,8 @@ const mapStateToProps = (state: AppReduxState) => { enabledModeButtons: decodeQueryParams(queryParamConfig, { modeButtons: urlSearchParams.get('modeButtons') - })?.modeButtons?.filter((mb): mb is string => mb !== null) || - modes?.initialState?.enabledModeButtons || - [], + })?.modeButtons?.filter((mb): mb is string => mb !== null) ?? + defaultModeButtons, loggedInUser: state.user.loggedInUser, mobilityProfile: state.otp.config?.mobilityProfile || false, modeButtonOptions: modes?.modeButtons || [], diff --git a/lib/components/form/batch-settings.tsx b/lib/components/form/batch-settings.tsx index 6ff733062..ce787dda0 100644 --- a/lib/components/form/batch-settings.tsx +++ b/lib/components/form/batch-settings.tsx @@ -8,20 +8,23 @@ import { ModeButtonDefinition } from '@opentripplanner/types' import { Search } from '@styled-icons/fa-solid/Search' import { SyncAlt } from '@styled-icons/fa-solid/SyncAlt' import { useIntl } from 'react-intl' +import AnimateHeight from 'react-animate-height' import React, { useCallback, useContext, useEffect } from 'react' import * as apiActions from '../../actions/api' import * as formActions from '../../actions/form' import * as narrativeActions from '../../actions/narrative' +import * as userAction from '../../actions/user' import { ComponentContext } from '../../util/contexts' import { getActiveSearch, hasValidLocation } from '../../util/state' import { getBaseColor, getDarkenedBaseColor } from '../util/colors' +import { getDefaultModeButtons } from '../../util/api' import { StyledIconWrapper } from '../util/styledIcon' -import AnimateHeight from 'react-animate-height' +import { User } from '../user/types' +// eslint-disable-next-line sort-imports-es6-autofix/sort-imports-es6 import { addModeButtonIcon, - alertUserTripPlan, modesQueryParamConfig, onSettingsUpdate, pipe, @@ -42,6 +45,7 @@ import DateTimeModal, { // TYPESCRIPT TODO: better types type Props = { activeSearch: any + applyUserSavedTripDefaults: (arg: string) => void currentQuery: any departArrive: DepartArriveValue enabledModeButtons: string[] @@ -55,6 +59,7 @@ type Props = { sort: any syncSortWithDepartArrive: any updateItineraryFilter: any + user: User } export function setModeButtonEnabled(enabledKeys: string[]) { @@ -71,6 +76,7 @@ export function setModeButtonEnabled(enabledKeys: string[]) { */ function BatchSettings({ activeSearch, + applyUserSavedTripDefaults, currentQuery, departArrive, enabledModeButtons, @@ -79,11 +85,11 @@ function BatchSettings({ modeButtonOptions, onPlanTripClick, openAdvancedSettings, - routingQuery, setQueryParam, sort, syncSortWithDepartArrive, - updateItineraryFilter + updateItineraryFilter, + user }: Props) { const intl = useIntl() @@ -189,6 +195,8 @@ const mapStateToProps = (state: any) => { const urlSearchParams = new URLSearchParams(state.router.location.search) const { homeTimezone, modes } = state.otp.config const { departArrive } = state.otp.currentQuery + const { loggedInUser } = state.user + const defaultEnabledModeButtons = getDefaultModeButtons(state) return { activeSearch: getActiveSearch(state), currentQuery: state.otp.currentQuery, @@ -197,19 +205,19 @@ const mapStateToProps = (state: any) => { enabledModeButtons: decodeQueryParams(modesQueryParamConfig, { modeButtons: urlSearchParams.get('modeButtons') - })?.modeButtons || - modes?.initialState?.enabledModeButtons || - {}, + })?.modeButtons?.filter((mb) => mb !== null) ?? defaultEnabledModeButtons, fillModeIcons: state.otp.config.itinerary?.fillModeIcons, homeTimezone, modeButtonOptions: modes?.modeButtons || [], sort: state.otp.filter.sort, syncSortWithDepartArrive: - state.otp.config?.itinerary?.syncSortWithDepartArrive + state.otp.config?.itinerary?.syncSortWithDepartArrive, + user: loggedInUser } } const mapDispatchToProps = { + applyUserSavedTripDefaults: userAction.applyUserSavedTripDefaults, routingQuery: apiActions.routingQuery, setQueryParam: formActions.setQueryParam, updateItineraryFilter: narrativeActions.updateItineraryFilter diff --git a/lib/util/api.ts b/lib/util/api.ts index ca2586d88..04ea6da28 100644 --- a/lib/util/api.ts +++ b/lib/util/api.ts @@ -5,6 +5,7 @@ import coreUtils from '@opentripplanner/core-utils' import qs from 'qs' import { AppConfig } from './config-types' +import { AppReduxState } from './state-types' const { getUrlParams } = coreUtils.query const { getCurrentDate, getCurrentTime } = coreUtils.time @@ -30,7 +31,7 @@ export const generateModeSettingValues = ( const fromUrl = urlSearchParams.get(setting.key) acc[setting.key] = fromUrl ? convertModeSettingValue(setting, fromUrl) - : initalStateValues?.[setting.key] || setting.default || '' + : initalStateValues?.[setting.key] ?? setting.default ?? '' return acc }, {} @@ -79,3 +80,31 @@ export function getDefaultQuery(config: AppConfig) { time: getCurrentTime() } } + +/** + * Returns default trip settings + * @param state App state + * @param initialState Mode setting values from the config + * @returns Either the user's configured trip defaults, or if none are available, then the defaults from config + */ +export function getDefaultModeSettingValues( + state: AppReduxState +): ModeSettingValues { + const userSavedTripDefaults = state.user?.loggedInUser?.userSavedTripDefaults + const initialState = state.otp.config.modes.initialState?.modeSettingValues + if (userSavedTripDefaults) { + return JSON.parse(userSavedTripDefaults) + } else { + return initialState ?? {} + } +} + +export function getDefaultModeButtons(state: AppReduxState): string[] { + const userSavedTripDefaults = state.user?.loggedInUser?.userSavedTripDefaults + const initialState = state.otp.config.modes.initialState?.enabledModeButtons + if (userSavedTripDefaults) { + return JSON.parse(userSavedTripDefaults).modeButtons?.split('_') + } else { + return initialState ?? [] + } +}