From 0861b6875ce2083951cdf94b85de254f669d8451 Mon Sep 17 00:00:00 2001
From: Paul Makles <paulmakles@gmail.com>
Date: Mon, 17 Dec 2018 20:32:38 +0000
Subject: [PATCH] Restructure project

---
 App.js          | 397 +-----------------------------------------------
 app.json        |   2 +-
 src/Arrivals.js | 195 ++++++++++++++++++++++++
 src/Request.js  |  66 ++++++++
 src/Search.js   |  98 ++++++++++++
 src/Tabs.js     |  44 ++++++
 src/styles.js   |  24 +--
 7 files changed, 419 insertions(+), 407 deletions(-)
 create mode 100644 src/Arrivals.js
 create mode 100644 src/Request.js
 create mode 100644 src/Search.js
 create mode 100644 src/Tabs.js

diff --git a/App.js b/App.js
index a271395..8b5bfce 100644
--- a/App.js
+++ b/App.js
@@ -1,400 +1,9 @@
 import React, { Component } from 'react';
-import { ToastAndroid, Button, AsyncStorage, TextInput, ScrollView, Text, View } from 'react-native';
+import { Text, View } from 'react-native';
 
-import moment, { relativeTimeThreshold, updateLocale } from 'moment';
 import Tabs from 'react-native-tabs';
