diff --git a/lib/actions/api.js b/lib/actions/api.js index dafc7cc42..7dafff4d4 100644 --- a/lib/actions/api.js +++ b/lib/actions/api.js @@ -12,7 +12,6 @@ import { getSecureFetchOptions } from '../util/middleware' import { getStopViewerConfig } from '../util/state' import { MainPanelContent } from './ui-constants' -import { setVisibleItinerary } from './narrative' import v1Actions from './apiV1' import v2Actions from './apiV2' @@ -139,9 +138,6 @@ export function updateOtpUrlParams(state, searchId) { params.ui_activeItinerary = -1 // At the same time, reset/delete the ui_itineraryView param. params.ui_itineraryView = undefined - if (config.itinerary?.showFirstResultByDefault) { - dispatch(setVisibleItinerary({ index: 0 })) - } // Merge in the provided OTP params and update the URL. dispatch(setUrlSearch(Object.assign(params, otpParams))) } diff --git a/lib/components/narrative/narrative-itineraries.js b/lib/components/narrative/narrative-itineraries.js index 6d20a2437..80eec5c36 100644 --- a/lib/components/narrative/narrative-itineraries.js +++ b/lib/components/narrative/narrative-itineraries.js @@ -331,13 +331,16 @@ class NarrativeItineraries extends Component { ) } - componentDidUpdate() { + componentDidUpdate(prevProps) { // If set in URL, set the active itinerary in the state, once. const { activeItinerary, activeSearch, + itineraryConfig, + mergedItineraries, setActiveItinerary, - setVisibleItinerary + setVisibleItinerary, + visibleItinerary } = this.props const { ui_activeItinerary: uiActiveItinerary } = coreUtils.query.getUrlParams() || {} @@ -350,6 +353,28 @@ class NarrativeItineraries extends Component { setActiveItinerary({ index: +uiActiveItinerary }) setVisibleItinerary({ index: +uiActiveItinerary }) } + + /** + * Show first result by default is a lot more complicated now that we have + * fixed indeces. We must update the visible itinerary here instead of in the redux state. + * Also, we need to update whenever new items arrive, to make sure that the highlighted + * itinerary is indeed the first one. + * + * Finally, we need to make sure we only update if the data changes, not if the user actually + * highlighted something else on their own. + */ + if (itineraryConfig?.showFirstResultByDefault) { + const firstSortedItin = + mergedItineraries?.length > 0 && mergedItineraries?.[0].index + + if ( + activeItinerary === -1 && + (visibleItinerary === null || visibleItinerary === false) && + prevProps.mergedItineraries.length !== mergedItineraries.length + ) { + setVisibleItinerary({ index: firstSortedItin }) + } + } } // eslint-disable-next-line complexity @@ -531,7 +556,7 @@ const reduceErrorsFromResponse = (acc, cur) => { const mapStateToProps = (state) => { const { config, filter } = state.otp - const { co2, errorMessages, modes } = config + const { co2, errorMessages, itinerary, modes } = config const { sort } = filter const activeSearch = getActiveSearch(state) @@ -590,6 +615,7 @@ const mapStateToProps = (state) => { groupItineraries, groupTransitModes, itineraries: allItineraries, + itineraryConfig: itinerary, itineraryIsExpanded, // use a key so that the NarrativeItineraries component and its state is // reset each time a new search is shown