Skip to content

Commit 353b088

Browse files
committed
feat(form): improve eScooter mode configuration and icon display
1 parent c76da2a commit 353b088

File tree

11 files changed

+226
-90
lines changed

11 files changed

+226
-90
lines changed

lib/actions/api.js

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,22 @@ export function routingQuery () {
3030
dispatch(routingRequest({ routingType, searchId }))
3131

3232
// fetch a realtime route
33-
fetch(constructRoutingQuery(otpState))
34-
.then(getJsonAndCheckResponse)
35-
.then(json => {
36-
dispatch(routingResponse({ response: json, searchId }))
37-
})
38-
.catch(error => {
39-
dispatch(routingError({ error, searchId }))
40-
})
33+
try {
34+
const realtimeResponse = await fetch(constructRoutingQuery(otpState))
35+
const realtimeJson = await getJsonAndCheckResponse(realtimeResponse)
36+
await dispatch(routingResponse({ response: realtimeJson, searchId }))
37+
} catch (error) {
38+
dispatch(routingError({ error, searchId }))
39+
}
4140

4241
// also fetch a non-realtime route
43-
fetch(constructRoutingQuery(otpState, true))
44-
.then(getJsonAndCheckResponse)
45-
.then(json => {
46-
dispatch(nonRealtimeRoutingResponse({ response: json, searchId }))
47-
})
48-
.catch(error => {
49-
console.error(error)
50-
// do nothing
51-
})
42+
try {
43+
const staticResponse = await fetch(constructRoutingQuery(otpState, true))
44+
const staticJson = await getJsonAndCheckResponse(staticResponse)
45+
await dispatch(nonRealtimeRoutingResponse({ response: staticJson, searchId }))
46+
} catch (error) {
47+
console.error(error)
48+
}
5249
}
5350
}
5451

