Skip to content

Commit

Permalink
Merge branch 'dev' into allow-multiple-transport-modes
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-heppner-ibigroup authored Oct 18, 2024
2 parents 6c100ce + 98467b5 commit 0108984
Show file tree
Hide file tree
Showing 25 changed files with 541 additions and 209 deletions.
2 changes: 1 addition & 1 deletion a11y/a11y.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ beforeAll(async () => {
})
// Web security is disabled to allow requests to the mock OTP server
browser = await puppeteer.launch({
args: ['--disable-web-security']
args: ['--disable-web-security', '--no-sandbox']
})
})

Expand Down
2 changes: 2 additions & 0 deletions i18n/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,8 @@ components:
oneHour: 1 hour
realtimeAlertFlagged: There is a realtime alert flagged on my journey
timeBefore: "{time} before"
TripPreviewLayout:
previewTrip: Preview Trip
TripStatus:
alerts: "{alerts, plural, one {# alert!} other {# alerts!}}"
deleteTrip: Delete Trip
Expand Down
2 changes: 2 additions & 0 deletions i18n/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,8 @@ components:
oneHour: 1 heure
realtimeAlertFlagged: Une alerte en temps réel affecte mon trajet
timeBefore: "{time} avant"
TripPreviewLayout:
previewTrip: Aperçu du trajet
TripStatus:
alerts: "{alerts, plural, =0 {# alerte !} one {# alerte !} other {# alertes !}}"
deleteTrip: Supprimer le trajet
Expand Down
6 changes: 5 additions & 1 deletion lib/actions/apiV2.js
Original file line number Diff line number Diff line change
Expand Up @@ -1148,7 +1148,11 @@ export function routingQuery(searchId = null, updateSearchInReducer) {
}
}
})
?.map(convertGraphQLResponseToLegacy)
?.map((leg) => ({
...convertGraphQLResponseToLegacy(leg),
route: leg.transitLeg ? leg.route : undefined
})),
otp2QueryParams: query.variables
})
)

Expand Down
27 changes: 26 additions & 1 deletion lib/actions/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import isEqual from 'lodash.isequal'
import qs from 'qs'
import toast from 'react-hot-toast'

import { applyRouteModeOverrides } from '../util/itinerary'
import {
convertToPlace,
getPersistenceMode,
Expand Down Expand Up @@ -129,6 +130,11 @@ export function fetchMonitoredTrips() {
'GET'
)
if (status === 'success') {
const { routeModeOverrides } = getState().otp.config
trips.data.forEach((trip) => {
applyRouteModeOverrides(trip.itinerary, routeModeOverrides)
})

dispatch(setCurrentUserMonitoredTrips(trips.data))
}
}
Expand Down Expand Up @@ -169,14 +175,33 @@ function convertRequestToSearch(config) {
}
}

/**
* Determines whether two GraphQL sets of variables are for the same trip request/search.
*
* Modes are excluded from the comparison because the UI triggers multiple queries
* with the same GraphQL variables but with different combinations of modes.
* Modes exclusion also means that if someone makes the same search with different mode settings
* (e.g. excludes/adds transit modes), that search will also be combined with the previous ones.
*/
function areRequestsSameExceptModes(qp1, qp2) {
const { modes: modes1, ...otherParams1 } = qp1
const { modes: modes2, ...otherParams2 } = qp2
return isEqual(otherParams1, otherParams2)
}

