BartLiveMobile is a mobile app that displays real-time BART departures with the data received from BART API.
Station List | Real Time Departures | Map View | System Maps |
---|---|---|---|
Station List | Real Time Departures | Map View | System Maps |
---|---|---|---|
- React Native
- React Navigation
- Redux
- React Native Maps
- React Hooks
- Expo
- Google Maps API
- Bart API
- Javascript
The combination of Redux + React Navigation makes things very easy. Rather than passing params around with () => props.navigation.navigate('MyScreen', params: {})
, using useSelector hook is a great way to pull whatever is needed from state.
User location, station locations and real time departures are placed in Redux store.
import { combineReducers } from "redux";
import userLocationReducer from './userLocationReducer';
import trainDepartureReducer from './trainDepartureReducer';
import stationLocationReducer from "./stationLocationReducer";
const rootReducer = combineReducers({
userLocation: userLocationReducer,
trainDepartures: trainDepartureReducer,
stationLocations: stationLocationReducer
})
export default rootReducer;
Using React Navigation for the first time, it was challenging to combine multiple navigations; but I am happy with the final product. The bottom navigator is setup in App.js file:
const TabNavigator = createBottomTabNavigator(
{
"Station List": {
screen: AllStationsScreen,
navigationOptions: {
//...
}
},
"Live Map": {
screen: LiveMapScreen,
navigationOptions: {
//...
}
},
"System Map": {
screen: SystemScreen,
navigationOptions: {
//...
}
},
About: {
screen: AboutScreen,
navigationOptions: {
//...
}
}
},
{
initialRouteName: "Station List"
}
);
const AppContainer = createAppContainer(TabNavigator);
The app also has a stack navigator to display train details screen after pressing on a station from the list:
const AllStationsScreen = createStackNavigator(
{
StationList: StationListScreen,
StationDetails: StationDetailsScreen
},
{
initialRouteName: "StationList"
}
);
System Map Tab features a top tab navigator for weekday vs. weekend maps:
const SystemMapNavigator = createMaterialTopTabNavigator(
{
"Weekday & Saturday": {
screen: WeekAndSatScreen,
navigationOptions: {
swipeEnabled: false
}
},
Sunday: {
screen: SundayScreen,
navigationOptions: {
swipeEnabled: false
}
}
},
{
tabBarComponent: SafeAreaMaterialTopTabBar
}
);
- The app asks for user permission to track their location. The location is then dispatched to Redux store to be used for the calculation of closest stations and centering the map view around the coordinates.
const getLocation = async () => {
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== "granted") {
dispatch({
type: "RECEIVE_USER_LOCATION",
payload: { coords: { latitude: 37.792874, longitude: -122.39703 } }
});
}
let location = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High
});
dispatch({
type: "RECEIVE_USER_LOCATION",
payload: location
});
};
- Receiving the real time data is done by a simple fetch function. The response is then dispatched to Redux store.
const fetchTrainDepartures = () => {
// setStationList(responseJson.root.station);
// setLastUpdate(responseJson.root.time);)
// call BART API
fetch(
"http://api.bart.gov/api/etd.aspx?cmd=etd&orig=ALL&key=MW9S-E7SL-26DU-VV8V&json=y"
)
.then(response => response.json())
.then(responseJson =>
dispatch({
type: "RECEIVE_TRAIN_DEPARTURE_DATA",
payload: responseJson.root.station
})
)
.catch(error => {
console.log(error);
});
};
The app receives new data every 5 seconds.
useEffect(() => {
const intervalId = setInterval(fetchTrainDepartures, 5000);
return () => clearInterval(intervalId);
});
After receiving user location and station locations and dispatching them to redux store; the calculation is done with the help of geolib library.
const calculateDistance = () => {
// const userLocation = useSelector(state => state.userLocation);
// const stations = useSelector(state => state.stationLocations);
return stations.map(station => {
return {
...station,
distance: convertDistance(
getDistance(
{
latitude: station.gtfs_latitude,
longitude: station.gtfs_longitude
},
{
latitude: String(userLocation.coords.latitude),
longitude: String(userLocation.coords.longitude)
}
),
"mi"
)
};
});
};
Built with ❤️ by Onur Eker - 2019.