-
-import { styles, element_styles } from './src/styles';
-
-class BusArrival extends Component {
-	constructor(props) {
-		super(props);
-		let time = moment(props.item.arrival);
-		this.state = {
-			id: props.item.id,
-			line: props.item.line,
-			destination: props.item.destination,
-			arrival: time.fromNow(),
-			next: props.item.next
-		};
-	}
-
-	componentDidMount() {
-		this.updateInterval = setInterval(() => {
-			this.setState({arrival: moment(this.props.item.arrival).fromNow()});
-		}, 1e3);
-	}
-
-	componentWillUnmount() {
-		clearInterval(this.updateInterval);
-	}
-
-	render() {
-		return (
-			<View style={[element_styles.container, {backgroundColor: this.state.id % 2 ? '#fafafa' : '#fefefe'}]}>
-				<View style={element_styles.top}>
-					<View>
-						<Text style={element_styles.line}>{this.state.line}</Text>
-						<Text style={element_styles.destination}>{this.state.destination}</Text>
-					</View>
-					<View style={{flex: 1, paddingTop: 3}}>
-						<Text style={element_styles.arrival}>{this.state.arrival}</Text>
-						{ this.state.next.length > 0 ?
-							<Text style={[element_styles.arrival, {fontSize: 11}]}>then {
-								this.state.next.map(i => {
-									return Math.max(Math.floor(moment.duration(moment(i).diff(moment())).asMinutes()), 1);
-							  	}).join(', ')
-							} min</Text>
-							: null }
-					</View>
-				</View>
-			</View>
-		);
-	}
-}
-
-let cache = {};
-const request = {
-	bus: {
-		stop: id => {
-			return new Promise((resolve, reject) => {
-				let i = "bus_stop_" + id;
-				if (cache[i]) {
-					if (cache[i].expires > +new Date()) {
-						return resolve(cache[i].data);
-					}
-				}
-				fetch('https://api.tfl.gov.uk/StopPoint/' + id + '/arrivals')
-					.then(response => response.json())
-					.then(data => {
-						let d = new Date();
-						d.setSeconds(d.getSeconds() + 20);
-						data.updated = new Date();
-						cache[i] = {
-							data,
-							expires: +d
-						};
-						resolve(data);
-					})
-					.catch(reject);
-			});
-		}
-	},
-	search: query => {
-		return new Promise((resolve, reject) => {
-			// avoid stopType = NaptanRailEntrance
-			// if [NaptanOnstreetBusCoachStopCluster, NaptanOnstreetBusCoachStopPair] get child and process
-			// add NaptanPublicBusCoachTram [check indicator] = "e.g. Stop Y"
-			fetch('https://api.tfl.gov.uk/StopPoint/Search/' + query)
-				.then(response => response.json())
-				.then(source => {
-					let search = [];
-					source.matches.forEach(res => {
-						search.push(res.id);
-					});
-					fetch('https://api.tfl.gov.uk/StopPoint/' + search.splice(0, 20).join(','))
-						.then(response => response.json())
-						.then(source => {
-						let data = [];
-						let process = (x, name) => {
-							if (['NaptanOnstreetBusCoachStopCluster', 'NaptanOnstreetBusCoachStopPair'].indexOf(x.stopType) > -1) {
-								x.children.forEach(y => process(y));
-							} else if (x.stopType == 'NaptanPublicBusCoachTram') {
-								let name = x.commonName;
-								if (x.indicator) {
-									name += ` [${x.indicator}]`
-								}
-								data.push({
-									id: x.id,
-									name,
-									//modes: res.modes,
-								});
-							}
-						};
-						if (search.length > 1) {
-							source.forEach(res => {
-								process(res);
-							});
-						} else {
-							process(source);
-						}
-						resolve(data);
-					})
-					.catch(reject);
-				})
-				.catch(reject);
-		});
-	}
-};
-
-class BusArrivals extends Component {
-	constructor(props) {
-		super(props);
-		this.state = {
-			name: props.name,
-			id: props.id,
-			data: [],
-			updated: 'never',
-			visible: true,
-		};
-	}
-
-	render() {
-		if (!this.state.visible) return null;
-		return (
-			<View>
-				<Text style={element_styles.header}>{this.state.name}</Text>
-				<Text style={styles.status}>Updated {this.state.updated}.</Text>
-				{Object.keys(this.state.data).map(i => {
-          			return (<BusArrival item={this.state.data[i]} key={this.state.data[i].key} />);
-				})}
-				<View style={styles.button}>
-					<Button
-						onPress={() => {
-							AsyncStorage.getItem('myStops').then(x => {
-								let arr = JSON.parse(x);
-								let j = -1;
-								for (let i=0;i<arr.length;i++) {
-									if (arr[i].id == this.state.id) j = i;
-								}
-								if (j > -1) {
-									arr.splice(j, 1);
-									AsyncStorage.setItem('myStops', JSON.stringify(arr)).then(() => {
-										!this.isCancelled && this.setState({visible: false});
-										ToastAndroid.show('Removed stop!', ToastAndroid.SHORT);
-									});
-								} else {
-									ToastAndroid.show('Could not remove stop!', ToastAndroid.SHORT);
-								}
-							}).catch(() => {
-								ToastAndroid.show('Encountered an error!', ToastAndroid.SHORT);
-							});
-						}}
-						title="Remove this stop"
-						color='#d3390a'
-					/>
-				</View>
-			</View>
-		);
-	}
-
-	componentDidMount() {
-		this.isCancelled = false;
-		this.updateInterval = setInterval(() => this.update(), 3e4);
-		this.counterInterval = setInterval(() => {
-			this.setState({
-				updated: this.state.updateTime ? Math.floor(moment(new Date()).diff(this.state.updateTime) / 1000) + ' seconds ago' : 'never'
-			});
-		}, 1e3);
-		return this.update(this);
-	}
-
-	componentWillUnmount() {
-		this.isCancelled = true;
-		clearInterval(this.updateInterval);
-		clearInterval(this.counterInterval);
-	}
-
-	update() {
-		if (!this.state.visible) return;
-		return request.bus.stop(this.state.id)
-			.then(data => {
-				let list = [];
-				data.forEach(x => {
-					list.push({
-						key: x.id,
-						line: x.lineName,
-						destination: x.destinationName,
-						arrival: x.expectedArrival
-					});
-				});
-				list.sort((a, b) => {
-					return new Date(a.arrival) > new Date(b.arrival);
-				});
-				let parse = {};
-				list.forEach((x, i) => {
-					x.id = i;
-					if (parse[x.line + x.destination]) {
-						parse[x.line + x.destination].next.push(x.arrival);
-					} else {
-						x.next = [];
-						parse[x.line + x.destination] = x;
-					}
-				});
-				list = [];
-				Object.keys(parse).forEach(x => {
-					list.push(parse[x]);
-				});
-				!this.isCancelled && this.setState({
-					data: list,
-					loaded: 2,
-					updateTime: data.updated,
-					updated: Math.floor(moment(new Date()).diff(this.state.updateTime) / 1000) + ' seconds ago'
-				});
-			});
-	}
-}
-
-class TabHome extends Component {
-	constructor(props) {
-		super(props);
-	}
-
-	render() {
-		return (
-			<View style={{flex: 1, padding: 24}}>
-				<Text style={{fontSize: 20, textAlign: 'center'}}>Select a tab above to get started!</Text>
-				<Text style={{fontSize: 16, color: '#1a1a1a'}}>⦿ My Stops - quick glance at favourites</Text>
-				<Text style={{fontSize: 16, color: '#1a1a1a'}}>⦿ Search - simple stop search</Text>
-				<Text style={{fontSize: 16, color: '#1a1a1a'}}>⦿ About - app info and libraries used</Text>
-			</View>
-		);
-	}
-}
-
-class TabAbout extends Component {
-	constructor(props) {
-		super(props);
-	}
-
-	render() {
-		return (
-			<View style={{flex: 1, padding: 24}}>
-				<Text style={{fontSize: 20, textAlign: 'center'}}>About</Text>
-				<Text style={{fontSize: 16, color: '#1a1a1a'}}>Created by Paul Makles (insrt.uk)</Text>
-				<Text style={{fontSize: 16, color: '#1a1a1a'}}>Libraries used:</Text>
-				<Text style={{fontSize: 16, color: '#1a1a1a'}}>⦿ react native</Text>
-				<Text style={{fontSize: 16, color: '#1a1a1a'}}>⦿ moment.js</Text>
-				<Text style={{fontSize: 16, color: '#1a1a1a'}}>⦿ react tabs</Text>
-			</View>
-		);
-	}
-}
-
-class TabMyStops extends Component {
-	constructor(props) {
-		super(props);
-		this.state = {
-			stops: []
-		};
-	}
-
-	componentDidMount() {
-		this.isCancelled = false;
-		AsyncStorage.getItem('myStops').then(x => {
-			if (x == null) return;
-			!this.isCancelled && this.setState({
-				stops: JSON.parse(x)
-			});
-		}).catch(() => {});
-	}
-
-	componentWillUnmount() {
-		this.isCancelled = true;
-	}
-
-	render() {
-		return (
-			<ScrollView style={styles.list}>
-				{this.state.stops.map(i => {
-					return (<BusArrivals name={i.name} id={i.id} key={i.id} />);
-				})}
-			</ScrollView>
-		);
-	}
-}
-
-class SearchEntry extends Component {
-	constructor(props) {
-		super(props);
-		this.state = {
-			i: props.data.i,
-			id: props.data.id,
-			name: props.data.name
-		};
-	}
-
-	render() {
-		return (
-			<View style={[element_styles.container, {backgroundColor: this.state.i % 2 ? '#fafafa' : '#fefefe'}]}>
-				<Text style={element_styles.destination}>{this.state.name}</Text>
-				<Button
-					onPress={() => {
-						let d = {id: this.state.id, name: this.state.name};
-						AsyncStorage.getItem('myStops').then(x => {
-							if (x == null) return AsyncStorage.setItem('myStops', JSON.stringify([d])).then(() => {
-								ToastAndroid.show('Added to your stops!', ToastAndroid.SHORT);
-							});
-							let arr = JSON.parse(x);
-							let found = false;
-							arr.forEach(y => {
-								if (y.id == this.state.id) found = true;
-							});
-							if (found) {
-								ToastAndroid.show('Already added!', ToastAndroid.SHORT);
-							} else {
-								arr.push(d);
-								AsyncStorage.setItem('myStops', JSON.stringify(arr)).then(() => {
-									ToastAndroid.show('Added to your stops!', ToastAndroid.SHORT);
-								});
-							}
-						}).catch(() => {
-							ToastAndroid.show('Encountered an error!', ToastAndroid.SHORT);
-						});
-					}}
-					title="Add"
-				/>
-			</View>
-		);
-	}
-}
-
-class TabSearch extends Component {
-	constructor(props) {
-		super(props);
-		this.state = {
-			query: '',
-			searching: false,
-			results: [],
-		};
-	}
-
-	componentDidMount() {
-		this.isCancelled = false;
-	}
-
-	search() {
-		if (this.state.query.length < 1) return this.setState({results: []});
-		this.setState({searching: true});
-		request.search(this.state.query).then(results => this.setState({results, searching: false}));
-	}
-
-	componentWillUnmount() {
-		this.isCancelled = true;
-	}
-
-	render() {
-		return (
-			<View style={styles.child}>
-				<View style={styles.header_addon}>
-					<TextInput
-						style={{position: 'relative', left: -11, height: 40, backgroundColor: 'white', color: 'black', paddingLeft: 4, paddingRight: 4}}
-						placeholder="Search for a bus stop."
-						editable={!this.state.searching}
-						onChangeText={(query) => this.setState({query})}
-						onSubmitEditing={() => this.search()}
-					/>
-				</View>
-				<Text style={styles.status}>{this.state.searching ? 'Searching, please wait...' : (this.state.results.length > 0 ? `Displaying ${this.state.results.length} results.` : `Waiting for input...`)}</Text>
-				<ScrollView style={styles.list}>
-					{Object.keys(this.state.results).map(i => {
-	          			return (<SearchEntry key={this.state.results[i].id} data={this.state.results[i]} />);
-					})}
-				</ScrollView>
-			</View>
-		);
-	}
-}
+import { styles } from './src/Styles';
+import { TabHome, TabAbout, TabSearch, TabMyStops } from './src/Tabs';
 
 export default class App extends Component {
 	constructor(props) {
diff --git a/app.json b/app.json
index bbbd460..697a2c0 100644
--- a/app.json
+++ b/app.json
@@ -8,7 +8,7 @@
     "platforms": ["ios", "android"],
     "version": "1.0.2",
     "orientation": "default",
-    "icon": "./assets/icon.png",
+	"icon": "./assets/icon.png",
     "splash": {
       "image": "./assets/splash.png",
       "resizeMode": "contain",
diff --git a/src/Arrivals.js b/src/Arrivals.js
new file mode 100644
index 0000000..f8666d0
--- /dev/null
+++ b/src/Arrivals.js
@@ -0,0 +1,195 @@
+import React, { Component } from 'react';
+import { ToastAndroid, Button, AsyncStorage, ScrollView, Text, View } from 'react-native';
+
+import { styles, element_styles } from './Styles';
+import moment from 'moment';
+
+import { BusStop } from './Request';
+
+export class Entry extends Component {
+	constructor(props) {
+		super(props);
+		let time = moment(props.item.arrival);
+		this.state = {
+			id: props.item.id,
+			line: props.item.line,
+			destination: props.item.destination,
+			arrival: time.fromNow(),
+			next: props.item.next
+		};
+	}
+
+	componentDidMount() {
+		this.updateInterval = setInterval(() => {
+			this.setState({arrival: moment(this.props.item.arrival).fromNow()});
+		}, 1e3);
+	}
+
+	componentWillUnmount() {
+		clearInterval(this.updateInterval);
+	}
+
+	render() {
+		return (
+			<View style={[element_styles.container, {backgroundColor: this.state.id % 2 ? '#f6f6f6' : '#fefefe'}]}>
+				<View style={element_styles.top}>
+					<View>
+						<Text style={element_styles.line}>{this.state.line}</Text>
+						<Text style={element_styles.destination}>{this.state.destination}</Text>
+					</View>
+					<View style={{flex: 1, paddingTop: 3}}>
+						<Text style={element_styles.arrival}>{this.state.arrival}</Text>
+						{ this.state.next.length > 0 ?
+							<Text style={[element_styles.arrival, {fontSize: 11}]}>then {
+								this.state.next.map(i => {
+									return Math.max(Math.floor(moment.duration(moment(i).diff(moment())).asMinutes()), 1);
+							  	}).join(', ')
+							} min</Text>
+							: null }
+					</View>
+				</View>
+			</View>
+		);
+	}
+}
+
+class BusArrivals extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			name: props.name,
+			id: props.id,
+			data: [],
+			updated: 'never',
+			visible: true,
+		};
+	}
+
+	render() {
+		if (!this.state.visible) return null;
+		return (
+			<View>
+				<Text style={element_styles.header}>{this.state.name}</Text>
+				<Text style={styles.status}>Updated {this.state.updated}.</Text>
+				{Object.keys(this.state.data).map(i => {
+          			return (<Entry item={this.state.data[i]} key={this.state.data[i].key} />);
+				})}
+				<View style={styles.button}>
+					<Button
+						onPress={() => {
+							AsyncStorage.getItem('myStops').then(x => {
+								let arr = JSON.parse(x);
+								let j = -1;
+								for (let i=0;i<arr.length;i++) {
+									if (arr[i].id == this.state.id) j = i;
+								}
+								if (j > -1) {
+									arr.splice(j, 1);
+									AsyncStorage.setItem('myStops', JSON.stringify(arr)).then(() => {
+										!this.isCancelled && this.setState({visible: false});
+										ToastAndroid.show('Removed stop!', ToastAndroid.SHORT);
+									});
+								} else {
+									ToastAndroid.show('Could not remove stop!', ToastAndroid.SHORT);
+								}
+							}).catch(() => {
+								ToastAndroid.show('Encountered an error!', ToastAndroid.SHORT);
+							});
+						}}
+						title="Remove this stop"
+						color='#d3390a'
+					/>
+				</View>
+			</View>
+		);
+	}
+
+	componentDidMount() {
+		this.isCancelled = false;
+		this.updateInterval = setInterval(() => this.update(), 3e4);
+		this.counterInterval = setInterval(() => {
+			this.setState({
+				updated: this.state.updateTime ? Math.floor(moment(new Date()).diff(this.state.updateTime) / 1000) + ' seconds ago' : 'never'
+			});
+		}, 1e3);
+		return this.update(this);
+	}
+
+	componentWillUnmount() {
+		this.isCancelled = true;
+		clearInterval(this.updateInterval);
+		clearInterval(this.counterInterval);
+	}
+
+	update() {
+		if (!this.state.visible) return;
+		return BusStop(this.state.id)
+			.then(data => {
+				let list = [];
+				data.forEach(x => {
+					list.push({
+						key: x.id,
+						line: x.lineName,
+						destination: x.destinationName,
+						arrival: x.expectedArrival
+					});
+				});
+				list.sort((a, b) => {
+					return new Date(a.arrival) > new Date(b.arrival);
+				});
+				let parse = {};
+				list.forEach((x, i) => {
+					x.id = i;
+					if (parse[x.line + x.destination]) {
+						parse[x.line + x.destination].next.push(x.arrival);
+					} else {
+						x.next = [];
+						parse[x.line + x.destination] = x;
+					}
+				});
+				list = [];
+				Object.keys(parse).forEach(x => {
+					list.push(parse[x]);
+				});
+				!this.isCancelled && this.setState({
+					data: list,
+					loaded: 2,
+					updateTime: data.updated,
+					updated: Math.floor(moment(new Date()).diff(this.state.updateTime) / 1000) + ' seconds ago'
+				});
+			});
+	}
+}
+
+export class TabMyStops extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			stops: []
+		};
+	}
+
+	componentDidMount() {
+		this.isCancelled = false;
+		AsyncStorage.getItem('myStops').then(x => {
+			if (x == null) return;
+			!this.isCancelled && this.setState({
+				stops: JSON.parse(x)
+			});
+		}).catch(() => {});
+	}
+
+	componentWillUnmount() {
+		this.isCancelled = true;
+	}
+
+	render() {
+		return (
+			<ScrollView style={styles.list}>
+				{this.state.stops.map(i => {
+					return (<BusArrivals name={i.name} id={i.id} key={i.id} />);
+				})}
+			</ScrollView>
+		);
+	}
+}
\ No newline at end of file
diff --git a/src/Request.js b/src/Request.js
new file mode 100644
index 0000000..9122db6
--- /dev/null
+++ b/src/Request.js
@@ -0,0 +1,66 @@
+function resolve(...args) {
+	return 'https://api.tfl.gov.uk/' + args.join('');
+}
+
+let cache = {};
+
+export async function BusStop(id) {
+	let i = "bus_stop_" + id;
+	if (cache[i]) {
+		if (cache[i].expires > +new Date()) {
+			return cache[i].data;
+		}
+	}
+	let response = await fetch(resolve('StopPoint/', id, '/arrivals'));
+	let data = await response.json();
+
+	let d = new Date();
+	d.setSeconds(d.getSeconds() + 20);
+	data.updated = new Date();
+	cache[i] = {
+		data,
+		expires: +d
+	};
+
+	return data;
+}
+
+export async function Search(query) {
+	let res = await fetch(resolve('StopPoint/Search/', query));
+	let source = await res.json();
+
+	let search = [];
+	source.matches.forEach(res => {
+		search.push(res.id);
+	});
+
+	res = await fetch(resolve('StopPoint/' + search.splice(0, 20).join(',')));
+	source = await res.json();
+
+	let data = [];
+	let process = x => {
+		if (['NaptanOnstreetBusCoachStopCluster', 'NaptanOnstreetBusCoachStopPair'].indexOf(x.stopType) > -1) {
+			x.children.forEach(y => process(y));
+		} else if (x.stopType == 'NaptanPublicBusCoachTram') {
+			let name = x.commonName;
+			if (x.indicator) {
+				name += ` [${x.indicator}]`
+			}
+			data.push({
+				id: x.id,
+				name,
+				//modes: res.modes,
+			});
+		}
+	};
+	
+	if (Array.isArray(search)) {
+		source.forEach(res => {
+			process(res);
+		});
+	} else {
+		process(source);
+	}
+
+	return data;
+}
\ No newline at end of file
diff --git a/src/Search.js b/src/Search.js
new file mode 100644
index 0000000..df384d5
--- /dev/null
+++ b/src/Search.js
@@ -0,0 +1,98 @@
+import React, { Component } from 'react';
+import { ToastAndroid, Button, AsyncStorage, TextInput, ScrollView, Text, View } from 'react-native';
+
+import { styles, element_styles } from './Styles';
+
+import { Search } from './Request';
+
+class SearchEntry extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			i: props.data.i,
+			id: props.data.id,
+			name: props.data.name
+		};
+	}
+
+	render() {
+		return (
+			<View style={[element_styles.container, {backgroundColor: this.state.i % 2 ? '#fafafa' : '#fefefe'}]}>
+				<Text style={element_styles.destination}>{this.state.name}</Text>
+				<Button
+					onPress={() => {
+						let d = {id: this.state.id, name: this.state.name};
+						AsyncStorage.getItem('myStops').then(x => {
+							if (x == null) return AsyncStorage.setItem('myStops', JSON.stringify([d])).then(() => {
+								ToastAndroid.show('Added to your stops!', ToastAndroid.SHORT);
+							});
+							let arr = JSON.parse(x);
+							let found = false;
+							arr.forEach(y => {
+								if (y.id == this.state.id) found = true;
+							});
+							if (found) {
+								ToastAndroid.show('Already added!', ToastAndroid.SHORT);
+							} else {
+								arr.push(d);
+								AsyncStorage.setItem('myStops', JSON.stringify(arr)).then(() => {
+									ToastAndroid.show('Added to your stops!', ToastAndroid.SHORT);
+								});
+							}
+						}).catch(() => {
+							ToastAndroid.show('Encountered an error!', ToastAndroid.SHORT);
+						});
+					}}
+					title="Add"
+				/>
+			</View>
+		);
+	}
+}
+
+export class TabSearch extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			query: '',
+			searching: false,
+			results: [],
+		};
+	}
+
+	componentDidMount() {
+		this.isCancelled = false;
+	}
+
+	search() {
+		if (this.state.query.length < 1) return this.setState({results: []});
+		this.setState({searching: true});
+		Search(this.state.query).then(results => this.setState({results, searching: false}));
+	}
+
+	componentWillUnmount() {
+		this.isCancelled = true;
+	}
+
+	render() {
+		return (
+			<View style={styles.child}>
+				<View style={styles.header_extension}>
+					<TextInput
+						style={{position: 'relative', left: -11, height: 40, backgroundColor: 'white', color: 'black', paddingLeft: 4, paddingRight: 4}}
+						placeholder="Search for a bus stop."
+						editable={!this.state.searching}
+						onChangeText={(query) => this.setState({query})}
+						onSubmitEditing={() => this.search()}
+					/>
+				</View>
+				<Text style={styles.status}>{this.state.searching ? 'Searching, please wait...' : (this.state.results.length > 0 ? `Displaying ${this.state.results.length} results.` : `Waiting for input...`)}</Text>
+				<ScrollView style={styles.list}>
+					{Object.keys(this.state.results).map(i => {
+	          			return (<SearchEntry key={this.state.results[i].id} data={this.state.results[i]} />);
+					})}
+				</ScrollView>
+			</View>
+		);
+	}
+}
\ No newline at end of file
diff --git a/src/Tabs.js b/src/Tabs.js
new file mode 100644
index 0000000..68b9bee
--- /dev/null
+++ b/src/Tabs.js
@@ -0,0 +1,44 @@
+import React, { Component } from 'react';
+import { View, Text } from 'react-native';
+
+export class TabHome extends Component {
+	constructor(props) {
+		super(props);
+	}
+
+	render() {
+		return (
+			<View style={{flex: 1, padding: 24}}>
+				<Text style={{fontSize: 20, textAlign: 'center'}}>Select a tab above to get started!</Text>
+				<Text style={{fontSize: 16, color: '#1a1a1a'}}>⦿ My Stops - quick glance at favourites</Text>
+				<Text style={{fontSize: 16, color: '#1a1a1a'}}>⦿ Search - simple stop search</Text>
+				<Text style={{fontSize: 16, color: '#1a1a1a'}}>⦿ About - app info and libraries used</Text>
+			</View>
+		);
+	}
+}
+
+export class TabAbout extends Component {
+	constructor(props) {
+		super(props);
+	}
+
+	render() {
+		return (
+			<View style={{flex: 1, padding: 24}}>
+				<Text style={{fontSize: 20, textAlign: 'center'}}>About</Text>
+				<Text style={{fontSize: 16, color: '#1a1a1a'}}>Created by Paul Makles (insrt.uk)</Text>
+				<Text style={{fontSize: 16, color: '#1a1a1a'}}>Libraries used:</Text>
+				<Text style={{fontSize: 16, color: '#1a1a1a'}}>⦿ react native</Text>
+				<Text style={{fontSize: 16, color: '#1a1a1a'}}>⦿ moment.js</Text>
+				<Text style={{fontSize: 16, color: '#1a1a1a'}}>⦿ react tabs</Text>
+			</View>
+		);
+	}
+}
+
+import { TabMyStops } from './Arrivals';
+module.exports.TabMyStops = TabMyStops;
+
+import { TabSearch } from './Search';
+module.exports.TabSearch = TabSearch;
\ No newline at end of file
diff --git a/src/styles.js b/src/styles.js
index 17021d7..6b11e30 100644
--- a/src/styles.js
+++ b/src/styles.js
@@ -1,9 +1,11 @@
 import { StyleSheet } from 'react-native';
 
