Merge remote-tracking branch 'origin/master'

This commit is contained in:
Erick Clark 2018-05-27 09:21:25 -05:00
commit d30e3fe122
11 changed files with 191 additions and 43 deletions

View file

@ -91,7 +91,7 @@ android {
applicationId "com.aretherecookies"
minSdkVersion 16
targetSdkVersion 22
versionCode 9
versionCode 12
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"

View file

@ -1 +1 @@
{"web":{"client_id":"648700523612-jbif6e356rc13pbcmrmc7gk212eelgcp.apps.googleusercontent.com","project_id":"august-copilot-171122","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://accounts.google.com/o/oauth2/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"U44sJzIyL0yCreGpJ1v1saNZ"}}
{"web":{"client_id":"648700523612-lm35m5d7m7k0sdutqmatbfhq2qsnd5if.apps.googleusercontent.com","project_id":"august-copilot-171122","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://accounts.google.com/o/oauth2/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"U44sJzIyL0yCreGpJ1v1saNZ"}}

View file

@ -1,6 +1,6 @@
//@flow
import React, { Component } from 'react';
import { View, StatusBar } from 'react-native';
import { View, StatusBar, AsyncStorage } from 'react-native'; // eslint-disable-line
import { ThemeProvider } from 'react-native-material-ui';
import theme from './ui-theme';
import DrawerMenu from './pages/DrawerMenu';
@ -16,6 +16,7 @@ import CreateFoodItem from './pages/CreateFoodItem';
import AuthManager from './AuthManager';
import LoginPage from './pages/LoginPage';
import LandingPage from './pages/LandingPage';
import ZipcodePage from './pages/ZipcodePage';
setObservableConfig(rxjsconfig);
@ -32,6 +33,11 @@ export default class App extends Component {
this._drawer.open();
};
componentDidMount() {
// uncomment this to force show the landing/zipcode screens
// AsyncStorage.removeItem('zipcode');
}
render() {
return (
<NativeRouter>
@ -53,6 +59,7 @@ export default class App extends Component {
<Route path="/foodItem/:id" component={FoodItemDetail} />
<Route path="/place/:id" component={PlaceDetail} />
<Route path="/login" component={LoginPage} />
<Route path="/zipcode" component={ZipcodePage} />
<Route
path="/createFoodItem"
render={() => {

35
js/apis/GoogleMapsApi.js Normal file
View file

@ -0,0 +1,35 @@
// @flow
import { GoogleAPIKey } from '../constants/AppConstants';
import { path } from 'ramda';
const url = 'https://maps.googleapis.com/maps/api/geocode/json';
type Location = {
lat: number,
lng: number,
};
type AddressComponent = {
long_name: string,
short_name: string,
types: Array<string>,
};
type GeocodeResult = {
results: Array<{
address_components: Array<AddressComponent>,
formatted_address: string,
geometry: {
location: Location,
},
place_id: string,
types: Array<string>,
}>,
};
export const getCoordsFromZip = async (zip: string): Promise<Location> => {
const res: GeocodeResult = await (await fetch(
`${url}?key=${GoogleAPIKey}&address=${zip}`
)).json();
return path(['results', 0, 'geometry', 'location'], res) || { lat: 0, lng: 0 };
};

View file

@ -1,9 +1,9 @@
// @flow
import { type GooglePlaceObj } from '../records/PlaceRecord';
import { GoogleAPIKey } from '../constants/AppConstants';
const apiKey = 'AIzaSyBfMm1y6JayCbXrQmgAG1R3ka4ZOJno_5E';
const placesUrl = `https://maps.googleapis.com/maps/api/place/details/json?key=${apiKey}`;
const photosUrl = `https://maps.googleapis.com/maps/api/place/photo?key=${apiKey}`;
const placesUrl = `https://maps.googleapis.com/maps/api/place/details/json?key=${GoogleAPIKey}`;
const photosUrl = `https://maps.googleapis.com/maps/api/place/photo?key=${GoogleAPIKey}`;
type GooglePlaceDetailsResponse = { error_message: ?string, result: GooglePlaceObj };

View file

@ -1,5 +1,7 @@
// @flow
import { emitter } from '../streams/LocationStream';
import { getCoordsFromZip } from './GoogleMapsApi';
import { AsyncStorage } from 'react-native';
export const getCurrentPosition = () => {
return new Promise((resolve, reject) => {
@ -20,15 +22,32 @@ export const getCurrentPosition = () => {
};
// TODO actually implement geolocation for zipcode into lat/lng
export const getPositionFromZip = () => {
const dummyPos: any = {
export const getPositionFromZip = async (zip: string) => {
const { lat: latitude, lng: longitude } = await getCoordsFromZip(zip);
const pos: any = {
coords: {
latitude: 30.267,
longitude: -97.7485,
latitude,
longitude,
},
};
return new Promise(resolve => {
emitter(dummyPos);
resolve(dummyPos);
});
emitter(pos);
return pos;
};
export const getLocation = async (zipcode: string) => {
if (zipcode) {
AsyncStorage.setItem('zipcode', zipcode);
}
const currentZip = zipcode || (await AsyncStorage.getItem('zipcode'));
if (currentZip && currentZip !== 'usegps') {
getPositionFromZip(currentZip);
} else {
AsyncStorage.setItem('zipcode', 'usegps');
getCurrentPosition();
}
};

View file

@ -8,3 +8,5 @@ export const BASE_URL = 'aretherecookies.herokuapp.com';
// @stouthaus
// export const BASE_URL = '192.168.1.169:3000';
export const GoogleAPIKey = 'AIzaSyBfMm1y6JayCbXrQmgAG1R3ka4ZOJno_5E';

View file

@ -1,13 +1,31 @@
// @flow
import React from 'react';
import { View, Text, Image } from 'react-native';
import { View, Text, Image, AsyncStorage } from 'react-native';
import atcCookieImage from '../../static/atc-cookie-logo.png';
import { Link } from 'react-router-native';
import RouterButton from 'react-router-native-button';
import { compose, withHandlers, withState } from 'recompose';
import { withRouterContext } from '../enhancers/routeEnhancers';
import theme from '../ui-theme';
const LandingPage = () => {
type Props = {
skipIfAlreadyChosen: () => void,
setLoading: (val: boolean) => void,
loading: boolean,
router: {
history: {
replace: (route: string) => void,
},
},
};
const LandingPage = ({ skipIfAlreadyChosen, loading }: Props) => {
if (loading) {
skipIfAlreadyChosen();
return null;
}
return (
<View
style={{
@ -17,11 +35,15 @@ const LandingPage = () => {
flex: 1,
height: '100%',
}}>
<Image source={atcCookieImage} style={{ height: 320, marginBottom: 10, }} resizeMode="contain" />
<Text style={{ fontSize: 24, marginBottom: 30, textAlign: 'center', width: 330, }}>
<Image
source={atcCookieImage}
style={{ height: 320, marginBottom: 10 }}
resizeMode="contain"
/>
<Text style={{ fontSize: 24, marginBottom: 30, textAlign: 'center', width: 330 }}>
We need to use your location to bring you the best experience possible.
</Text>
<View style={{ width: 275, marginBottom: 30, }}>
<View style={{ width: 275, marginBottom: 30 }}>
<RouterButton
to="/list/food?positionBy=location"
replace
@ -29,11 +51,26 @@ const LandingPage = () => {
color={theme.palette.primaryColor}
/>
</View>
<Link to="/list/food?positionBy=zip" replace>
<Text style={{ color: theme.palette.primaryColor, marginBottom: 20, fontSize: 16, }}>I'd rather not.</Text>
<Link to="/zipcode" replace>
<Text style={{ color: theme.palette.primaryColor, marginBottom: 20, fontSize: 16 }}>
I'd rather not.
</Text>
</Link>
</View>
);
};
export default LandingPage;
export default compose(
withRouterContext,
withState('loading', 'setLoading', true),
withHandlers({
skipIfAlreadyChosen: (props: Props) => async () => {
const zipcode = await AsyncStorage.getItem('zipcode');
if (zipcode) {
props.router.history.replace('/list/food');
} else {
props.setLoading(false);
}
},
})
)(LandingPage);

View file

@ -6,7 +6,7 @@ import ScrollableTabView from 'react-native-scrollable-tab-view';
import theme from '../ui-theme.js';
import { type RoutingContextFlowTypes, routingContextPropTypes } from '../routes';
import { getSearch } from '../helpers/RouteHelpers';
import { getCurrentPosition, getPositionFromZip } from '../apis/PositionApi';
import { getLocation } from '../apis/PositionApi';
const tabs = ['food', 'places'];
@ -37,22 +37,10 @@ class List extends Component {
// TODO convert this component to SFC using recompose
// also, figure out a less lifecyle-y way to do this request
componentDidMount() {
this.getLocation();
const { zipcode } = getSearch(this.context);
getLocation(zipcode);
}
getLocation = () => {
const { positionBy = 'zip' } = getSearch(this.context);
try {
if (positionBy === 'location') {
return getCurrentPosition();
} else {
return getPositionFromZip();
}
} catch (error) {
console.log(error); // eslint-disable-line
}
};
updateTabRoute = ({ i }: { i: number }) => {
const currentTab = getTabIndex(this.props);
@ -94,8 +82,8 @@ class List extends Component {
prerenderingSiblingsNumber={Infinity}
onChangeTab={this.updateTabRoute}
initialPage={getTabIndex(this.props)}>
<Food tabLabel="FOOD" onRefresh={this.getLocation} />
<Places tabLabel="PLACES" onRefresh={this.getLocation} />
<Food tabLabel="FOOD" onRefresh={getLocation} />
<Places tabLabel="PLACES" onRefresh={getLocation} />
</ScrollableTabView>
);
}

View file

@ -1,7 +1,7 @@
//@flow
import React from 'react';
import { View, Text, Button } from 'react-native';
import { withHandlers, compose } from 'recompose';
import { withHandlers, compose, withState } from 'recompose';
import { withRouterContext } from '../enhancers/routeEnhancers';
import AuthManager, { type AUTH_PROVIDER } from '../AuthManager';
import queryString from 'query-string';
@ -10,8 +10,18 @@ import theme from '../ui-theme';
type Props = {
authUser: (provider: string) => () => Promise<void>,
error: string,
setError: (err: string) => void,
router: {
history: {
replace: (url: string) => void,
location: {
search: string,
},
},
},
};
const LoginPageComponent = ({ authUser }: Props) => {
const LoginPageComponent = ({ authUser, error }: Props) => {
return (
<View style={{ flexDirection: 'column', alignItems: 'center' }}>
<Text style={theme.loginPage.titleStyle}>Sign In</Text>
@ -29,14 +39,18 @@ const LoginPageComponent = ({ authUser }: Props) => {
color={theme.palette.google}
/>
</View>
{error && <Text>Login Failed! {error}</Text>}
</View>
);
};
const LoginPage = compose(
withRouterContext,
withState('error', 'setError', null),
withHandlers({
authUser: ({ router: { history } }) => (provider: AUTH_PROVIDER) => async () => {
authUser: ({ router: { history }, setError }: Props) => (
provider: AUTH_PROVIDER
) => async () => {
const { returnto = '/' } = queryString.parse(history.location.search);
try {
if (!AuthManager.isLoggedIn()) {
@ -44,7 +58,11 @@ const LoginPage = compose(
}
history.replace(returnto);
} catch (error) {
console.log(error); //eslint-disable-line
if (error && error.message) {
setError(error.message);
} else if (typeof error === 'string') {
setError(error);
}
}
},
})

42
js/pages/ZipcodePage.js Normal file
View file

@ -0,0 +1,42 @@
// @flow
import React from 'react';
import { View, Text, TextInput } from 'react-native';
import { compose, withState } from 'recompose';
import RouterButton from 'react-router-native-button';
import theme from '../ui-theme';
type Props = {
zipcode: string,
setZipcode: (zip: string) => void,
};
const ZipcodePage = ({ zipcode, setZipcode }: Props) => {
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 30 }}>
<Text style={{ fontSize: 18 }}>
OK, we won't detect your location. Instead, please enter a zipcode for us to use as your
default location.
</Text>
<TextInput
onChangeText={setZipcode}
value={zipcode}
placeholder="Zipcode"
keyboardType="numeric"
style={{ marginBottom: 20 }}
maxLength={5}
autoFocus
/>
<RouterButton
to={`/list/food?zipcode=${zipcode}`}
title="Save"
color={theme.palette.primaryColor}
disabled={!(zipcode && zipcode.length === 5)}
replace
/>
</View>
);
};
export default compose(withState('zipcode', 'setZipcode', ''))(ZipcodePage);