Skip to content

Commit f5a000b

Browse files
authored
Create hosted building hours (#617)
* extract data loading from BuildingHours list and add loading from github * split BuildingHours into the fetching and the listing components
1 parent e8b7be0 commit f5a000b

File tree

2 files changed

+157
-74
lines changed

2 files changed

+157
-74
lines changed

views/building-hours/index.js

Lines changed: 52 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,50 @@
11
// @flow
22
/**
33
* All About Olaf
4-
* Building Hours list page
4+
* Building Hours view. This component loads data from either GitHub or
5+
* the local copy as a fallback, and renders the list of buildings.
56
*/
67

78
import React from 'react'
8-
import {ListView, RefreshControl, StyleSheet, Platform} from 'react-native'
9-
import {BuildingRow} from './row'
9+
import {NoticeView} from '../components/notice'
1010
import {tracker} from '../../analytics'
11+
import {BuildingHoursList} from './list'
1112

13+
import type momentT from 'moment'
1214
import type {TopLevelViewPropsType} from '../types'
1315
import type {BuildingType} from './types'
14-
import delay from 'delay'
15-
import {data as buildingHours} from '../../docs/building-hours'
16+
import {data as fallbackBuildingHours} from '../../docs/building-hours'
1617
import groupBy from 'lodash/groupBy'
1718

18-
import * as c from '../components/colors'
19-
import {ListSeparator, ListSectionHeader} from '../components/list'
2019
import moment from 'moment-timezone'
2120
const CENTRAL_TZ = 'America/Winnipeg'
2221

2322
export {BuildingHoursDetailView} from './detail'
2423

25-
const styles = StyleSheet.create({
26-
container: {
27-
backgroundColor: c.white,
28-
},
29-
})
24+
const githubBaseUrl = 'https://stodevx.github.io/AAO-React-Native'
25+
26+
const groupBuildings = (buildings: BuildingType[]) => groupBy(buildings, b => b.category || 'Other')
27+
3028

3129
export class BuildingHoursView extends React.Component {
32-
state = {
33-
dataSource: this.getDataSource(),
34-
intervalId: 0,
30+
state: {
31+
error: ?Error,
32+
loading: boolean,
33+
now: momentT,
34+
buildings: {[key: string]: BuildingType[]},
35+
intervalId: number,
36+
} = {
37+
error: null,
38+
loading: true,
3539
// now: moment.tz('Wed 7:25pm', 'ddd h:mma', null, CENTRAL_TZ),
3640
now: moment.tz(CENTRAL_TZ),
37-
refreshing: false,
41+
buildings: groupBuildings(fallbackBuildingHours),
42+
intervalId: 0,
3843
}
3944

4045
componentWillMount() {
46+
this.fetchData()
47+
4148
// This updates the screen every ten seconds, so that the building
4249
// info statuses are updated without needing to leave and come back.
4350
this.setState({intervalId: setInterval(this.updateTime, 10000)})
@@ -49,77 +56,48 @@ export class BuildingHoursView extends React.Component {
4956

5057
props: TopLevelViewPropsType;
5158

52-
getDataSource(){
53-
return new ListView.DataSource({
54-
rowHasChanged: (r1: BuildingType, r2: BuildingType) => r1 !== r2,
55-
sectionHeaderHasChanged: (r1: any, r2: any) => r1 !== r2,
56-
}).cloneWithRowsAndSections(groupBy(buildingHours, b => b.category || 'Other'))
57-
}
58-
5959
updateTime = () => {
60-
this.setState({
61-
now: moment.tz(CENTRAL_TZ),
62-
dataSource: this.getDataSource(),
63-
})
64-
}
65-
66-
onPressRow = (data: BuildingType) => {
67-
tracker.trackEvent('building-hours', data.name)
68-
this.props.navigator.push({
69-
id: 'BuildingHoursDetailView',
70-
index: this.props.route.index + 1,
71-
title: data.name,
72-
backButtonTitle: 'Hours',
73-
props: data,
74-
sceneConfig: Platform.OS === 'android' ? 'fromBottom' : undefined,
75-
})
60+
this.setState({now: moment.tz(CENTRAL_TZ)})
7661
}
7762

78-
renderRow = (data: BuildingType) => {
79-
return (
80-
<BuildingRow
81-
name={data.name}
82-
info={data}
83-
now={this.state.now}
84-
onPress={() => this.onPressRow(data)}
85-
/>
86-
)
87-
}
63+
fetchData = async () => {
64+
this.setState({loading: true})
8865

89-
renderSectionHeader = (data: any, id: string) => {
90-
return <ListSectionHeader style={styles.rowSectionHeader} title={id} />
91-
}
66+
let buildings: BuildingType[] = []
67+
try {
68+
let container = await fetchJson(`${githubBaseUrl}/building-hours.json`)
69+
let data = container.data
70+
buildings = data
71+
} catch (err) {
72+
tracker.trackException(err.message)
73+
console.warn(err)
74+
buildings = fallbackBuildingHours
75+
}
9276

93-
renderSeparator = (sectionID: any, rowID: any) => {
94-
return <ListSeparator key={`${sectionID}-${rowID}`} />
95-
}
77+
if (__DEV__) {
78+
buildings = fallbackBuildingHours
79+
}
9680

97-
refresh = async () => {
98-
this.setState({refreshing: true})
99-
await delay(500)
10081
this.setState({
82+
loading: false,
83+
buildings: groupBuildings(buildings),
10184
now: moment.tz(CENTRAL_TZ),
102-
refreshing: false,
103-
dataSource: this.getDataSource(),
10485
})
10586
}
10687

107-
// Render a given scene
10888
render() {
89+
if (this.state.error) {
90+
return <NoticeView text={'Error: ' + this.state.error.message} />
91+
}
92+
10993
return (
110-
<ListView
111-
dataSource={this.state.dataSource}
112-
renderRow={this.renderRow}
113-
renderSectionHeader={this.renderSectionHeader}
114-
renderSeparator={this.renderSeparator}
115-
contentContainerStyle={styles.container}
116-
removeClippedSubviews={false} // remove after https://github.com/facebook/react-native/issues/8607#issuecomment-241715202
117-
refreshControl={
118-
<RefreshControl
119-
refreshing={this.state.refreshing}
120-
onRefresh={this.refresh}
121-
/>
122-
}
94+
<BuildingHoursList
95+
route={this.props.route}
96+
navigator={this.props.navigator}
97+
buildings={this.state.buildings}
98+
now={this.state.now}
99+
onRefresh={this.fetchData}
100+
loading={this.state.loading}
123101
/>
124102
)
125103
}

views/building-hours/list.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// @flow
2+
/**
3+
* All About Olaf
4+
* Building Hours list page
5+
*/
6+
7+
import React from 'react'
8+
import {ListView, RefreshControl, StyleSheet, Platform} from 'react-native'
9+
import {BuildingRow} from './row'
10+
import {tracker} from '../../analytics'
11+
12+
import type momentT from 'moment'
13+
import type {TopLevelViewPropsType} from '../types'
14+
import type {BuildingType} from './types'
15+
16+
import * as c from '../components/colors'
17+
import {ListSeparator, ListSectionHeader} from '../components/list'
18+
19+
export {BuildingHoursDetailView} from './detail'
20+
21+
const styles = StyleSheet.create({
22+
container: {
23+
backgroundColor: c.white,
24+
},
25+
})
26+
27+
type BuildingHoursPropsType = TopLevelViewPropsType & {
28+
now: momentT,
29+
loading: boolean,
30+
onRefresh: () => any,
31+
buildings: {[key: string]: BuildingType[]},
32+
};
33+
34+
export class BuildingHoursList extends React.Component {
35+
state = {
36+
dataSource: this.getDataSource(this.props),
37+
}
38+
39+
componentWillMount() {
40+
this.setState({dataSource: this.getDataSource(this.props)})
41+
}
42+
43+
componentWillReceiveProps(nextProps: BuildingHoursPropsType) {
44+
this.setState({dataSource: this.getDataSource(nextProps)})
45+
}
46+
47+
props: BuildingHoursPropsType;
48+
49+
getDataSource(props: BuildingHoursPropsType) {
50+
return new ListView.DataSource({
51+
rowHasChanged: (r1: BuildingType, r2: BuildingType) => r1 !== r2,
52+
sectionHeaderHasChanged: (r1: any, r2: any) => r1 !== r2,
53+
}).cloneWithRowsAndSections(props.buildings)
54+
}
55+
56+
onPressRow = (data: BuildingType) => {
57+
tracker.trackEvent('building-hours', data.name)
58+
this.props.navigator.push({
59+
id: 'BuildingHoursDetailView',
60+
index: this.props.route.index + 1,
61+
title: data.name,
62+
backButtonTitle: 'Hours',
63+
props: data,
64+
sceneConfig: Platform.OS === 'android' ? 'fromBottom' : undefined,
65+
})
66+
}
67+
68+
renderRow = (data: BuildingType) => {
69+
return (
70+
<BuildingRow
71+
name={data.name}
72+
info={data}
73+
now={this.props.now}
74+
onPress={() => this.onPressRow(data)}
75+
/>
76+
)
77+
}
78+
79+
renderSectionHeader = (data: any, id: string) => {
80+
return <ListSectionHeader style={styles.rowSectionHeader} title={id} />
81+
}
82+
83+
renderSeparator = (sectionID: any, rowID: any) => {
84+
return <ListSeparator key={`${sectionID}-${rowID}`} />
85+
}
86+
87+
render() {
88+
return (
89+
<ListView
90+
dataSource={this.state.dataSource}
91+
renderRow={this.renderRow}
92+
renderSectionHeader={this.renderSectionHeader}
93+
renderSeparator={this.renderSeparator}
94+
contentContainerStyle={styles.container}
95+
removeClippedSubviews={false} // remove after https://github.com/facebook/react-native/issues/8607#issuecomment-241715202
96+
refreshControl={
97+
<RefreshControl
98+
refreshing={this.props.loading}
99+
onRefresh={this.props.onRefresh}
100+
/>
101+
}
102+
/>
103+
)
104+
}
105+
}

0 commit comments

Comments
 (0)