-const element_styles = StyleSheet.create({
+export const element_styles = StyleSheet.create({
 	container: {
 		width: '100%',
-		padding: 4,
+		padding: 2,
+		paddingLeft: 8,
+		paddingRight: 8
 	},
 	top: {
 		padding: 6,
@@ -12,15 +14,15 @@ const element_styles = StyleSheet.create({
 	},
 	line: {
 		fontWeight: '900',
-		fontSize: 26,
+		fontSize: 22,
 		textAlign: 'left',
 	},
 	destination: {
 		paddingLeft: 5,
-		fontSize: 18,
+		fontSize: 14,
 	},
 	arrival: {
-		padding: 4,
+		padding: 2,
 		fontSize: 14,
 		textAlign: 'right',
 	},
@@ -29,13 +31,13 @@ const element_styles = StyleSheet.create({
 		color: 'white',
 		fontWeight: '200',
 		textAlign: 'left',
-		fontSize: 24,
+		fontSize: 22,
 		padding: 4,
-		paddingLeft: 8
+		paddingLeft: 16
 	}
 });
 	
-const styles = StyleSheet.create({
+export const styles = StyleSheet.create({
 	container: {
 		display: 'flex',
 		flex: 1,
@@ -50,7 +52,7 @@ const styles = StyleSheet.create({
 		width: '100%',
 		backgroundColor: '#d3390a',
 	},
-	header_addon: {
+	header_extension: {
 		paddingLeft: 22,
 		paddingBottom: 8,
 		width: '100%',
@@ -78,6 +80,4 @@ const styles = StyleSheet.create({
 	button: {
 		padding: 6
 	}
-});
-
-module.exports = { element_styles, styles };
\ No newline at end of file
+});
\ No newline at end of file
-- 
GitLab