/**
* Removes duplicate requests so that only one request is displayed per "batch".
*/
function removeDuplicateRequests(filtered, tripRequest) {
// Compare one trip request to the next one.
if (filtered.length === 0) {
filtered.push(tripRequest)
} else if (!isEqual(filtered[filtered.length - 1].query, tripRequest.query)) {
} else if (
!areRequestsSameExceptModes(
filtered[filtered.length - 1].query,
tripRequest.query
)
) {
filtered.push(tripRequest)
} else {
filtered[filtered.length - 1].query.modes.push(...tripRequest.query.modes)
Expand Down
129 changes: 20 additions & 109 deletions lib/components/app/print-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,164 +1,75 @@
import { Button } from 'react-bootstrap'
import { connect } from 'react-redux'
import { FormattedMessage, injectIntl, IntlShape } from 'react-intl'
import { Itinerary } from '@opentripplanner/types'
import { Map } from '@styled-icons/fa-solid/Map'
import { Print } from '@styled-icons/fa-solid/Print'
import { Times } from '@styled-icons/fa-solid/Times'
// @ts-expect-error not typescripted yet
import PrintableItinerary from '@opentripplanner/printable-itinerary'
import React, { Component } from 'react'

import * as apiActions from '../../actions/api'
import * as formActions from '../../actions/form'
import {
addPrintViewClassToRootHtml,
clearClassFromRootHtml
} from '../../util/print'
import { ComponentContext } from '../../util/contexts'
import { AppReduxState } from '../../util/state-types'
import { getActiveItinerary, getActiveSearch } from '../../util/state'
import { IconWithText } from '../util/styledIcon'
import { summarizeQuery } from '../form/user-settings-i18n'
import { User } from '../user/types'
import DefaultMap from '../map/default-map'
import PageTitle from '../util/page-title'
import SpanWithSpace from '../util/span-with-space'
import TripDetails from '../narrative/connected-trip-details'

import TripPreviewLayoutBase from './trip-preview-layout-base'

type Props = {
// TODO: Typescript activeSearch type
activeSearch: any
// TODO: Typescript config type
config: any
currentQuery: any
intl: IntlShape
itinerary: Itinerary
location?: { search?: string }
parseUrlQueryString: (params?: any, source?: string) => any
// TODO: Typescript user type
user: any
}

type State = {
mapVisible?: boolean
user: User
}

class PrintLayout extends Component<Props, State> {
static contextType = ComponentContext

constructor(props: Props) {
super(props)
this.state = {
mapVisible: true
}
}

_toggleMap = () => {
this.setState({ mapVisible: !this.state.mapVisible })
}

_print = () => {
window.print()
}

class PrintLayout extends Component<Props> {
_close = () => {
window.location.replace(String(window.location).replace('print/', ''))
}

componentDidMount() {
const { itinerary, location, parseUrlQueryString } = this.props

// Add print-view class to html tag to ensure that iOS scroll fix only applies
// to non-print views.
addPrintViewClassToRootHtml()
// Parse the URL query parameters, if present
if (!itinerary && location && location.search) {
parseUrlQueryString()
}

// TODO: use currentQuery to pan/zoom to the correct part of the map
}

componentWillUnmount() {
clearClassFromRootHtml()
}

render() {
const { activeSearch, config, intl, itinerary, user } = this.props
const { LegIcon } = this.context
const { activeSearch, intl, itinerary, user } = this.props
const printVerb = intl.formatMessage({ id: 'common.forms.print' })

return (
<div className="otp print-layout">
<PageTitle
title={[
printVerb,
activeSearch &&
summarizeQuery(activeSearch.query, intl, user.savedLocations)
]}
/>
{/* The header bar, including the Toggle Map and Print buttons */}
<div className="header">
<div style={{ float: 'right' }}>
<SpanWithSpace margin={0.25}>
<Button
aria-expanded={this.state.mapVisible}
bsSize="small"
onClick={this._toggleMap}
>
<IconWithText Icon={Map}>
<FormattedMessage id="components.PrintLayout.toggleMap" />
</IconWithText>
</Button>
</SpanWithSpace>
<SpanWithSpace margin={0.25}>
<Button bsSize="small" onClick={this._print}>
<IconWithText Icon={Print}>{printVerb}</IconWithText>
</Button>
</SpanWithSpace>
<Button bsSize="small" onClick={this._close} role="link">
<IconWithText Icon={Times}>
<FormattedMessage id="common.forms.close" />
</IconWithText>
</Button>
</div>
<FormattedMessage id="components.PrintLayout.itinerary" />
</div>

{/* The map, if visible */}
{this.state.mapVisible && (
<TripPreviewLayoutBase
header={<FormattedMessage id="components.PrintLayout.itinerary" />}
itinerary={itinerary}
mapElement={
<div className="map-container">
{/* FIXME: Improve reframing/setting map bounds when itinerary is received. */}
<DefaultMap />
</div>
)}

{/* The main itinerary body */}
{itinerary && (
<>
<PrintableItinerary
config={config}
itinerary={itinerary}
LegIcon={LegIcon}
/>
<TripDetails className="percy-hide" itinerary={itinerary} />
</>
)}
</div>
}
onClose={this._close}
subTitle={
activeSearch &&
summarizeQuery(activeSearch.query, intl, user.savedLocations)
}
title={printVerb}
/>
)
}
}

// connect to the redux store

// TODO: Typescript state
const mapStateToProps = (state: any) => {
const mapStateToProps = (state: AppReduxState) => {
const activeSearch = getActiveSearch(state)
const { localUser, loggedInUser } = state.user
const user = loggedInUser || localUser
return {
activeSearch,
config: state.otp.config,
currentQuery: state.otp.currentQuery,
itinerary: getActiveItinerary(state) as Itinerary,
user
}
Expand Down
3 changes: 2 additions & 1 deletion lib/components/app/responsive-webapp.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import BeforeSignInScreen from '../user/before-signin-screen'
import Map from '../map/map'
import MobileMain from '../mobile/main'
import printRoutes from '../../util/webapp-print-routes'
import tripPreviewRoutes from '../../util/webapp-trip-preview-routes'
import webAppRoutes from '../../util/webapp-routes'
import withLoggedInUserSupport from '../user/with-logged-in-user-support'
import withMap from '../map/with-map'
Expand All @@ -43,7 +44,7 @@ import SessionTimeout from './session-timeout'

const { isMobile } = coreUtils.ui

const routes = [...webAppRoutes, ...printRoutes]
const routes = [...webAppRoutes, ...printRoutes, ...tripPreviewRoutes]

class ResponsiveWebapp extends Component {
static propTypes = {
Expand Down
Loading

0 comments on commit 0108984

Please sign in to comment.