Skip to content

Commit

Permalink
Merge pull request #100 from opentripplanner/print-view-text-tweaks
Browse files Browse the repository at this point in the history
Fix up print view text for various rental modes
  • Loading branch information
landonreed authored Aug 15, 2019
2 parents 5b4294d + 3fa82f8 commit 0a5e04c
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 35 deletions.
13 changes: 10 additions & 3 deletions lib/components/app/print-layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class PrintLayout extends Component {
}

render () {
const { itinerary, companies, timeFormat } = this.props
const { configCompanies, customIcons, itinerary, timeFormat } = this.props
return (
<div className='otp print-layout'>
{/* The header bar, including the Toggle Map and Print buttons */}
Expand Down Expand Up @@ -87,7 +87,14 @@ class PrintLayout extends Component {
}

{/* The main itinerary body */}
{itinerary && <PrintableItinerary itinerary={itinerary} companies={companies} timeFormat={timeFormat} />}
{itinerary
? <PrintableItinerary
configCompanies={configCompanies}
customIcons={customIcons}
itinerary={itinerary}
timeFormat={timeFormat} />
: null
}
</div>
)
}
Expand All @@ -98,7 +105,7 @@ class PrintLayout extends Component {
const mapStateToProps = (state, ownProps) => {
return {
itinerary: getActiveItinerary(state.otp),
companies: state.otp.currentQuery.companies,
configCompanies: state.otp.config.companies,
timeFormat: getTimeFormat(state.otp.config)
}
}
Expand Down
8 changes: 7 additions & 1 deletion lib/components/app/responsive-webapp.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,13 @@ class RouterWrapper extends Component {
/>
<Route
path='/print'
component={PrintLayout}
component={(routerProps) => {
// combine the router props with the other props that get
// passed to the exported component. This way it's possible for
// the PrintLayout component to receive the custom icons prop.
const props = {...this.props, ...routerProps}
return <PrintLayout {...props} />
}}
/>
{/* For any other route, simply return the web app. */}
<Route
Expand Down
2 changes: 1 addition & 1 deletion lib/components/narrative/line-itin/access-leg-body.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class AccessLegSummary extends Component {
<div>
{getLegModeLabel(leg)}
{' '}
{leg.distance && <span> {distanceString(leg.distance)}</span>}
{leg.distance > 0 && <span> {distanceString(leg.distance)}</span>}
{` to ${getPlaceName(leg.to, config.companies)}`}
</div>
</div>
Expand Down
12 changes: 7 additions & 5 deletions lib/components/narrative/line-itin/place-row.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { connect } from 'react-redux'
import LocationIcon from '../../icons/location-icon'
import ViewStopButton from '../../viewers/view-stop-button'
import {
getCompanyForNetwork,
getCompaniesLabelFromNetworks,
getModeForPlace,
getPlaceName
} from '../../../util/itinerary'
Expand Down Expand Up @@ -187,9 +187,11 @@ class RentedVehicleLeg extends PureComponent {
// resumes there won't be any network info. In that case we simply return
// that the rental is continuing.
if (leg.from.networks) {
const companies = leg.from.networks.map(n => getCompanyForNetwork(n, configCompanies))
const companyLabel = companies.map(co => co.label).join('/')
rentalDescription += ` ${companyLabel}`
const companiesLabel = getCompaniesLabelFromNetworks(
leg.from.networks,
configCompanies
)
rentalDescription += ` ${companiesLabel}`
// Only show vehicle name for car rentals. For bikes and eScooters, these
// IDs/names tend to be less relevant (or entirely useless) in this context.
if (leg.rentedCar && leg.from.name) {
Expand All @@ -200,7 +202,7 @@ class RentedVehicleLeg extends PureComponent {
rentalDescription = 'Continue using rental'
}

rentalDescription += ` ${modeString}${vehicleName}`
rentalDescription += ` ${modeString} ${vehicleName}`
}
// e.g., Pick up REACHNOW rented car XYZNDB OR
// Pick up SPIN eScooter
Expand Down
124 changes: 101 additions & 23 deletions lib/components/narrative/printable/printable-itinerary.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'

import ModeIcon from '../../icons/mode-icon'
import TripDetails from '../trip-details'
import { distanceString } from '../../../util/distance'
import { formatTime, formatDuration } from '../../../util/time'
import { getLegModeLabel, getStepDirection, getStepStreetName, getTimeZoneOffset } from '../../../util/itinerary'
import {
getCompaniesLabelFromNetworks,
getLegIcon,
getLegModeLabel,
getPlaceName,
getStepDirection,
getStepStreetName,
getTimeZoneOffset
} from '../../../util/itinerary'

export default class PrintableItinerary extends Component {
static propTypes = {
itinerary: PropTypes.object
}

render () {
const { itinerary, timeFormat } = this.props
const {
configCompanies,
customIcons,
itinerary,
timeFormat
} = this.props

const timeOptions = {
format: timeFormat,
Expand All @@ -31,10 +44,26 @@ export default class PrintableItinerary extends Component {
</div>
)}
{itinerary.legs.map((leg, k) => leg.transitLeg
? <TransitLeg key={k} leg={leg} interlineFollows={k < itinerary.legs.length - 1 && itinerary.legs[k + 1].interlineWithPreviousLeg} timeOptions={timeOptions} />
? <TransitLeg
key={k}
customIcons={customIcons}
interlineFollows={k < itinerary.legs.length - 1 &&
itinerary.legs[k + 1].interlineWithPreviousLeg
}
leg={leg}
timeOptions={timeOptions} />
: leg.hailedCar
? <TNCLeg leg={leg} timeOptions={timeOptions} />
: <AccessLeg key={k} leg={leg} timeOptions={timeOptions} />
? <TNCLeg
customIcons={customIcons}
leg={leg}
timeOptions={timeOptions} />
: <AccessLeg
key={k}
configCompanies={configCompanies}
customIcons={customIcons}
leg={leg}
timeOptions={timeOptions}
/>
)}
<TripDetails itinerary={itinerary} />
</div>
Expand All @@ -48,18 +77,23 @@ class TransitLeg extends Component {
}

render () {
const { leg, interlineFollows, timeOptions } = this.props
const { customIcons, leg, interlineFollows, timeOptions } = this.props

// Handle case of transit leg interlined w/ previous
if (leg.interlineWithPreviousLeg) {
return (
<div className='leg collapse-top'>
<div className='leg-body'>
<div className='leg-header'>
Continues as <b>{leg.routeShortName} {leg.routeLongName}</b> to <b>{leg.to.name}</b>
Continues as{' '}
<b>{leg.routeShortName} {leg.routeLongName}</b>{' '}
to <b>{leg.to.name}</b>
</div>
<div className='leg-details'>
<div className='leg-detail'>Get off at <b>{leg.to.name}</b> at {formatTime(leg.endTime, timeOptions)}</div>
<div className='leg-detail'>
Get off at <b>{leg.to.name}</b>{' '}
at {formatTime(leg.endTime, timeOptions)}
</div>
</div>
</div>
</div>
Expand All @@ -68,17 +102,25 @@ class TransitLeg extends Component {

return (
<div className='leg'>
<div className='mode-icon'><ModeIcon mode={leg.mode} /></div>
<div className='mode-icon'>{getLegIcon(leg, customIcons)}</div>
<div className='leg-body'>
<div className='leg-header'>
<b>{leg.routeShortName} {leg.routeLongName}</b> to <b>{leg.to.name}</b>
</div>
<div className='leg-details'>
<div className='leg-detail'>Board at <b>{leg.from.name}</b> at {formatTime(leg.startTime, timeOptions)}</div>
{interlineFollows
? <div className='leg-detail'>Stay on board at <b>{leg.to.name}</b></div>
: <div className='leg-detail'>Get off at <b>{leg.to.name}</b> at {formatTime(leg.endTime, timeOptions)}</div>
}
<div className='leg-detail'>
Board at <b>{leg.from.name}</b>{' '}
at {formatTime(leg.startTime, timeOptions)}
</div>
<div className='leg-detail'>
{interlineFollows
? <span>Stay on board at <b>{leg.to.name}</b></span>
: <span>
Get off at <b>{leg.to.name}</b>{' '}
at {formatTime(leg.endTime, timeOptions)}
</span>
}
</div>
</div>
</div>
</div>
Expand All @@ -92,19 +134,49 @@ class AccessLeg extends Component {
}

render () {
const { leg } = this.props
const { configCompanies, customIcons, leg } = this.props

// calculate leg mode label in a special way for this component
let legModeLabel = getLegModeLabel(leg)

if (leg.rentedBike) {
// FIXME: Special case for TriMet that needs to be refactored to
// incorporate actual company.
legModeLabel = 'Ride BIKETOWN bike'
} else if (leg.rentedCar) {
// Add extra information to printview that would otherwise clutter up
// other places that use the getLegModeLabel function
const companiesLabel = getCompaniesLabelFromNetworks(
leg.from.networks,
configCompanies
)
legModeLabel = `Drive ${companiesLabel} ${leg.from.name}`
} else if (leg.rentedVehicle) {
const companiesLabel = getCompaniesLabelFromNetworks(
leg.from.networks,
configCompanies
)
legModeLabel = `Ride ${companiesLabel} eScooter`
}

return (
<div className='leg'>
<div className='mode-icon'><ModeIcon mode={leg.mode} /></div>
<div className='mode-icon'>{getLegIcon(leg, customIcons)}</div>
<div className='leg-body'>
<div className='leg-header'>
<b>{getLegModeLabel(leg)}</b> to <b>{leg.to.name}</b>
<b>{legModeLabel}</b>{' '}
{!leg.hailedCar &&
leg.distance > 0 &&
<span> {distanceString(leg.distance)} </span>}
to <b>{getPlaceName(leg.to, configCompanies)}</b>
</div>
{!leg.hailedCar && (
<div className='leg-details'>
{leg.steps.map((step, k) => {
return (
<div key={k} className='leg-detail'>{getStepDirection(step)} on <b>{getStepStreetName(step)}</b></div>
<div key={k} className='leg-detail'>
{getStepDirection(step)} on <b>{getStepStreetName(step)}</b>
</div>
)
})}
</div>
Expand All @@ -121,20 +193,26 @@ class TNCLeg extends Component {
}

render () {
const { leg } = this.props
const { customIcons, leg } = this.props
const { tncData } = leg
if (!tncData) return null

return (
<div className='leg'>
<div className='mode-icon'><ModeIcon mode={leg.mode} /></div>
<div className='mode-icon'>{getLegIcon(leg, customIcons)}</div>
<div className='leg-body'>
<div className='leg-header'>
<b>Take {tncData.displayName}</b> to <b>{leg.to.name}</b>
</div>
<div className='leg-details'>
<div className='leg-detail'>Estimated wait time for pickup: <b>{formatDuration(tncData.estimatedArrival)}</b></div>
<div className='leg-detail'>Estimated travel time: <b>{formatDuration(leg.duration)}</b> (does not account for traffic)</div>
<div className='leg-detail'>
Estimated wait time for pickup:{' '}
<b>{formatDuration(tncData.estimatedArrival)}</b>
</div>
<div className='leg-detail'>
Estimated travel time:{' '}
<b>{formatDuration(leg.duration)}</b> (does not account for traffic)
</div>
</div>
</div>
</div>
Expand Down
21 changes: 19 additions & 2 deletions lib/util/itinerary.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ export function toSentenceCase (str) {
*/
export function getLegIcon (leg, customIcons) {
// check if a custom function exists for determining the icon for a leg
if (typeof customIcons.customIconForLeg === 'function') {
if (customIcons && typeof customIcons.customIconForLeg === 'function') {
// function exits, get the icon string lookup. It's possible for there to be
// a custom function that only returns a string for when a leg meets the
// criteria of the custom function
Expand All @@ -402,14 +402,31 @@ export function getLegIcon (leg, customIcons) {
return getIcon(iconStr, customIcons)
}

export function getCompanyForNetwork (networkString, companies = []) {
/**
* Get the configured company object for the given network string if the company
* has been defined in the provided companies array config.
*/
function getCompanyForNetwork (networkString, companies = []) {
const company = companies.find(co => co.id === networkString)
if (!company) {
console.warn(`No company found in config.yml that matches rented vehicle network: ${networkString}`, companies)
}
return company
}

/**
* Get a string label to display from a list of vehicle rental networks.
*
* @param {Array<string>} networks A list of network ids.
* @param {Array<object>} [companies=[]] An optional list of the companies config.
* @return {string} A label for use in presentation on a website.
*/
export function getCompaniesLabelFromNetworks (networks, companies = []) {
return networks.map(network => getCompanyForNetwork(network, companies))
.map(co => co.label)
.join('/')
}

/**
* Returns mode name by checking the vertex type (VertexType class in OTP) for
* the provided place. NOTE: this is currently only intended for vehicles at
Expand Down

0 comments on commit 0a5e04c

Please sign in to comment.