lib/components/form/settings-selector-panel.js

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,34 @@ class SettingsSelectorPanel extends Component {
5959

6060
_setMicromobilityOnly = () => { this._setSoloMode('MICROMOBILITY') }
6161

62-
_addBikeModeToQueryModes = (bikeMode) => {
62+
/**
63+
* Replace own mode with new mode. The only mode will have already been set,
64+
* so this toggles whether the own mode includes a rental.
65+
*/
66+
_replaceOwnMode = (newMode, referenceOwnMode) => {
6367
const { queryModes, setQueryParam } = this.props
64-
const nonBikeModes = queryModes.filter(m => !m.startsWith('BICYCLE'))
65-
setQueryParam({ mode: [...nonBikeModes, bikeMode].join(',') })
68+
const nonOwnModes = queryModes.filter(m => !m.startsWith(referenceOwnMode))
69+
setQueryParam({ mode: [...nonOwnModes, newMode].join(',') })
6670
}
6771

68-
_setOwnBike = () => this._addBikeModeToQueryModes('BICYCLE')
72+
_setOwnBike = () => this._replaceOwnMode('BICYCLE', 'BICYCLE')
6973

70-
_setRentedBike = () => this._addBikeModeToQueryModes('BICYCLE_RENT')
74+
_setRentedBike = () => this._replaceOwnMode('BICYCLE_RENT', 'BICYCLE')
75+
76+
_setOwnVehicle = () => this._replaceOwnMode('MICROMOBILITY', 'MICROMOBILITY')
77+
78+
_setRentedVehicle = () => {
79+
this._replaceOwnMode('MICROMOBILITY_RENT', 'MICROMOBILITY')
80+
this.props.setQueryParam({ companies: this._getCompaniesForMode('MICROMOBILITY_RENT') })
81+
}
82+
83+
_getCompaniesForMode = (modeStr) => {
84+
const {config} = this.props
85+
return config.companies
86+
.filter(co => co.modes.indexOf(modeStr) > -1)
87+
.map(co => co.id)
88+
.join(',')
89+
}
7190

7291
_toggleCompany (company) {
7392
const {companies, setQueryParam} = this.props
@@ -145,10 +164,7 @@ class SettingsSelectorPanel extends Component {
145164
// a specific company
146165
: (hasVehicleRental(modeStr) || hasVehicleHailing(modeStr))
147166
// when switching, add all companies at first
148-
? config.companies
149-
.filter(co => co.modes.indexOf(modeStr) > -1)
150-
.map(co => co.id)
151-
.join(',')
167+
? this._getCompaniesForMode(modeStr)
152168
// mode is not renting or hailing and not associated with any company
153169
: null
154170

@@ -171,6 +187,12 @@ class SettingsSelectorPanel extends Component {
171187
return null
172188
}
173189

190+
// hack for TriMet-MOD project, don't show companies if Biketown enabled
191+
// when using just bike rentals
192+
if (mode && mode.indexOf('BICYCLE_RENT') > -1) {
193+
return null
194+
}
195+
174196
// check if renting or hailing
175197
if (hasVehicleRental(mode) || hasVehicleHailing(mode)) {
176198
const queryModes = mode.split(',')

lib/components/narrative/line-itin/access-leg-body.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import currencyFormatter from 'currency-formatter'
66
import LegDiagramPreview from '../leg-diagram-preview'
77

88
import { distanceString } from '../../../util/distance'
9-
import { getLegModeString, getIcon, getPlaceName, getStepDirection, getStepStreetName } from '../../../util/itinerary'
9+
import { getLegModeLabel, getIcon, getPlaceName, getStepDirection, getStepStreetName } from '../../../util/itinerary'
1010
import { formatDuration, formatTime } from '../../../util/time'
1111
import { isMobile } from '../../../util/ui'
1212

@@ -15,7 +15,6 @@ import DirectionIcon from '../../icons/direction-icon'
1515
export default class AccessLegBody extends Component {
1616
static propTypes = {
1717
leg: PropTypes.object,
18-
legMode: PropTypes.any,
1918
routingType: PropTypes.string
2019
}
2120

@@ -33,15 +32,15 @@ export default class AccessLegBody extends Component {
3332
}
3433

3534
render () {
36-
const { customIcons, leg, legMode, timeOptions, followsTransit } = this.props
35+
const { customIcons, followsTransit, iconKey, leg, timeOptions } = this.props
3736

3837
if (leg.mode === 'CAR' && leg.hailedCar) {
39-
return <TNCLeg leg={leg} legMode={legMode} onSummaryClick={this._onSummaryClick} timeOptions={timeOptions} followsTransit={followsTransit} customIcons={customIcons} />
38+
return <TNCLeg leg={leg} iconKey={iconKey} onSummaryClick={this._onSummaryClick} timeOptions={timeOptions} followsTransit={followsTransit} customIcons={customIcons} />
4039
}
4140

4241
return (
4342
<div className='leg-body'>
44-
<AccessLegSummary leg={leg} legMode={legMode} onSummaryClick={this._onSummaryClick} customIcons={customIcons} />
43+
<AccessLegSummary leg={leg} iconKey={iconKey} onSummaryClick={this._onSummaryClick} customIcons={customIcons} />
4544

4645
<div onClick={this._onStepsHeaderClick} className='steps-header'>
4746
{formatDuration(leg.duration)}
@@ -60,7 +59,15 @@ export default class AccessLegBody extends Component {
6059
class TNCLeg extends Component {
6160
render () {
6261
// TODO: ensure that client ID fields are populated
63-
const { customIcons, leg, legMode, timeOptions, followsTransit, LYFT_CLIENT_ID, UBER_CLIENT_ID } = this.props
62+
const {
63+
LYFT_CLIENT_ID,
64+
UBER_CLIENT_ID,
65+
customIcons,
66+
followsTransit,
67+
iconKey,
68+
leg,
69+
timeOptions
70+
} = this.props
6471
const universalLinks = {
6572
'UBER': `https://m.uber.com/${isMobile() ? 'ul/' : ''}?client_id=${UBER_CLIENT_ID}&action=setPickup&pickup[latitude]=${leg.from.lat}&pickup[longitude]=${leg.from.lon}&pickup[formatted_address]=${encodeURI(leg.from.name)}&dropoff[latitude]=${leg.to.lat}&dropoff[longitude]=${leg.to.lon}&dropoff[formatted_address]=${encodeURI(leg.to.name)}`,
6673
'LYFT': `https://lyft.com/ride?id=lyft&partner=${LYFT_CLIENT_ID}&pickup[latitude]=${leg.from.lat}&pickup[longitude]=${leg.from.lon}&destination[latitude]=${leg.to.lat}&destination[longitude]=${leg.to.lon}`
@@ -76,7 +83,7 @@ class TNCLeg extends Component {
7683

7784
<div className='leg-body'>
7885
{/* The icon/summary row */}
79-
<AccessLegSummary leg={leg} legMode={legMode} onSummaryClick={this.props.onSummaryClick} customIcons={customIcons} />
86+
<AccessLegSummary leg={leg} iconKey={iconKey} onSummaryClick={this.props.onSummaryClick} customIcons={customIcons} />
8087

8188
{/* The "Book Ride" button */}
8289
<div style={{ marginTop: 10, marginBottom: 10, height: 32, position: 'relative' }}>
@@ -119,15 +126,15 @@ class TNCLeg extends Component {
119126

120127
class AccessLegSummary extends Component {
121128
render () {
122-
const { customIcons, leg, legMode } = this.props
129+
const { customIcons, leg, iconKey } = this.props
123130
return (
124131
<div className='summary leg-description' onClick={this.props.onSummaryClick}>
125132
{/* Mode-specific icon */}
126-
<div><div className='icon'>{getIcon(legMode, customIcons)}</div></div>
133+
<div><div className='icon'>{getIcon(iconKey, customIcons)}</div></div>
127134

128135
{/* Leg description, e.g. "Walk 0.5 mi to..." */}
129136
<div>
130-
{getLegModeString(leg)}
137+
{getLegModeLabel(leg)}
131138
{' '}
132139
{leg.distance && <span> {distanceString(leg.distance)}</span>}
133140
{` to ${getPlaceName(leg.to)}`}

lib/components/narrative/line-itin/itin-summary.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { Component } from 'react'
22
import PropTypes from 'prop-types'
33

4-
import { calculateFares, calculatePhysicalActivity, getLegMode, getIcon, isTransit } from '../../../util/itinerary'
4+
import { calculateFares, calculatePhysicalActivity, getLegIconKey, getIcon, isTransit } from '../../../util/itinerary'
55
import { formatDuration, formatTime } from '../../../util/time'
66

77
// TODO: make this a prop
@@ -61,13 +61,13 @@ export default class ItinerarySummary extends Component {
6161
{itinerary.legs.filter(leg => {
6262
return !(leg.mode === 'WALK' && itinerary.transitTime > 0)
6363
}).map((leg, k) => {
64-
let { legMode } = getLegMode(companies, leg)
65-
if (typeof customIcons.customModeForLeg === 'function') {
66-
const customMode = customIcons.customModeForLeg(leg)
67-
if (customMode) legMode = customMode
64+
let iconStr = getLegIconKey(leg)
65+
if (typeof customIcons.customIconForLeg === 'function') {
66+
const customIconStr = customIcons.customIconForLeg(leg)
67+
if (customIconStr) iconStr = customIconStr
6868
}
6969
return <div className='route-preview' key={k}>
70-
<div className='mode-icon'>{getIcon(legMode, customIcons)}</div>
70+
<div className='mode-icon'>{getIcon(iconStr, customIcons)}</div>
7171
{isTransit(leg.mode)
7272
? (
7373
<div className='short-name' style={{ backgroundColor: getRouteColorForBadge(leg) }}>

lib/components/narrative/line-itin/line-itinerary.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react'
22

33
import NarrativeItinerary from '../narrative-itinerary'
44
import SimpleRealtimeAnnotation from '../simple-realtime-annotation'
5-
import { getLegModeString, getTimeZoneOffset, isTransit } from '../../../util/itinerary'
5+
import { getLegModeLabel, getTimeZoneOffset, isTransit } from '../../../util/itinerary'
66

77
import ItinerarySummary from './itin-summary'
88
import ItineraryBody from './itin-body'
@@ -18,14 +18,14 @@ export default class LineItinerary extends NarrativeItinerary {
1818
let transitModes = []
1919
itinerary.legs.forEach((leg, index) => {
2020
if (isTransit(leg.mode)) {
21-
const modeStr = getLegModeString(leg)
21+
const modeStr = getLegModeLabel(leg)
2222
if (transitModes.indexOf(modeStr) === -1) transitModes.push(modeStr)
2323
}
2424
})
2525

2626
// check for access mode
2727
if (!isTransit(itinerary.legs[0].mode)) {
28-
summary += getLegModeString(itinerary.legs[0])
28+
summary += getLegModeLabel(itinerary.legs[0])
2929
}
3030

3131
// append transit modes, if applicable

lib/components/narrative/line-itin/place-row.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { Component } from 'react'
22

33
import LocationIcon from '../../icons/location-icon'
44
import ViewStopButton from '../../viewers/view-stop-button'
5-
import { getLegMode, getPlaceName } from '../../../util/itinerary'
5+
import { getLegIconKey, getPlaceName } from '../../../util/itinerary'
66
import { formatTime } from '../../../util/time'
77

88
import TransitLegBody from './transit-leg-body'
@@ -12,7 +12,6 @@ import AccessLegBody from './access-leg-body'
1212
const defaultRouteColor = '#008'
1313

1414
export default class PlaceRow extends Component {
15-
1615
_createLegLine (leg) {
1716
switch (leg.mode) {
1817
case 'WALK': return <div className='leg-line leg-line-walk' />
@@ -124,14 +123,14 @@ export default class PlaceRow extends Component {
124123
)
125124
: (/* This is an access (e.g. walk/bike/etc.) leg */
126125
<AccessLegBody
126+
customIcons={customIcons}
127+
followsTransit={followsTransit}
128+
iconKey={getLegIconKey(leg)}
127129
leg={leg}
128130
legIndex={legIndex}
129-
legMode={getLegMode(this.props.companies, leg).legMode}
130131
routingType={this.props.routingType}
131132
setActiveLeg={this.props.setActiveLeg}
132133
timeOptions={timeOptions}
133-
followsTransit={followsTransit}
134-
customIcons={customIcons}
135134
/>
136135
)
137136
)}

lib/components/narrative/line-itin/transit-leg-body.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ export default class TransitLegBody extends Component {
4242
const { alerts, mode, routeShortName, routeLongName, headsign } = leg
4343
const { alertsExpanded, stopsExpanded } = this.state
4444

45-
let iconMode = mode
46-
if (typeof customIcons.customModeForLeg === 'function') {
47-
const customMode = customIcons.customModeForLeg(leg)
48-
if (customMode) iconMode = customMode
45+
let iconKey = mode
46+
if (typeof customIcons.customIconForLeg === 'function') {
47+
const customIcon = customIcons.customIconForLeg(leg)
48+
if (customIcon) iconKey = customIcon
4949
}
5050

5151
return (
@@ -54,7 +54,7 @@ export default class TransitLegBody extends Component {
5454
<div className='summary' onClick={this._onSummaryClick}>
5555
<div className='route-name leg-description'>
5656
<div>
57-
<div className='icon'>{getIcon(iconMode, customIcons)}</div>
57+
<div className='icon'>{getIcon(iconKey, customIcons)}</div>
5858
</div>
5959
{routeShortName && (
6060
<div>

lib/components/narrative/printable/printable-itinerary.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types'
44
import ModeIcon from '../../icons/mode-icon'
55
import TripDetails from '../trip-details'
66
import { formatTime, formatDuration } from '../../../util/time'
7-
import { getLegModeString, getStepDirection, getStepStreetName, getLegMode, getTimeZoneOffset } from '../../../util/itinerary'
7+
import { getLegModeLabel, getStepDirection, getStepStreetName, getTimeZoneOffset } from '../../../util/itinerary'
88

99
export default class PrintableItinerary extends Component {
1010
static propTypes = {
@@ -33,7 +33,7 @@ export default class PrintableItinerary extends Component {
3333
{itinerary.legs.map((leg, k) => leg.transitLeg
3434
? <TransitLeg key={k} leg={leg} interlineFollows={k < itinerary.legs.length - 1 && itinerary.legs[k + 1].interlineWithPreviousLeg} timeOptions={timeOptions} />
3535
: leg.hailedCar
36-
? <TNCLeg leg={leg} legMode={getLegMode(companies, leg)} timeOptions={timeOptions} />
36+
? <TNCLeg leg={leg} timeOptions={timeOptions} />
3737
: <AccessLeg key={k} leg={leg} timeOptions={timeOptions} />
3838
)}
3939
<TripDetails itinerary={itinerary} />
@@ -98,7 +98,7 @@ class AccessLeg extends Component {
9898
<div className='mode-icon'><ModeIcon mode={leg.mode} /></div>
9999
<div className='leg-body'>
100100
<div className='leg-header'>
101-
<b>{getLegModeString(leg)}</b> to <b>{leg.to.name}</b>
101+
<b>{getLegModeLabel(leg)}</b> to <b>{leg.to.name}</b>
102102
</div>
103103
{!leg.hailedCar && (
104104
<div className='leg-details'>

lib/util/itinerary.js

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ export function getStepStreetName (step) {
174174
return step.streetName
175175
}
176176

177-
export function getLegModeString (leg) {
177+
export function getLegModeLabel (leg) {
178178
switch (leg.mode) {
179179
case 'BICYCLE_RENT': return 'Biketown'
180180
case 'CAR': return leg.hailedCar ? 'Ride' : 'Drive'
@@ -350,36 +350,21 @@ export function toSentenceCase (str) {
350350
return str.charAt(0).toUpperCase() + str.substr(1).toLowerCase()
351351
}
352352

353-
// Temporary hack for getting TNC details
354-
// TODO: do we still need this?
355-
export function getLegMode (companies, leg) {
356-
let legMode = leg.mode
357-
let isTNC = false
358-
if (legMode === 'CAR' && leg.rentedCar) {
359-
legMode = {
360-
company: companies,
361-
mode: 'CAR_RENT'
362-
}
363-
} else if (legMode === 'CAR' && companies) {
364-
legMode = {
365-
company: companies,
366-
mode: 'CAR_HAIL'
367-
}
368-
isTNC = true
369-
} else if (legMode === 'BICYCLE' && leg.rentedBike) {
370-
legMode = {
371-
mode: 'BICYCLE_RENT'
372-
}
373-
} else if (legMode === 'MICROMOBILITY' && leg.rentedVehicle) {
374-
legMode = {
375-
mode: 'MICROMOBILITY_RENT'
376-
}
353+
// Return an icon key depending on the mode
354+
export function getLegIconKey (leg) {
355+
const legModeStr = leg.mode
356+
if (legModeStr === 'CAR' && leg.rentedCar) {
357+
return leg.rentedCarData.companies[0]
358+
} else if (legModeStr === 'CAR' && leg.tncData) {
359+
return leg.tncData.company
360+
} else if (legModeStr === 'BICYCLE' && leg.rentedBike) {
361+
// hack for current TriMet MOD project
362+
return 'BIKETOWN'
363+
} else if (legModeStr === 'MICROMOBILITY' && leg.rentedVehicle) {
364+
return leg.rentedVehicleData.companies[0]
377365
}
378366

379-
return {
380-
legMode,
381-
isTNC
382-
}
367+
return legModeStr
383368
}
384369

385370
export function getPlaceName (place) {

0 commit comments

Comments
 (0)