Skip to content

Commit 1b1e45d

Browse files
authored
Merge pull request #1175 from StoDevX/transit-maps
Add basic transit maps
2 parents 84dfde6 + f99c357 commit 1b1e45d

File tree

5 files changed

+180
-12
lines changed

5 files changed

+180
-12
lines changed

source/navigation.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
BuildingHoursView,
1919
BuildingHoursDetailView,
2020
} from './views/building-hours'
21-
import TransportationView from './views/transportation'
21+
import TransportationView, {BusMapView} from './views/transportation'
2222
import SettingsView from './views/settings'
2323
import SISLoginView from './views/settings/login'
2424
import CreditsView from './views/settings/credits'
@@ -58,6 +58,7 @@ export const AppNavigator = StackNavigator(
5858
StudentOrgsDetailView: {screen: StudentOrgsDetailView},
5959
StudentOrgsView: {screen: StudentOrgsView},
6060
TransportationView: {screen: TransportationView},
61+
BusMapView: {screen: BusMapView},
6162
},
6263
{
6364
navigationOptions: {

source/views/transportation/bus/bus-line.js

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
// @flow
22
import React from 'react'
3-
import {View, StyleSheet, Platform, Text} from 'react-native'
3+
import {View, Text, StyleSheet, Platform} from 'react-native'
44
import type {BusLineType, FancyBusTimeListType} from './types'
55
import {getScheduleForNow, getSetOfStopsForNow} from './lib'
66
import get from 'lodash/get'
77
import zip from 'lodash/zip'
88
import head from 'lodash/head'
99
import last from 'lodash/last'
10+
import Icon from 'react-native-vector-icons/Ionicons'
1011
import moment from 'moment-timezone'
1112
import * as c from '../../components/colors'
1213
import {Separator} from '../../components/separator'
1314
import {BusStopRow} from './bus-stop-row'
14-
import {ListRow, ListSectionHeader, Title} from '../../components/list'
15+
import {ListRow, ListSectionHeader, Title, Detail} from '../../components/list'
16+
import {Row, Column} from '../../components/layout'
1517

1618
const TIME_FORMAT = 'h:mma'
1719
const TIMEZONE = 'America/Winnipeg'
@@ -65,7 +67,7 @@ const parseTime = (now: moment) => time => {
6567
}
6668

6769
export class BusLine extends React.PureComponent {
68-
props: {line: BusLineType, now: moment}
70+
props: {line: BusLineType, now: moment, openMap: () => any}
6971

7072
render() {
7173
const {line, now} = this.props
@@ -123,11 +125,39 @@ export class BusLine extends React.PureComponent {
123125
isFirstRow={i === 0}
124126
isLastRow={i === list.length - 1}
125127
/>
126-
{i < list.length - 1
127-
? <Separator style={styles.separator} />
128-
: null}
128+
<Separator style={styles.separator} />
129129
</View>,
130130
)}
131+
132+
<ListRow
133+
onPress={this.props.openMap}
134+
fullWidth={true}
135+
spacing={{left: 45}}
136+
>
137+
<Row alignItems="center">
138+
<Column alignItems="center" width={45} paddingRight={5}>
139+
<Icon
140+
name={
141+
Platform.OS === 'ios'
142+
? 'ios-navigate-outline'
143+
: 'md-navigate'
144+
}
145+
size={24}
146+
style={{color: c.iosDisabledText}}
147+
/>
148+
</Column>
149+
150+
<Column>
151+
<Title>
152+
<Text>Open Map</Text>
153+
</Title>
154+
155+
<Detail>
156+
<Text>See the planned bus route on a map!</Text>
157+
</Detail>
158+
</Column>
159+
</Row>
160+
</ListRow>
131161
</View>
132162
)
133163
}

source/views/transportation/bus/bus-view.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {BusLineType} from './types'
66
import {BusLine} from './bus-line'
77
import moment from 'moment-timezone'
88
import {NoticeView} from '../../components/notice'
9+
import type {TopLevelViewPropsType} from '../../types'
910

1011
import {data as defaultBusLines} from '../../../../docs/bus-times.json'
1112

@@ -31,7 +32,7 @@ export class BusView extends React.PureComponent {
3132
clearTimeout(this.state.intervalId)
3233
}
3334

34-
props: {
35+
props: TopLevelViewPropsType & {
3536
busLines: BusLineType[],
3637
line: string,
3738
}
@@ -40,6 +41,10 @@ export class BusView extends React.PureComponent {
4041
this.setState(() => ({now: moment.tz(TIMEZONE)}))
4142
}
4243

44+
openMap = () => {
45+
this.props.navigation.navigate('BusMapView', {line: this.props.line})
46+
}
47+
4348
render() {
4449
let {now} = this.state
4550
// now = moment.tz('Fri 8:13pm', 'ddd h:mma', true, TIMEZONE)
@@ -56,7 +61,7 @@ export class BusView extends React.PureComponent {
5661

5762
return (
5863
<ScrollView>
59-
<BusLine line={activeBusLine} now={now} />
64+
<BusLine line={activeBusLine} now={now} openMap={this.openMap} />
6065
</ScrollView>
6166
)
6267
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// @flow
2+
3+
import React from 'react'
4+
import {StyleSheet} from 'react-native'
5+
import type {BusLineType} from './types'
6+
import MapView from 'react-native-maps'
7+
import moment from 'moment-timezone'
8+
import {NoticeView} from '../../components/notice'
9+
import type {TopLevelViewPropsType} from '../../types'
10+
import {getScheduleForNow} from './lib'
11+
import zip from 'lodash/zip'
12+
import uniqBy from 'lodash/uniqBy'
13+
14+
import {data as defaultBusLines} from '../../../../docs/bus-times.json'
15+
16+
const TIMEZONE = 'America/Winnipeg'
17+
18+
const styles = StyleSheet.create({
19+
map: {...StyleSheet.absoluteFillObject},
20+
})
21+
22+
export class BusMapView extends React.PureComponent {
23+
static navigationOptions = ({navigation}) => ({
24+
title: `${navigation.state.params.line} Map`,
25+
})
26+
27+
static defaultProps = {
28+
busLines: defaultBusLines,
29+
}
30+
31+
state = {
32+
intervalId: 0,
33+
now: moment.tz(TIMEZONE),
34+
region: {
35+
latitude: 44.44946671480875,
36+
latitudeDelta: 0.06175530810822494,
37+
longitude: -93.17014753996669,
38+
longitudeDelta: 0.05493163793703104,
39+
},
40+
}
41+
42+
componentWillMount() {
43+
// This updates the screen every second, so that the "next bus" times are
44+
// updated without needing to leave and come back.
45+
this.setState(() => ({intervalId: setInterval(this.updateTime, 5000)}))
46+
}
47+
48+
componentWillUnmount() {
49+
clearTimeout(this.state.intervalId)
50+
}
51+
52+
props: TopLevelViewPropsType & {
53+
busLines: BusLineType[],
54+
navigation: {
55+
state: {
56+
params: {
57+
line: string,
58+
},
59+
},
60+
},
61+
}
62+
63+
onRegionChange = (region: {
64+
latitude: number,
65+
latitudeDelta: number,
66+
longitude: number,
67+
longitudeDelta: number,
68+
}) => {
69+
this.setState(() => ({region}))
70+
}
71+
72+
updateTime = () => {
73+
this.setState(() => ({now: moment.tz(TIMEZONE)}))
74+
}
75+
76+
render() {
77+
let {now} = this.state
78+
// now = moment.tz('Fri 8:13pm', 'ddd h:mma', true, TIMEZONE)
79+
const busLines = this.props.busLines
80+
const lineToDisplay = this.props.navigation.state.params.line
81+
const activeBusLine = busLines.find(({line}) => line === lineToDisplay)
82+
83+
if (!activeBusLine) {
84+
const lines = busLines.map(({line}) => line).join(', ')
85+
const notice = `The line "${lineToDisplay}" was not found among ${lines}`
86+
return <NoticeView text={notice} />
87+
}
88+
89+
const schedule = getScheduleForNow(activeBusLine.schedules, now)
90+
if (!schedule) {
91+
const notice = `No schedule was found for today, ${now.format('dddd')}`
92+
return <NoticeView text={notice} />
93+
}
94+
95+
const coords = schedule.coordinates || []
96+
if (!coords.length) {
97+
const notice = `No coordinates have been provided for today's (${now.format(
98+
'dddd',
99+
)}) schedule on the "${lineToDisplay}" line`
100+
return <NoticeView text={notice} />
101+
}
102+
103+
const markers = uniqBy(
104+
zip(coords, schedule.stops),
105+
([[lat, lng]]) => `${lat},${lng}`,
106+
)
107+
108+
return (
109+
<MapView
110+
region={this.state.region}
111+
style={styles.map}
112+
onRegionChange={this.onRegionChange}
113+
loadingEnabled={true}
114+
>
115+
{markers.map(([[latitude, longitude], title], i) =>
116+
<MapView.Marker
117+
key={i}
118+
coordinate={{latitude, longitude}}
119+
title={title}
120+
// description={marker.description}
121+
// TODO: add "next arrival" time as the description
122+
/>,
123+
)}
124+
</MapView>
125+
)
126+
}
127+
}

source/views/transportation/index.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,31 @@ import {TabBarIcon} from '../components/tabbar-icon'
1212
import OtherModesView from './otherModes'
1313
import BusView from './bus'
1414

15+
export {BusMapView} from './bus/map'
16+
1517
export default TabNavigator(
1618
{
1719
ExpressLineBusView: {
18-
screen: () => <BusView line="Express Bus" />,
20+
screen: ({navigation}) =>
21+
<BusView line="Express Bus" navigation={navigation} />,
1922
navigationOptions: {
2023
tabBarLabel: 'Express Bus',
2124
tabBarIcon: TabBarIcon('bus'),
2225
},
2326
},
2427

2528
RedLineBusView: {
26-
screen: () => <BusView line="Red Line" />,
29+
screen: ({navigation}) =>
30+
<BusView line="Red Line" navigation={navigation} />,
2731
navigationOptions: {
2832
tabBarLabel: 'Red Line',
2933
tabBarIcon: TabBarIcon('bus'),
3034
},
3135
},
3236

3337
BlueLineBusView: {
34-
screen: () => <BusView line="Blue Line" />,
38+
screen: ({navigation}) =>
39+
<BusView line="Blue Line" navigation={navigation} />,
3540
navigationOptions: {
3641
tabBarLabel: 'Blue Line',
3742
tabBarIcon: TabBarIcon('bus'),

0 commit comments

Comments
 (0)