Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync itinerary sort with depart arrive #1266

Merged
merged 12 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions example-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ persistence:
# iconUrl: ''
# href: ''

### These settings are only used for the field trip features.
dateTime:
timeFormat: h:mm a
dateFormat: MM/dd/yyyy
daniel-heppner-ibigroup marked this conversation as resolved.
Show resolved Hide resolved

map:
initLat: 45.52
initLon: -122.682
Expand Down Expand Up @@ -411,6 +416,8 @@ itinerary:
displayA11yError: false
# Whether to display itinerary info in the side of the preview or next to the departure times
showInlineItinerarySummary: false
# Whether to sync the sort type with the depart/arrive time in the date/time modal
syncSortWithDepartArrive: true
# The sort option to use by default
# Available sort options: 'BEST', 'DURATION', 'ARRIVALTIME', 'WALKTIME', 'COST', 'DEPARTURETIME'
# defaultSort: "BEST" # Default
Expand Down
76 changes: 0 additions & 76 deletions lib/components/form/date-time-modal.js

This file was deleted.

116 changes: 116 additions & 0 deletions lib/components/form/date-time-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { connect } from 'react-redux'
import coreUtils from '@opentripplanner/core-utils'
import React, { useCallback } from 'react'

import * as formActions from '../../actions/form'
import * as narrativeActions from '../../actions/narrative'
import { AppConfig } from '../../util/config-types'
import { AppReduxState, FilterType, SortType } from '../../util/state-types'

import { StyledDateTimeSelector } from './styled'

type Props = {
config: AppConfig
date: string
dateFormatLegacy?: string
departArrive: DepartArriveValue
setQueryParam: (params: any) => void
sort: SortType
time: string
timeFormatLegacy?: string
updateItineraryFilter: (payload: FilterType) => void
}

type DepartArriveValue = 'NOW' | 'DEPART' | 'ARRIVE'

const DepartArriveTypeMap: Record<
DepartArriveValue,
FilterType['sort']['type']
> = {
ARRIVE: 'ARRIVALTIME',
DEPART: 'DEPARTURETIME',
NOW: 'DURATION'
}

function DateTimeModal({
config,
date,
dateFormatLegacy,
departArrive,
setQueryParam,
sort,
time,
timeFormatLegacy,
updateItineraryFilter
}: Props) {
const { homeTimezone, isTouchScreenOnDesktop } = config
const touchClassName = isTouchScreenOnDesktop
? 'with-desktop-touchscreen'
: ''

const syncSortWithDepartArrive = config?.itinerary?.syncSortWithDepartArrive
// Note the side effect that this will resort the results of a previous query
// if the user changes the depart/arrive setting before the query is run.
const setQueryParamMiddleware = useCallback(
(params: any) => {
if (syncSortWithDepartArrive) {
updateItineraryFilter({
daniel-heppner-ibigroup marked this conversation as resolved.
Show resolved Hide resolved
sort: {
...sort,
type: DepartArriveTypeMap[params.departArrive as DepartArriveValue]
}
})
}
setQueryParam(params)
},
[setQueryParam, updateItineraryFilter, sort, syncSortWithDepartArrive]
)
return (
<div className="date-time-modal">
<div className="main-panel">
<StyledDateTimeSelector
className={`date-time-selector ${touchClassName}`}
date={date}
dateFormatLegacy={dateFormatLegacy}
departArrive={departArrive}
onQueryParamChange={setQueryParamMiddleware}
time={time}
// These props below are for legacy browsers
// that don't support `<input type="time|date">`.
// These props are not relevant in modern browsers,
// where `<input type="time|date">` already
// formats the time|date according to the OS settings.
// eslint-disable-next-line react/jsx-sort-props
timeFormatLegacy={timeFormatLegacy}
timeZone={homeTimezone}
/>
</div>
</div>
)
}

const mapStateToProps = (state: AppReduxState) => {
const { date, departArrive, time } = state.otp.currentQuery
const config = state.otp.config
const { sort } = state.otp.filter
return {
config,
date,
// This prop is for legacy browsers (see render method above).
// @ts-expect-error Mismatched config types
dateFormatLegacy: coreUtils.time.getDateFormat(config),
departArrive,
sort,
time,
// This prop is for legacy browsers (see render method above).
// @ts-expect-error Mismatched config types
timeFormatLegacy: coreUtils.time.getTimeFormat(config)
}
}

const mapDispatchToProps = {
setQueryParam: formActions.setQueryParam,
updateItineraryFilter: narrativeActions.updateItineraryFilter
}

export default connect(mapStateToProps, mapDispatchToProps)(DateTimeModal)
10 changes: 1 addition & 9 deletions lib/components/narrative/narrative-itineraries-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export default function NarrativeItinerariesHeader({
enabledSortModes,
errors,
itineraries,
itinerary,
itineraryIsExpanded,
onSortChange,
onSortDirChange,
Expand Down Expand Up @@ -109,13 +108,6 @@ export default function NarrativeItinerariesHeader({
const sortOptionsArr = sortOptions(intl, enabledSortModes)
const sortText = sortOptionsArr.find((x) => x.value === sort.type)?.text

const handleSortClick = useCallback(
(value) => {
onSortChange(value)
},
[onSortChange]
)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this useCallback is probably hurting performance more than anything. onSortChange won't be recreated on renders anyway since it's being passed in

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love!

return (
<div
className="options header"
Expand Down Expand Up @@ -222,7 +214,7 @@ export default function NarrativeItinerariesHeader({
<li className="sort-option" key={sortOption.value}>
<UnstyledButton
aria-selected={sortText === sortOption.text || undefined}
onClick={() => handleSortClick(sortOption.value)}
onClick={() => onSortChange(sortOption.value)}
role="option"
>
{sortOption.text}
Expand Down
7 changes: 7 additions & 0 deletions lib/util/config-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ export interface ItineraryConfig {
showPlanFirstLastButtons?: boolean
showRouteFares?: boolean
sortModes?: ItinerarySortOption[]
syncSortWithDepartArrive?: boolean
weights?: ItineraryCostWeights
}

Expand Down Expand Up @@ -362,6 +363,11 @@ export interface StopScheduleViewerConfig {
showBlockIds?: boolean
}

export interface DateTimeConfig {
dateFormat: string
timeFormat: string
}

/** The main application configuration object */
export interface AppConfig {
accessibilityScore?: AccessibilityScoreConfig
Expand All @@ -375,6 +381,7 @@ export interface AppConfig {
bugsnag?: BugsnagConfig
co2?: CO2Config
companies?: Company[]
dateTime?: DateTimeConfig
elevationProfile?: boolean
extraMenuItems?: AppMenuItemConfig[]
geocoder: GeocoderConfig
Expand Down
15 changes: 10 additions & 5 deletions lib/util/state-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ export interface OtpState {
activeSearchId?: string
config: AppConfig
currentQuery: any
filter: {
sort: {
type: string
}
}
filter: FilterType
location: any
modeSettingDefinitions: ModeSetting[]
overlay: any
Expand All @@ -31,6 +27,15 @@ export interface OtpState {
ui: any // TODO
}

export interface SortType {
direction: string
type: string
}

export interface FilterType {
sort: SortType
}

export interface UserState {
itineraryExistence?: ItineraryExistence
localUser?: any
Expand Down
Loading