diff --git a/js/views/lines/Line.jsx b/js/views/lines/Line.jsx
index 0e601a92..9d7ee402 100644
--- a/js/views/lines/Line.jsx
+++ b/js/views/lines/Line.jsx
@@ -1,12 +1,11 @@
import React from 'react'
import PropTypes from 'prop-types'
-import { View, Text, StyleSheet } from 'react-native'
+import { View, StyleSheet } from 'react-native'
import leaflet from 'leaflet'
import { withRouter } from 'react-router'
import queryString from 'query-string'
import { vars } from '../../styles.js'
-import SettingsStore from '../../stores/SettingsStore.js'
import UiStore from '../../stores/UiStore.js'
import Header from '../reusable/Header.jsx'
import LinkedScroll from '../reusable/LinkedScroll.jsx'
@@ -14,8 +13,9 @@ import LinkedScroll from '../reusable/LinkedScroll.jsx'
import Layer from '../maps/Layer.jsx'
import LineData from '../../data/LineData.js'
import { LineStops } from './stops/LineStops.jsx'
+import { LineTimetable } from './timetable/LineTimetable.jsx'
+import { LineErrorBoundary } from './LineErrorBoundary.jsx'
import IconHelper from '../../helpers/icons/index.js'
-import Timetable from './Timetable.jsx'
import TripInfo from './TripInfo.jsx'
import LineIcon from '../../../dist/icons/linepicker.svg'
@@ -96,7 +96,6 @@ class Line extends React.Component {
this.state = {
lineMetadata: [],
- timetable: [],
realtimeStopUpdates: {},
currentTrip: parsed.trip_id || null,
vehiclepos: [],
@@ -114,7 +113,6 @@ class Line extends React.Component {
componentDidMount() {
this.dataResolved = this.getData()
- this.getTimetable()
this.getPositionData()
this.liveRefresh = setInterval(() => {
@@ -132,135 +130,30 @@ class Line extends React.Component {
}
async getData() {
- try {
- const metadata = await this.lineData.getMeta()
-
- if (metadata.length === 0) {
- throw new Error('The line was not found.')
- }
-
- const route =
- metadata.find(i => i.direction_id === this.lineData.direction_id) ||
- metadata[0]
- this.lineData.direction_id = route.direction_id
-
- this.setState({
- lineMetadata: metadata,
- })
+ const metadata = await this.lineData.getMeta()
- // if the stop wasn't specified in the URL
- if (this.lineData.stop_id === null) {
- this.lineData.stop_id = route.first_stop_id
- this.getTimetable()
- }
- } catch (err) {
+ if (metadata.length === 0) {
clearInterval(this.liveRefresh)
- console.error(err)
this.setState({
error: true,
- errorMessage: err.message,
+ errorMessage: 'The line was not found.',
})
+ return
}
- }
- getTimetable = async () => {
- // this will be invoked again properly
- if (this.lineData.stop_id === null) return
-
- const getTimetableState = rawData => {
- const tolerance = 1000 * 60 * 30
- const visibleTolerance = 1000 * 60 * 2
- const realtimeTolerance = 1000 * 60 * 90
- const now = new Date(new Date().getTime() - tolerance)
- const visibleNow = new Date(new Date().getTime() - visibleTolerance)
- const realtimeNow = new Date(new Date().getTime() + realtimeTolerance)
- const newState = {
- timetable: rawData
- .filter(service => {
- return now < new Date(service.departure_time)
- })
- .sort(
- (a, b) => new Date(a.departure_time) > new Date(b.departure_time)
- )
- .map(service => {
- service.visible = visibleNow < new Date(service.departure_time)
- service.realtimeQuery =
- new Date(service.departure_time) < realtimeNow
- return service
- }),
- }
- if (this.state.currentTrip !== null) {
- newState.currentTrip = this.state.currentTrip
- } else if (newState.timetable.length > 0) {
- const selectedTrip = newState.timetable.find(a => a.visible === true)
- if (selectedTrip) {
- newState.currentTrip = selectedTrip.trip_id
- }
- }
- return newState
- }
- try {
- const timetableData = await this.lineData.getTimetable(0)
- this.setState(getTimetableState(timetableData), async () => {
- const { timetable } = this.state
-
- this.lineData.realtime_trips = timetable
- .filter(t => t.realtimeQuery === true)
- .map(t => t.trip_id)
-
- this.getRealtimeStopUpdate()
-
- const tomorrowTimetableData = await this.lineData.getTimetable(1)
- const newState = getTimetableState(
- timetable.slice().concat(tomorrowTimetableData)
- )
- this.lineData.realtime_trips = newState.timetable
- .filter(t => t.realtimeQuery === true)
- .map(t => t.trip_id)
-
- this.setState(newState, () => {
- if (newState.timetable.length === 0) {
- // timetable is empty, so best guess
- const { lineMetadata } = this.state
-
- // if there's no metadata, try get it
- if (lineMetadata.length === 0) {
- this.lineData.stop_id = null
- this.getData()
- } else {
- const secondAttempt =
- lineMetadata.find(
- i => i.direction_id === this.lineData.direction_id
- ) || lineMetadata[0]
- this.lineData.direction_id = secondAttempt.direction_id
-
- if (
- this.lineData.stop_id === secondAttempt.first_stop_id &&
- this.lineData.direction_id === secondAttempt.direction_id
- ) {
- // already attempted this, so give up
- this.setState({
- error: true,
- errorMessage:
- 'We didn’t find any services for this line within the next few days.',
- })
- } else {
- this.lineData.direction_id = secondAttempt.direction_id
- this.lineData.stop_id = secondAttempt.first_stop_id
- this.getTimetable()
- }
- }
- }
- })
- })
- } catch (err) {
- // cannot get timetable, usually because the stop_id is undefined
- console.error(err)
- this.setState({
- error: true,
- errorMessage: err.message,
- })
+ const route =
+ metadata.find(i => i.direction_id === this.lineData.direction_id) ||
+ metadata[0]
+ this.lineData.direction_id = route.direction_id
+
+ // if the stop wasn't specified in the URL
+ if (this.lineData.stop_id === null) {
+ this.lineData.stop_id = route.first_stop_id
}
+
+ this.setState({
+ lineMetadata: metadata,
+ })
}
getPositionData = async () => {
@@ -289,6 +182,7 @@ class Line extends React.Component {
// this makes sure the route data has been loaded.
await this.dataResolved
const { lineMetadata } = this.state
+ if (lineMetadata.length === 0) return 'cancelled' // this if it the line can't loa
const icon = icons.get(
this.iconHelper.getRouteType(lineMetadata[0].route_type)
)
@@ -309,6 +203,14 @@ class Line extends React.Component {
this.setState({ realtimeStopUpdates })
}
+ setRealtimeTrips = (trips, triggerUpdate) => {
+ this.lineData.realtime_trips = trips
+ .filter(t => t.realtimeQuery === true)
+ .map(t => t.trip_id)
+
+ if (triggerUpdate) this.getRealtimeStopUpdate()
+ }
+
triggerTrip = tripId => {
return () => {
this.setState({
@@ -328,11 +230,11 @@ class Line extends React.Component {
error,
errorMessage,
lineMetadata,
- timetable,
currentTrip,
realtimeStopUpdates,
vehiclepos,
} = this.state
+
const currentLine =
lineMetadata.length > 0
? lineMetadata.length === 1
@@ -342,55 +244,45 @@ class Line extends React.Component {
)
: {}
- if (error) {
- return (
-
-
-
-
- Sorry! We couldn't load the {match.params.route_short_name}{' '}
- line.
-
- {errorMessage}
-
-
- )
- }
-
- const tripInfo = vehiclepos.find(pos => pos.trip_id === currentTrip)
- const timetableElement =
- timetable.length > 0 ? (
-
- ) : null
-
return (
- }
- actionFn={this.triggerPicker}
- />
-
- {timetableElement}
- {window.location.hostname === 'localhost' ? (
-
- ) : null}
-
+ }
+ actionFn={this.triggerPicker}
/>
-
+
+
+ {window.location.hostname === 'localhost' ? (
+ pos.trip_id === currentTrip)}
+ />
+ ) : null}
+
+
+
)
}
@@ -429,14 +321,5 @@ styles = StyleSheet.create({
fontSize: 13,
color: '#888',
},
-
- error: {
- padding: vars.padding,
- },
- errorMessage: {
- fontSize: vars.defaultFontSize,
- fontFamily: vars.fontFamily,
- marginBottom: vars.padding,
- },
})
export default withRouter(Line)
diff --git a/js/views/lines/LineErrorBoundary.jsx b/js/views/lines/LineErrorBoundary.jsx
new file mode 100644
index 00000000..5a1b6b7f
--- /dev/null
+++ b/js/views/lines/LineErrorBoundary.jsx
@@ -0,0 +1,49 @@
+import React, { Component } from 'react'
+import { View, Text, StyleSheet } from 'react-native'
+
+import { t } from '../../stores/translationStore.js'
+import { vars, paragraphStyles } from '../../styles.js'
+import Header from '../reusable/Header.jsx'
+
+let styles
+
+export class LineErrorBoundary extends Component {
+ constructor(props) {
+ super(props)
+ this.state = { hasError: false }
+ }
+
+ static getDerivedStateFromError(err) {
+ return { hasError: true, message: err.message }
+ }
+
+ render() {
+ const { line, forceError, errorMessage } = this.props
+ const { hasError, message } = this.state
+ if (hasError || forceError) {
+ // You can render any custom fallback UI
+ return (
+ <>
+
+
+
+ Sorry! We couldn't load the {line} line.
+
+
+ {errorMessage || message}
+
+
+ >
+ )
+ }
+
+ const { children } = this.props
+ return children
+ }
+}
+
+const { padding } = vars
+styles = StyleSheet.create({
+ error: { padding },
+ bold: { fontWeight: '600' },
+})
diff --git a/js/views/lines/Timetable.jsx b/js/views/lines/Timetable.jsx
deleted file mode 100644
index 3921f3b5..00000000
--- a/js/views/lines/Timetable.jsx
+++ /dev/null
@@ -1,189 +0,0 @@
-import React, { Fragment } from 'react'
-import PropTypes from 'prop-types'
-
-import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'
-import { vars } from '../../styles.js'
-
-import { getTime } from '../../helpers/date.js'
-
-const formatDate = (dateString, delay, isTwentyFourHour, region) => {
- const date = new Date(dateString)
- date.setTime(date.getTime() + delay * 1000)
- const humanTime = getTime(date, isTwentyFourHour, true, region)
-
- // make this nicer
- return `${humanTime.text || ''}${humanTime.subtext || ''}${
- humanTime.minutes ? `${humanTime.minutes} min` : ''
- }`
-}
-
-let styles = null
-
-const Timetable = ({
- timetable,
- currentTrip,
- triggerTrip,
- realtimeStopUpdates,
- region,
- isTwentyFourHour = false,
-}) => {
- const tripIds = {}
- return (
-
- Departures
-
- {timetable
- .map(service => {
- const realtimeTrip = realtimeStopUpdates[service.trip_id]
- if (
- realtimeTrip &&
- realtimeTrip.stopTimeUpdate &&
- realtimeTrip.stopTimeUpdate.length > 0
- ) {
- let stop = realtimeTrip.stopTimeUpdate.find(
- i => i.stopSequence === service.stop_sequence
- )
- if (!stop) [stop] = realtimeTrip.stopTimeUpdate
- service.realtimeStop = stop
-
- if (stop.stopSequence) {
- service.visible =
- stop.stopSequence < service.stop_sequence ||
- service.trip_id === currentTrip
- }
- }
- return service
- })
- .filter(service => {
- // ensures that services that start and finish at the same place with the same trip_id don't show up more than once
- tripIds[service.trip_id] = (tripIds[service.trip_id] || 0) + 1
- return service.visible === true && tripIds[service.trip_id] === 1
- })
- .map(service => {
- let departureTextStyle = null
- let emotion = null
- let delay = 0
- let scheduleRelationship = 'scheduled'
-
- if (service.realtimeStop) {
- const stop = service.realtimeStop
- scheduleRelationship = stop.scheduleRelationship.toLowerCase()
-
- if (stop.scheduleRelationship === 'SCHEDULED') {
- const estimate = stop.departure || stop.arrival
- delay = estimate.delay
-
- const delayText = `(${Math.ceil(Math.abs(delay) / 60)}m)`
- if (delay < -90) {
- emotion = styles.neutral
- scheduleRelationship = <>early {delayText}>
- } else if (delay < 180) {
- emotion = styles.positive
- scheduleRelationship = 'on time'
- } else if (delay >= 180) {
- emotion = styles.negative
- scheduleRelationship = <>late {delayText}>
- }
- } else if (stop.scheduleRelationship === 'CANCELLED') {
- departureTextStyle = styles.cancelled
- emotion = styles.negative
- }
- }
- return (
-
-
- {formatDate(
- service.departure_time,
- delay,
- isTwentyFourHour,
- region
- )}
-
-
- {scheduleRelationship}
-
-
- )
- })}
-
-
- )
-}
-
-Timetable.propTypes = {
- currentTrip: PropTypes.string,
- timetable: PropTypes.arrayOf(
- PropTypes.shape({
- trip_id: PropTypes.string.isRequired,
- departure_time: PropTypes.string.isRequired,
- })
- ).isRequired,
- triggerTrip: PropTypes.func.isRequired,
-}
-
-Timetable.defaultProps = {
- currentTrip: '',
-}
-
-styles = StyleSheet.create({
- departures: {
- flexDirection: 'row',
- overflowX: 'scroll',
- paddingBottom: vars.padding / 2,
- },
- departure: {
- backgroundColor: 'rgba(0,0,0,0.06)',
- width: `calc(33.3333% - ${Math.floor((vars.padding * 4) / 3)}px)`,
- marginLeft: vars.padding,
- borderRadius: 3,
- paddingTop: vars.padding / 2,
- paddingBottom: vars.padding / 2,
- },
- departureSelected: {
- backgroundColor: '#fff',
- boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
- },
- departureDate: {
- fontWeight: '600',
- textAlign: 'center',
- fontFamily: vars.fontFamily,
- fontSize: 16,
- },
- cancelled: {
- textDecorationStyle: 'solid',
- textDecorationLine: 'line-through',
- },
- departureStatus: {
- textAlign: 'center',
- fontFamily: vars.fontFamily,
- fontSize: 13,
- color: '#888',
- textTransform: 'capitalize',
- },
- positive: {
- color: '#27ae60',
- },
- neutral: {
- color: '#2980b9',
- },
- negative: {
- color: '#c0392b',
- },
- direction: {
- paddingTop: vars.padding,
- paddingLeft: vars.padding,
- paddingBottom: vars.padding * 0.5,
- fontWeight: '600',
- fontSize: vars.defaultFontSize,
- fontFamily: vars.fontFamily,
- },
-})
-export default Timetable
diff --git a/js/views/lines/stops/LineStops.jsx b/js/views/lines/stops/LineStops.jsx
index 65f5742c..561acbb8 100644
--- a/js/views/lines/stops/LineStops.jsx
+++ b/js/views/lines/stops/LineStops.jsx
@@ -34,15 +34,19 @@ export class LineStops extends Component {
componentDidUpdate(prevProps) {
const { tripId } = this.props
if (tripId !== prevProps.tripId) {
+ this.setState({ loading: true })
this.getStops()
}
}
componentWillUnmount() {
const { pointsLayer, shapesLayer } = this
- shapesLayer.hide(true, true)
+ // shapeslayer is nullable
+ if (shapesLayer) {
+ shapesLayer.hide(true, true)
+ shapesLayer.unmounted = true
+ }
pointsLayer.hide()
- shapesLayer.unmounted = true
pointsLayer.unmounted = true
}
@@ -113,23 +117,25 @@ export class LineStops extends Component {
render() {
const { line, region, stopId, tripId, realtimeTripUpdate } = this.props
const { loading, current, next, color } = this.state
- if (loading) {
- return
- }
+
return (
Stops
-
+ {loading ? (
+
+ ) : (
+
+ )}
)
}
diff --git a/js/views/lines/timetable/LineTimetable.jsx b/js/views/lines/timetable/LineTimetable.jsx
new file mode 100644
index 00000000..9513e070
--- /dev/null
+++ b/js/views/lines/timetable/LineTimetable.jsx
@@ -0,0 +1,160 @@
+import React, { Component } from 'react'
+import { View, Text, StyleSheet } from 'react-native'
+
+import LineData from '../../../data/LineData.js'
+import SettingsStore from '../../../stores/SettingsStore.js'
+import LineTimetableSelector from './LineTimetableSelector.jsx'
+import Spinner from '../../reusable/Spinner.jsx'
+import { vars } from '../../../styles.js'
+
+let styles = null
+
+export class LineTimetable extends Component {
+ constructor(props) {
+ super(props)
+
+ const { region, tripId, stopId } = props
+ this.lineData = new LineData({
+ region,
+ trip_id: tripId,
+ stop_id: stopId,
+ })
+
+ this.state = {
+ timetable: [],
+ }
+ }
+
+ componentDidMount() {
+ this.getTimetable()
+ }
+
+ componentDidUpdate(prevProps) {
+ const { stopId, line, agencyId, directionId } = this.props
+ if (
+ stopId !== prevProps.stopId ||
+ line !== prevProps.line ||
+ agencyId !== prevProps.agencyId ||
+ directionId !== prevProps.directionId
+ ) {
+ console.info('Getting new data')
+ this.getTimetable()
+ }
+ }
+
+ getTimetable = async () => {
+ const {
+ line,
+ stopId,
+ agencyId,
+ directionId,
+ triggerTrip,
+ setRealtimeTrips,
+ } = this.props
+ // this will be invoked again properly
+ if (stopId === null) return
+ this.lineData.route_short_name = line
+ this.lineData.stop_id = stopId
+ this.lineData.agency_id = agencyId
+ this.lineData.direction_id = directionId
+
+ const getTimetableState = rawData => {
+ const tolerance = 1000 * 60 * 30
+ const visibleTolerance = 1000 * 60 * 2
+ const realtimeTolerance = 1000 * 60 * 90
+ const now = new Date(new Date().getTime() - tolerance)
+ const visibleNow = new Date(new Date().getTime() - visibleTolerance)
+ const realtimeNow = new Date(new Date().getTime() + realtimeTolerance)
+ const newState = {
+ timetable: rawData
+ .filter(service => {
+ return now < new Date(service.departure_time)
+ })
+ .sort(
+ (a, b) => new Date(a.departure_time) > new Date(b.departure_time)
+ )
+ .map(service => ({
+ ...service,
+ visible: visibleNow < new Date(service.departure_time),
+ realtimeQuery: new Date(service.departure_time) < realtimeNow,
+ })),
+ }
+ const { tripId } = this.props
+ if (tripId == null && newState.timetable.length > 0) {
+ const selectedTrip = newState.timetable.find(a => a.visible === true)
+ if (selectedTrip) {
+ // sends the computed trip id back up to the parent component
+ triggerTrip(selectedTrip.trip_id)()
+ }
+ }
+ return newState
+ }
+
+ try {
+ const timetableData = await this.lineData.getTimetable(0)
+ this.setState(getTimetableState(timetableData), async () => {
+ const { timetable } = this.state
+ setRealtimeTrips(timetable, true)
+
+ const tomorrowTimetableData = await this.lineData.getTimetable(1)
+ const newState = getTimetableState(
+ timetable.slice().concat(tomorrowTimetableData)
+ )
+
+ setRealtimeTrips(newState.timetable, false)
+
+ this.setState(newState, async () => {
+ if (newState.timetable.length > 0) return
+ // direction id 2 is the magical both directions
+ this.lineData.direction_id = 2
+ const widerTimetableData = await this.lineData.getTimetable(0)
+ if (widerTimetableData.length > 0) {
+ this.setState(getTimetableState(widerTimetableData))
+ } else {
+ this.setState(() => {
+ throw new Error(
+ 'We didn’t find any services for this line within the next few days.'
+ )
+ })
+ }
+ })
+ })
+ } catch (err) {
+ // cannot get timetable, usually because the stop_id is undefined
+ throw new Error(err.message)
+ }
+ }
+
+ render() {
+ const { tripId, region, triggerTrip, realtimeStopUpdates } = this.props
+ const { timetable } = this.state
+
+ return (
+
+ Departures
+ {timetable.length === 0 ? (
+ // could potentially do a nicer loading state in the future
+ ) : (
+
+ )}
+
+ )
+ }
+}
+styles = StyleSheet.create({
+ direction: {
+ paddingTop: vars.padding,
+ paddingLeft: vars.padding,
+ paddingBottom: vars.padding * 0.5,
+ fontWeight: '600',
+ fontSize: vars.defaultFontSize,
+ fontFamily: vars.fontFamily,
+ },
+})
diff --git a/js/views/lines/timetable/LineTimetableSelector.jsx b/js/views/lines/timetable/LineTimetableSelector.jsx
new file mode 100644
index 00000000..81257f98
--- /dev/null
+++ b/js/views/lines/timetable/LineTimetableSelector.jsx
@@ -0,0 +1,178 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'
+import { vars } from '../../../styles.js'
+
+import { getTime } from '../../../helpers/date.js'
+
+const formatDate = (dateString, delay, isTwentyFourHour, region) => {
+ const date = new Date(dateString)
+ date.setTime(date.getTime() + delay * 1000)
+ const humanTime = getTime(date, isTwentyFourHour, true, region)
+
+ // make this nicer
+ return `${humanTime.text || ''}${humanTime.subtext || ''}${
+ humanTime.minutes ? `${humanTime.minutes} min` : ''
+ }`
+}
+
+let styles = null
+
+const LineTimetableSelector = ({
+ timetable,
+ currentTrip,
+ triggerTrip,
+ realtimeStopUpdates,
+ region,
+ isTwentyFourHour = false,
+}) => {
+ const tripIds = {}
+ return (
+
+ {timetable
+ .map(service => {
+ const realtimeTrip = realtimeStopUpdates[service.trip_id]
+ if (
+ realtimeTrip &&
+ realtimeTrip.stopTimeUpdate &&
+ realtimeTrip.stopTimeUpdate.length > 0
+ ) {
+ let stop = realtimeTrip.stopTimeUpdate.find(
+ i => i.stopSequence === service.stop_sequence
+ )
+ if (!stop) [stop] = realtimeTrip.stopTimeUpdate
+ service.realtimeStop = stop
+
+ if (stop.stopSequence) {
+ service.visible =
+ stop.stopSequence < service.stop_sequence ||
+ service.trip_id === currentTrip
+ }
+ }
+ return service
+ })
+ .filter(service => {
+ // ensures that services that start and finish at the same place with the same trip_id don't show up more than once
+ tripIds[service.trip_id] = (tripIds[service.trip_id] || 0) + 1
+ return service.visible === true && tripIds[service.trip_id] === 1
+ })
+ .map(service => {
+ let departureTextStyle = null
+ let emotion = null
+ let delay = 0
+ let scheduleRelationship = 'scheduled'
+
+ if (service.realtimeStop) {
+ const stop = service.realtimeStop
+ scheduleRelationship = stop.scheduleRelationship.toLowerCase()
+
+ if (stop.scheduleRelationship === 'SCHEDULED') {
+ const estimate = stop.departure || stop.arrival
+ delay = estimate.delay
+
+ const delayText = `(${Math.ceil(Math.abs(delay) / 60)}m)`
+ if (delay < -90) {
+ emotion = styles.neutral
+ scheduleRelationship = <>early {delayText}>
+ } else if (delay < 180) {
+ emotion = styles.positive
+ scheduleRelationship = 'on time'
+ } else if (delay >= 180) {
+ emotion = styles.negative
+ scheduleRelationship = <>late {delayText}>
+ }
+ } else if (stop.scheduleRelationship === 'CANCELLED') {
+ departureTextStyle = styles.cancelled
+ emotion = styles.negative
+ }
+ }
+ return (
+
+
+ {formatDate(
+ service.departure_time,
+ delay,
+ isTwentyFourHour,
+ region
+ )}
+
+
+ {scheduleRelationship}
+
+
+ )
+ })}
+
+ )
+}
+
+LineTimetableSelector.propTypes = {
+ currentTrip: PropTypes.string,
+ timetable: PropTypes.arrayOf(
+ PropTypes.shape({
+ trip_id: PropTypes.string.isRequired,
+ departure_time: PropTypes.string.isRequired,
+ })
+ ).isRequired,
+ triggerTrip: PropTypes.func.isRequired,
+}
+
+LineTimetableSelector.defaultProps = {
+ currentTrip: '',
+}
+
+styles = StyleSheet.create({
+ departures: {
+ flexDirection: 'row',
+ overflowX: 'scroll',
+ paddingBottom: vars.padding / 2,
+ },
+ departure: {
+ backgroundColor: 'rgba(0,0,0,0.06)',
+ width: `calc(33.3333% - ${Math.floor((vars.padding * 4) / 3)}px)`,
+ marginLeft: vars.padding,
+ borderRadius: 3,
+ paddingTop: vars.padding / 2,
+ paddingBottom: vars.padding / 2,
+ },
+ departureSelected: {
+ backgroundColor: '#fff',
+ boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
+ },
+ departureDate: {
+ fontWeight: '600',
+ textAlign: 'center',
+ fontFamily: vars.fontFamily,
+ fontSize: 16,
+ },
+ cancelled: {
+ textDecorationStyle: 'solid',
+ textDecorationLine: 'line-through',
+ },
+ departureStatus: {
+ textAlign: 'center',
+ fontFamily: vars.fontFamily,
+ fontSize: 13,
+ color: '#888',
+ textTransform: 'capitalize',
+ },
+ positive: {
+ color: '#27ae60',
+ },
+ neutral: {
+ color: '#2980b9',
+ },
+ negative: {
+ color: '#c0392b',
+ },
+})
+export default LineTimetableSelector
diff --git a/js/views/station/Station.jsx b/js/views/station/Station.jsx
index 814e53a2..43a6ba68 100644
--- a/js/views/station/Station.jsx
+++ b/js/views/station/Station.jsx
@@ -198,7 +198,7 @@ class Station extends Component {
const stopId = match.params.station
let tripIdParameter = ''
if (tripId != null) {
- tripIdParameter = `&tripId=${tripId}`
+ tripIdParameter = `&trip_id=${tripId}`
}
history.push(
diff --git a/stories/timetable.stories.jsx b/stories/timetable.stories.jsx
index 8eb2cabd..8e5ad802 100644
--- a/stories/timetable.stories.jsx
+++ b/stories/timetable.stories.jsx
@@ -1,6 +1,6 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
-import Timetable from '../js/views/lines/Timetable'
+import LineTimetableSelector from '../js/views/lines/timetable/LineTimetableSelector'
const now = new Date().toISOString()
const tenMins = new Date(new Date(now).getTime() + 10 * 60000).toISOString()
@@ -9,7 +9,7 @@ const past = new Date(new Date(now).getTime() - 10 * 60000).toISOString()
storiesOf('Timetable', module)
.add('1 trip', () => (
-
))
.add('2 trips', () => (
-
))
.add('3 trips', () => (
-
))
.add('selected trip', () => (
-
))
.add('realtime - cancelled', () => (
-
))
.add('realtime - late', () => (
-
))
.add('realtime - on time', () => (
-
))
.add('realtime - early', () => (
-
))
.add('realtime - very late', () => (
-
))
.add('realtime - very early', () => (
-