diff --git a/__tests__/components/viewers/__snapshots__/nearby-view.js.snap b/__tests__/components/viewers/__snapshots__/nearby-view.js.snap index 41b77e69d..ac5bc3bee 100644 --- a/__tests__/components/viewers/__snapshots__/nearby-view.js.snap +++ b/__tests__/components/viewers/__snapshots__/nearby-view.js.snap @@ -4232,7 +4232,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` >

viewers > nearby view renders proper scooter dates 1`] = ` >

viewers > nearby view renders proper scooter dates 1`] = ` >

viewers > nearby view renders proper scooter dates 1`] = ` >

+ Roosevelt Station - Bay 2 @@ -11664,7 +12231,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

  • viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

      viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

    1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

        viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

      1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

          viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

          + Roosevelt @@ -18071,7 +19004,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

        1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

            viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

          1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

              viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

            1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                viewers > nearby view renders proper scooter dates 1`] = ` >

                viewers > nearby view renders proper scooter dates 1`] = ` >

                viewers > nearby view renders proper scooter dates 1`] = ` >

                viewers > nearby view renders proper scooter dates 1`] = ` >

                + Roosevelt Station - Bay 1 @@ -28233,7 +29810,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

              1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                  viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                    viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                  1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                      viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                    1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                        viewers > nearby view renders proper scooter dates 1`] = ` >

                        viewers > nearby view renders proper scooter dates 1`] = ` >

                        viewers > nearby view renders proper scooter dates 1`] = ` >

                        viewers > nearby view renders proper scooter dates 1`] = ` >

                        viewers > nearby view renders proper scooter dates 1`] = ` >

                        viewers > nearby view renders proper scooter dates 1`] = ` >

                        + Roosevelt Station Bay 5 - Bay 5 @@ -37200,7 +39143,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                      1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                          viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                        1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                            viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                          1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                              viewers > nearby view renders proper scooter dates 1`] = ` >

                              viewers > nearby view renders proper scooter dates 1`] = ` >

                              + Roosevelt @@ -45564,7 +48048,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                            1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                + Roosevelt Station - Bay 3 @@ -53175,7 +56303,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                              1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                  viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                    viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                  1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                      viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                    1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                        viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                      1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                          viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                        1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                            viewers > nearby view renders proper scooter dates 1`] = ` >

                                            viewers > nearby view renders proper scooter dates 1`] = ` >

                                            + NE 65th St & 14th Ave NE @@ -64307,7 +68002,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                          1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                              viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                            1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                                viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                              1. viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                                  viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                                { export const findStopTimesForStop = (params) => function (dispatch, getState) { dispatch(fetchingStopTimesForStop(params)) - const { date, stopId } = params + const { date, onlyRequestForOperators, stopId } = params const timeZone = getState().otp.config.homeTimezone // Create a service date timestamp from 3:30am local. const serviceDay = getServiceStart(date, timeZone).getTime() / 1000 - return dispatch( - createGraphQLQueryAction( - `query StopTimes( + const fullStopTimesQuery = `query StopTimes( $serviceDay: Long! $stopId: String! ) { @@ -567,7 +565,48 @@ export const findStopTimesForStop = (params) => } } } - }`, + }` + + const shorterStopTimesQueryForOperators = `query StopTimes( + $stopId: String! + ) { + stop(id: $stopId) { + gtfsId + code + routes { + id: gtfsId + agency { + gtfsId + name + } + patterns { + id + headsign + } + } + stoptimesForPatterns(numberOfDepartures: 100, omitNonPickups: true, omitCanceled: false) { + pattern { + desc: name + headsign + id: code + route { + agency { + gtfsId + } + gtfsId + } + } + } + } + }` + + const query = onlyRequestForOperators + ? shorterStopTimesQueryForOperators + : fullStopTimesQuery + + return dispatch( + createGraphQLQueryAction( + query, { serviceDay, stopId diff --git a/lib/components/map/default-map.tsx b/lib/components/map/default-map.tsx index 38d7570e4..b2e7b899f 100644 --- a/lib/components/map/default-map.tsx +++ b/lib/components/map/default-map.tsx @@ -3,6 +3,7 @@ // @ts-nocheck import { connect } from 'react-redux' import { GeolocateControl, NavigationControl } from 'react-map-gl' +import { getCurrentDate } from '@opentripplanner/core-utils/lib/time' import { injectIntl } from 'react-intl' import BaseMap from '@opentripplanner/base-map' import generateOTP2TileLayers from '@opentripplanner/otp2-tile-overlay' @@ -13,6 +14,7 @@ import { assembleBasePath, bikeRentalQuery, carRentalQuery, + findStopTimesForStop, vehicleRentalQuery } from '../../actions/api' import { ComponentContext } from '../../util/contexts' @@ -22,6 +24,7 @@ import { MainPanelContent } from '../../actions/ui-constants' import { setLocation, setMapPopupLocationAndGeocode } from '../../actions/map' import { setViewedStop } from '../../actions/ui' import { updateOverlayVisibility } from '../../actions/config' +import TransitOperatorIcons from '../util/connected-transit-operator-icons' import ElevationPointMarker from './elevation-point-marker' import EndpointsOverlay from './connected-endpoints-overlay' @@ -153,6 +156,17 @@ class DefaultMap extends Component { } } + // Generate operator logos to pass through OTP tile layer to map-popup + getEntityPrefix = (entity) => { + const stopId = entity.gtfsId + this.props.findStopTimesForStop({ + date: getCurrentDate(), + onlyRequestForOperators: true, + stopId + }) + return + } + /** * Checks whether the modes have changed between old and new queries and * whether to update the map overlays accordingly (e.g., to show rental vehicle @@ -407,7 +421,8 @@ class DefaultMap extends Component { setLocation, setViewedStop, viewedRouteStops, - config.companies + config.companies, + this.getEntityPrefix ) default: return null @@ -468,6 +483,7 @@ const mapStateToProps = (state) => { const mapDispatchToProps = { bikeRentalQuery, carRentalQuery, + findStopTimesForStop, getCurrentPosition, setLocation, setMapPopupLocationAndGeocode, diff --git a/lib/components/util/connected-transit-operator-icons.tsx b/lib/components/util/connected-transit-operator-icons.tsx new file mode 100644 index 000000000..0475e54e4 --- /dev/null +++ b/lib/components/util/connected-transit-operator-icons.tsx @@ -0,0 +1,38 @@ +import { connect } from 'react-redux' +import { TransitOperator } from '@opentripplanner/types' +import React from 'react' + +import { AppReduxState } from '../../util/state-types' +import { FETCH_STATUS } from '../../util/constants' + +import { StopData } from './types' +import TransitOperatorLogos from './transit-operator-icons' + +interface Props { + stopData?: StopData + transitOperators: TransitOperator[] +} + +function TransitOperatorIcons({ stopData, transitOperators }: Props) { + const loading = stopData?.fetchStatus === FETCH_STATUS.FETCHING + return ( + + ) +} + +const mapStateToProps = ( + state: AppReduxState, + ownProps: Props & { stopId: string } +) => { + const stops = state.otp.transitIndex.stops + return { + stopData: stops?.[ownProps.stopId], + transitOperators: state.otp.config.transitOperators || [] + } +} + +export default connect(mapStateToProps)(TransitOperatorIcons) diff --git a/lib/components/util/operator-logo.tsx b/lib/components/util/operator-logo.tsx index 420e37ce7..f88f6d51a 100644 --- a/lib/components/util/operator-logo.tsx +++ b/lib/components/util/operator-logo.tsx @@ -2,20 +2,32 @@ import { TransitOperator } from '@opentripplanner/types' import React from 'react' import styled from 'styled-components' -const OperatorImg = styled.img` +const OperatorImg = styled.img<{ marginRight?: number; maxHeight?: number }>` &:not(:last-of-type) { margin-right: 0.5ch; } width: 25px; ` +const StyledOperatorImg = styled(OperatorImg)` + margin-right: 0.5ch; + max-height: 1em; + // Make sure icons stay square + max-width: 1em; +` + type Props = { alt?: string operator?: TransitOperator + styled?: boolean } -const OperatorLogo = ({ alt, operator }: Props): JSX.Element | null => { +const OperatorLogo = ({ alt, operator, styled }: Props): JSX.Element | null => { if (!operator?.logo) return null + if (styled) { + return + } + return } diff --git a/lib/components/util/transit-operator-icons.tsx b/lib/components/util/transit-operator-icons.tsx new file mode 100644 index 000000000..7f40fe11c --- /dev/null +++ b/lib/components/util/transit-operator-icons.tsx @@ -0,0 +1,83 @@ +import { MapPin } from '@styled-icons/fa-solid' +import { useIntl } from 'react-intl' +import React from 'react' +import Skeleton from 'react-loading-skeleton' +import type { TransitOperator } from '@opentripplanner/types' + +import InvisibleA11yLabel from './invisible-a11y-label' +import OperatorLogo from './operator-logo' +import type { StopData } from './types' + +const Operator = ({ operator }: { operator?: TransitOperator }) => { + const intl = useIntl() + + if (!operator) { + return null + } else { + const operatorLogoAriaLabel = intl.formatMessage( + { + id: 'components.StopViewer.operatorLogoAriaLabel' + }, + { + operatorName: operator.name + } + ) + return operator.logo ? ( + // Span with agency classname allows optional contrast/customization in user + // config for logos with poor contrast. Class name is hyphenated agency name + // e.g. "sound-transit" + + + + ) : ( + // If operator exists but logo is missing, + // we still need to announce the operator name to screen readers. + <> + + {operatorLogoAriaLabel} + + ) + } +} + +const TransitOperatorLogos = ({ + loading = false, + stopData, + transitOperators +}: { + loading?: boolean + stopData?: StopData + transitOperators?: TransitOperator[] +}): JSX.Element => { + const agencies = + (stopData && + stopData.stoptimesForPatterns?.reduce>((prev, cur) => { + // @ts-expect-error The agency type is not yet compatible with OTP2 + const agencyGtfsId = cur.pattern.route.agency?.gtfsId + return agencyGtfsId ? prev.add(agencyGtfsId) : prev + }, new Set())) || + new Set() + + return ( + <> + {loading ? ( + + ) : ( + transitOperators + ?.filter((to) => Array.from(agencies).includes(to.agencyId)) + // Second pass to remove duplicates based on name + .filter( + (to, index, arr) => + index === arr.findIndex((t) => t?.name === to?.name) + ) + .map((to) => ) + )} + + ) +} + +export default TransitOperatorLogos diff --git a/lib/components/viewers/nearby/stop-card-header.tsx b/lib/components/viewers/nearby/stop-card-header.tsx index 045616e60..d161ba7d6 100644 --- a/lib/components/viewers/nearby/stop-card-header.tsx +++ b/lib/components/viewers/nearby/stop-card-header.tsx @@ -1,6 +1,5 @@ import { connect } from 'react-redux' import { FormattedMessage, useIntl } from 'react-intl' -import { MapPin } from '@styled-icons/fa-solid' import { Search } from '@styled-icons/fa-solid/Search' import { TransitOperator } from '@opentripplanner/types' import React, { ComponentType } from 'react' @@ -10,8 +9,8 @@ import { Icon, IconWithText } from '../../util/styledIcon' import { StopData } from '../../util/types' import InvisibleA11yLabel from '../../util/invisible-a11y-label' import Link from '../../util/link' -import OperatorLogo from '../../util/operator-logo' import Strong from '../../util/strong-text' +import TransitOperatorLogos from '../../util/transit-operator-icons' import { CardBody, CardHeader, CardTitle } from './styled' import DistanceDisplay from './distance-display' @@ -28,41 +27,6 @@ type Props = { transitOperators?: TransitOperator[] } -const Operator = ({ operator }: { operator?: TransitOperator }) => { - const intl = useIntl() - if (!operator) { - return null - } else { - const operatorLogoAriaLabel = intl.formatMessage( - { - id: 'components.StopViewer.operatorLogoAriaLabel' - }, - { - operatorName: operator.name - } - ) - return operator.logo ? ( - // Span with agency classname allows optional contrast/customization in user - // config for logos with poor contrast. Class name is hyphenated agency name - // e.g. "sound-transit" - - - - ) : ( - // If operator exists but logo is missing, - // we still need to announce the operator name to screen readers. - <> - - {operatorLogoAriaLabel} - - ) - } -} - const StopCardHeader = ({ actionIcon, actionParams, @@ -75,12 +39,7 @@ const StopCardHeader = ({ transitOperators }: Props): JSX.Element => { const intl = useIntl() - const agencies = - stopData.stoptimesForPatterns?.reduce>((prev, cur) => { - // @ts-expect-error The agency type is not yet compatible with OTP2 - const agencyGtfsId = cur.pattern.route.agency?.gtfsId - return agencyGtfsId ? prev.add(agencyGtfsId) : prev - }, new Set()) || new Set() + const zoomButtonText = onZoomClick ? intl.formatMessage({ id: 'components.StopViewer.zoomToStop' @@ -92,16 +51,10 @@ const StopCardHeader = ({ {/* @ts-expect-error The 'as' prop in styled-components is not listed for TypeScript. */} - {transitOperators - ?.filter((to) => Array.from(agencies).includes(to.agencyId)) - // Second pass to remove duplicates based on name - .filter( - (to, index, arr) => - index === arr.findIndex((t) => t?.name === to?.name) - ) - .map((to) => ( - - ))} + {stopData.name} diff --git a/lib/components/viewers/nearby/styled.tsx b/lib/components/viewers/nearby/styled.tsx index 1ffd1dc76..d088ebbfe 100644 --- a/lib/components/viewers/nearby/styled.tsx +++ b/lib/components/viewers/nearby/styled.tsx @@ -48,7 +48,6 @@ export const CardTitle = styled.p` display: flex; font-size: 22px; font-weight: 600; - gap: 0.5ch; grid-column: 1; margin: 0; /* Prevent svg and images to be taller than the text. */