Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Itineraries display (results) #2

Merged
merged 12 commits into from
Jul 10, 2018
Binary file added assets/images/bus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/tram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/walk.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 92 additions & 0 deletions components/Itinerary/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, { Component } from 'react';
import T from 'prop-types';
import moment from 'moment';

import { View, Text } from 'react-native';

import LegType from '../../components/LegType';

// Itinerary is a component that displays an itinerary
// with its different legs
class Itinerary extends Component {
state = {
open: false,
};

render() {
const { itinerary: i } = this.props;
return (
<View
style={{
marginBottom: 8,
paddingBottom: 8,
borderBottomRadius: 4,
borderBottomWidth: 0.5,
borderBottomColor: '#CCCCCC',
}}>
<View style={{ display: 'flex', flexDirection: 'column' }}>
<View style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
<View
style={{
flex: 4,
display: 'flex',
flexDirection: 'row',
}}>
{/* Legs Walk > Bus > Walk ... */}
{i.legs.map((leg, index) => (
<View
key={index}
style={{
display: 'flex',
flexDirection: 'row',
alignContent: 'center',
alignItems: 'center',
}}>
<LegType leg={leg} />
{index !== i.legs.length - 1 && <Text style={{ color: '#CCCCCC' }}>{' > '}</Text>}
</View>
))}
</View>
<Text style={{ flex: 1, padding: 4 }}>{Math.round(i.duration / 60)} min ></Text>
</View>
</View>
<View style={{ marginTop: 8 }}>
<Text>
{moment(i.startTime).format('HH:mm')}
{' - '}
{moment(i.endTime).format('HH:mm')}
</Text>
</View>
</View>
);
}
}

Itinerary.propTypes = {
itinerary: T.shape({
// duration <seconds>
duration: T.number.isRequired,

// startTime <timestamp>
startTime: T.number.isRequired,
// endTime <timestamp>
endTime: T.number.isRequired,

// legs <Leg>
legs: T.array.isRequired,

// transfers <int>
transfers: T.number.isRequired,

// transitTime <seconds>
transitTime: T.number.isRequired,
// waitingTime <seconds>
waitingTime: T.number.isRequired,
// walkDistance <meters>
walkDistance: T.number.isRequired,
// walkTime <seconds>
walkTime: T.number.isRequired,
}),
};

export default Itinerary;
111 changes: 111 additions & 0 deletions components/LegType/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React, { Component } from 'react';
import T from 'prop-types';

import { View, Text, Image } from 'react-native';

const imageStyle = {
resizeMode: 'contain',
height: 20,
width: 20,
};

const modeToDisplay = type => {
switch (type) {
case 'WALK':
return <Image source={require('../../assets/images/walk.png')} style={imageStyle} />;
case 'BUS':
return <Image source={require('../../assets/images/bus.png')} style={imageStyle} />;
case 'TRAM':
return <Image source={require('../../assets/images/tram.png')} style={imageStyle} />;
default:
return <Text style={{ padding: 4, flex: 1 }}>?</Text>;
}
};

// Itinerary is a component that displays an itinerary
// with its different legs
class LegType extends Component {
render() {
const { leg: l } = this.props;
return (
<View
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-around',
alignContent: 'center',
alignItems: 'center',
}}>
{modeToDisplay(l.mode)}

{l.mode !== 'WALK' && (
<Text
style={{
backgroundColor: `#${l.routeColor}`,
textAlign: 'center',
paddingLeft: 4,
paddingRight: 4,
marginLeft: 4,
}}>
{l.route}
</Text>
)}
{l.mode === 'WALK' && (
<Text
style={{
textAlign: 'left',
color: '#424242',
marginTop: 8,
}}>
{Math.round(l.duration / 60)}
</Text>
)}
</View>
);
}
}

LegType.propTypes = {
leg: T.shape({
// For transit legs, the type of the route.
// - Non transit -1
// - When 0-7:
// - 0 Tram
// - 1 Subway
// - 2 Train
// - 3 Bus
// - 4 Ferry
// - 5 Cable Car
// - 6 Gondola
// - 7 Funicular
// - When equal or highter than 100:
// it is coded using the Hierarchical Vehicle Type (HVT)
// codes from the European TPEG standard
routeType: T.number.isRequired,

// For transit leg:
// - the route's (background) color (if one exists)
// For non-transit legs
// - null.
routeColor: T.string,

// For transit leg:
// - the route's text color (if one exists)
// For non-transit legs
// - null.
routeTextcolor: T.string,

// The mode used when traversing this leg.
// ex: BUS, WALK
mode: T.string.isRequired,

// For transit legs:
// - the route of the bus or train being used
// For non-transit legs
// - the name of the street being traversed.
// ex: 4, eq Line 4
route: T.string.isRequired,
}),
};

export default LegType;
4 changes: 3 additions & 1 deletion components/RouteSearchForm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class RouteSearchForm extends Component {
change = (fromTo, place) => this.setState({ [fromTo]: place }, this.lookForRoute);

lookForRoute = async () => {
this.props.onSearch();
console.log('@lookForRoute', this.state);
const { from, to } = this.state;
if (!from || !to) return;
Expand All @@ -59,7 +60,7 @@ class RouteSearchForm extends Component {
}),
});
const data = await response.json();
this.props.onResults(data.plan.itineraries);
this.props.onResults(data.plan.itineraries || []);
} catch (error) {
console.log('error', error, Object.keys(error));
}
Expand All @@ -80,6 +81,7 @@ class RouteSearchForm extends Component {
RouteSearchForm.propTypes = {
/* functions */
onResults: T.func.isRequired,
onSearch: T.func.isRequired,
};

export default RouteSearchForm;
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@
},
"dependencies": {
"@expo/samples": "2.1.1",
"expo": "^27.0.1",
"expo": "28.0.0",
"moment": "^2.22.2",
"react": "16.3.1",
"react-native": "https://github.com/expo/react-native/archive/sdk-27.0.0.tar.gz",
"react-navigation": "2.0.0"
},
"devDependencies": {
"eslint": "^4.19.1",
"eslint-config-universe": "^1.0.7",
"jest-expo": "^27.0.0",
"jest-expo": "28.0.0",
"prettier": "^1.13.5"
}
}
23 changes: 21 additions & 2 deletions screens/HomeScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,46 @@ import { WebBrowser } from 'expo';
import { MonoText } from '../components/StyledText';

import RouteSearchForm from '../components/RouteSearchForm';
import Itinerary from '../components/Itinerary';

export default class HomeScreen extends React.Component {
static navigationOptions = {
header: null,
};

state = {
loading: false,
results: [],
hasSearched: false,
};

onItineraryResults = results => {
console.log('results', results);

this.setState({ results });
this.setState({ results, hasSearched: true, loading: false });
};

render() {
return (
<View style={styles.container}>
<ScrollView style={styles.container} contentContainerStyle={styles.contentContainer}>
<RouteSearchForm onResults={this.onItineraryResults} />
<RouteSearchForm
onResults={this.onItineraryResults}
onSearch={() => this.setState({ loading: true })}
/>

{this.state.loading && (
<View>
<Text>Recherche en cours..</Text>
</View>
)}

{/* interaries */}
{this.state.hasSearched && (
<View style={{ padding: 8 }}>
{this.state.results.map(result => <Itinerary itinerary={result} />)}
</View>
)}

<View style={styles.welcomeContainer}>
<Image
Expand Down
Loading