mirror of
https://gitlab.com/wheres-the-tp/ui-mobile.git
synced 2026-01-25 06:04:55 -06:00
implement profiles tab
This commit is contained in:
parent
770ce4f71e
commit
0e1f45f638
8 changed files with 209 additions and 55 deletions
|
|
@ -6,7 +6,7 @@ import rxjsconfig from 'recompose/rxjsObservableConfig';
|
|||
import setObservableConfig from 'recompose/setObservableConfig';
|
||||
import TopToolbar from './components/TopToolbar';
|
||||
import { NativeRouter, Route, Redirect, AndroidBackButton, Switch } from 'react-router-native';
|
||||
import List from './pages/List';
|
||||
import Nav from './pages/Nav';
|
||||
import FoodItemDetail from './pages/FoodItemDetail';
|
||||
import PlaceDetail from './pages/PlaceDetail';
|
||||
import CreateFoodItem from './pages/CreateFoodItem';
|
||||
|
|
@ -33,10 +33,11 @@ export default class App extends Component {
|
|||
<TopToolbar />
|
||||
<Switch>
|
||||
<Route path="/landing" component={LandingPage} />
|
||||
<Route path="/list/:type" component={List} />
|
||||
<Route path="/list/:type" component={Nav} />
|
||||
<Route path="/foodItem/:id" component={FoodItemDetail} />
|
||||
<Route path="/place/:id" component={PlaceDetail} />
|
||||
<Route path="/login" component={LoginPage} />
|
||||
<Route path="/logout" component={LoginPage} />
|
||||
<Route path="/zipcode" component={ZipcodePage} />
|
||||
<Route
|
||||
path="/createFoodItem"
|
||||
|
|
|
|||
|
|
@ -37,12 +37,8 @@ export const getPositionFromZip = async (zip: string) => {
|
|||
return pos;
|
||||
};
|
||||
|
||||
export const getLocation = async (zipcode: string) => {
|
||||
if (zipcode) {
|
||||
AsyncStorage.setItem('zipcode', zipcode);
|
||||
}
|
||||
|
||||
const currentZip = zipcode || (await AsyncStorage.getItem('zipcode'));
|
||||
export const getLocation = async () => {
|
||||
const currentZip = await AsyncStorage.getItem('zipcode');
|
||||
|
||||
if (currentZip && currentZip !== 'usegps') {
|
||||
getPositionFromZip(currentZip);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// @flow
|
||||
import { curry, pipe, pathOr, prop } from 'ramda';
|
||||
import { curry, pipe, pathOr } from 'ramda';
|
||||
import queryString from 'query-string';
|
||||
|
||||
type RouteTo = {
|
||||
|
|
@ -26,8 +26,3 @@ export const getSearch = pipe(
|
|||
pathOr('', ['router', 'route', 'location', 'search']),
|
||||
queryString.parse
|
||||
);
|
||||
|
||||
export const getZipcode = pipe(
|
||||
getSearch,
|
||||
prop('zipcode')
|
||||
);
|
||||
|
|
|
|||
|
|
@ -44,12 +44,7 @@ const LandingPage = ({ skipIfAlreadyChosen, loading }: Props) => {
|
|||
We need to use your location to bring you the best experience possible.
|
||||
</Text>
|
||||
<View style={{ width: 275, marginBottom: 30 }}>
|
||||
<RouterButton
|
||||
to="/list/food?positionBy=location"
|
||||
replace
|
||||
title="OK, fine"
|
||||
color={theme.palette.primaryColor}
|
||||
/>
|
||||
<RouterButton to="/list/food" replace title="OK, fine" color={theme.palette.primaryColor} />
|
||||
</View>
|
||||
<Link to="/zipcode" replace>
|
||||
<Text style={{ color: theme.palette.primaryColor, marginBottom: 20, fontSize: 16 }}>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ type Props = {
|
|||
error: string,
|
||||
setError: (err: string) => void,
|
||||
router: {
|
||||
location: {
|
||||
pathname: string,
|
||||
},
|
||||
history: {
|
||||
replace: (url: string) => void,
|
||||
location: {
|
||||
|
|
@ -20,28 +23,35 @@ type Props = {
|
|||
},
|
||||
},
|
||||
},
|
||||
deauthUser: () => void,
|
||||
};
|
||||
const LoginPageComponent = ({ authUser, error }: Props) => {
|
||||
return (
|
||||
<View style={{ flexDirection: 'column', alignItems: 'center' }}>
|
||||
<Text style={theme.loginPage.titleStyle}>Sign In</Text>
|
||||
<View style={theme.loginPage.buttonStyle}>
|
||||
<Button
|
||||
title="Sign in with Facebook"
|
||||
onPress={authUser('facebook')}
|
||||
color={theme.palette.facebook}
|
||||
/>
|
||||
const LoginPageComponent = ({ authUser, deauthUser, error, router }: Props) => {
|
||||
debugger;
|
||||
if (/logout/.test(router.route.location.pathname)) {
|
||||
deauthUser();
|
||||
return null;
|
||||
} else {
|
||||
return (
|
||||
<View style={{ flexDirection: 'column', alignItems: 'center' }}>
|
||||
<Text style={theme.loginPage.titleStyle}>Sign In</Text>
|
||||
<View style={theme.loginPage.buttonStyle}>
|
||||
<Button
|
||||
title="Sign in with Facebook"
|
||||
onPress={authUser('facebook')}
|
||||
color={theme.palette.facebook}
|
||||
/>
|
||||
</View>
|
||||
<View style={theme.loginPage.buttonStyle}>
|
||||
<Button
|
||||
title="Sign in with Google"
|
||||
onPress={authUser('google')}
|
||||
color={theme.palette.google}
|
||||
/>
|
||||
</View>
|
||||
{error && <Text>Login Failed! {error}</Text>}
|
||||
</View>
|
||||
<View style={theme.loginPage.buttonStyle}>
|
||||
<Button
|
||||
title="Sign in with Google"
|
||||
onPress={authUser('google')}
|
||||
color={theme.palette.google}
|
||||
/>
|
||||
</View>
|
||||
{error && <Text>Login Failed! {error}</Text>}
|
||||
</View>
|
||||
);
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const LoginPage = compose(
|
||||
|
|
@ -51,7 +61,7 @@ const LoginPage = compose(
|
|||
authUser: ({ router: { history }, setError }: Props) => (
|
||||
provider: AUTH_PROVIDER
|
||||
) => async () => {
|
||||
const { returnto = '/' } = queryString.parse(history.location.search);
|
||||
const { returnto = '/list/food' } = queryString.parse(history.location.search);
|
||||
try {
|
||||
if (!AuthManager.isLoggedIn()) {
|
||||
await AuthManager.authenticate(provider);
|
||||
|
|
@ -65,6 +75,11 @@ const LoginPage = compose(
|
|||
}
|
||||
}
|
||||
},
|
||||
deauthUser: ({ router: { history } }) => () => {
|
||||
const { returnto = '/list/food' } = queryString.parse(history.location.search);
|
||||
AuthManager.deauthenticate();
|
||||
history.replace(returnto);
|
||||
},
|
||||
})
|
||||
)(LoginPageComponent);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ import { View } from 'react-native';
|
|||
import Food from './Food';
|
||||
import Places from './Places';
|
||||
import { BottomNavigation } from 'react-native-material-ui';
|
||||
import { getZipcode } from '../helpers/RouteHelpers';
|
||||
import { getLocation } from '../apis/PositionApi';
|
||||
import { compose, pure, withHandlers, withProps, lifecycle } from 'recompose';
|
||||
import { withRouterContext, type routerContext } from '../enhancers/routeEnhancers';
|
||||
import { pathOr } from 'ramda';
|
||||
|
||||
import uiTheme from '../ui-theme';
|
||||
import ProfilePage from './ProfilePage';
|
||||
import AuthManager from '../AuthManager';
|
||||
|
||||
type Props = {
|
||||
activeTab: string,
|
||||
|
|
@ -18,12 +19,13 @@ type Props = {
|
|||
setRoute: (route: string) => () => void,
|
||||
};
|
||||
|
||||
const List = ({ activeTab, setRoute }: Props) => {
|
||||
const Nav = ({ activeTab, setRoute }: Props) => {
|
||||
return (
|
||||
<View style={uiTheme.listView}>
|
||||
<View style={{ flex: 1 }}>
|
||||
{activeTab === 'food' && <Food onRefresh={getLocation} />}
|
||||
{activeTab === 'places' && <Places onRefresh={getLocation} />}
|
||||
{activeTab === 'profile' && <ProfilePage isLoggedIn={AuthManager.isLoggedIn()} />}
|
||||
</View>
|
||||
<BottomNavigation active={activeTab}>
|
||||
<BottomNavigation.Action
|
||||
|
|
@ -39,7 +41,12 @@ const List = ({ activeTab, setRoute }: Props) => {
|
|||
onPress={setRoute('/list/places')}
|
||||
/>
|
||||
<BottomNavigation.Action key="favorites" icon="favorite" label="Favorites" />
|
||||
<BottomNavigation.Action key="profile" icon="person" label="Profile" />
|
||||
<BottomNavigation.Action
|
||||
key="profile"
|
||||
icon="person"
|
||||
label="Profile"
|
||||
onPress={setRoute('/list/profile')}
|
||||
/>
|
||||
</BottomNavigation>
|
||||
</View>
|
||||
);
|
||||
|
|
@ -59,7 +66,7 @@ export default compose(
|
|||
}),
|
||||
lifecycle({
|
||||
componentDidMount() {
|
||||
getLocation(getZipcode(this.props));
|
||||
getLocation();
|
||||
},
|
||||
})
|
||||
)(List);
|
||||
)(Nav);
|
||||
132
js/pages/ProfilePage.js
Normal file
132
js/pages/ProfilePage.js
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { View, Text, Switch, TextInput, AsyncStorage } from 'react-native';
|
||||
import IconButton from '../components/IconButton';
|
||||
import { palette } from '../ui-theme';
|
||||
import { compose, withState, withHandlers, lifecycle } from 'recompose';
|
||||
import { withRouterContext } from '../enhancers/routeEnhancers';
|
||||
import AuthManager from '../AuthManager';
|
||||
import { getLocation } from '../apis/PositionApi';
|
||||
|
||||
type Props = {
|
||||
zipcode: ?string,
|
||||
loading: boolean,
|
||||
setLoading: (l: boolean) => void,
|
||||
setZip: (zip: string | null) => void,
|
||||
saveZip: (zip: number) => Promise<void>,
|
||||
error: string,
|
||||
setError: (e: string) => void,
|
||||
toggleGPS: (a: boolean) => Promise<void>,
|
||||
router: {
|
||||
history: {
|
||||
replace: (route: string) => void,
|
||||
},
|
||||
},
|
||||
isLoggedIn: boolean,
|
||||
};
|
||||
|
||||
export const ProfilePage = (props: Props) => {
|
||||
const {
|
||||
zipcode,
|
||||
loading,
|
||||
setZip,
|
||||
saveZip,
|
||||
error,
|
||||
toggleGPS,
|
||||
router: { history },
|
||||
isLoggedIn,
|
||||
} = props;
|
||||
const usingGPS = zipcode === 'usegps';
|
||||
return (
|
||||
<View style={{ flex: 1, flexDirection: 'column', padding: 15 }}>
|
||||
<Text style={{ fontSize: 32, marginBottom: 20, color: '#555555' }}>Profile</Text>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<Text style={{ fontSize: 18 }}>Use My Location</Text>
|
||||
<Switch value={usingGPS} onValueChange={toggleGPS} />
|
||||
</View>
|
||||
{!usingGPS && (
|
||||
<View style={{ flexDirection: 'row' }}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-end',
|
||||
borderBottomColor: palette.disabledColor,
|
||||
borderBottomWidth: 1,
|
||||
height: 60,
|
||||
}}>
|
||||
<TextInput
|
||||
editable={!loading}
|
||||
value={zipcode}
|
||||
placeholder="zipcode"
|
||||
onChangeText={setZip}
|
||||
onEndEditing={saveZip}
|
||||
keyboardType="numeric"
|
||||
style={{
|
||||
fontSize: 18,
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{!!error && <Text style={{ fontSize: 16, color: 'red' }}>{error}</Text>}
|
||||
<View style={{ marginTop: 20 }}>
|
||||
{isLoggedIn && (
|
||||
<IconButton
|
||||
glyph="exit-to-app"
|
||||
text="Logout"
|
||||
onPress={() => history.replace('/logout?returnto=/list/food')}
|
||||
color={palette.accentColor}
|
||||
textStyle={{ color: palette.accentColor }}
|
||||
/>
|
||||
)}
|
||||
{!isLoggedIn && (
|
||||
<IconButton
|
||||
glyph="exit-to-app"
|
||||
text="Login"
|
||||
onPress={() => history.replace('/login?returnto=/list/food')}
|
||||
color={palette.accentColor}
|
||||
textStyle={{ color: palette.accentColor }}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default compose(
|
||||
withRouterContext,
|
||||
withState('loading', 'setLoading', false),
|
||||
withState('zipcode', 'setZip', null),
|
||||
withState('error', 'setError', ''),
|
||||
lifecycle({
|
||||
componentDidMount() {
|
||||
this.props.setLoading(true);
|
||||
AsyncStorage.getItem('zipcode').then(zip => {
|
||||
this.props.setZip(zip);
|
||||
this.props.setLoading(false);
|
||||
});
|
||||
},
|
||||
}),
|
||||
withHandlers({
|
||||
saveZip: (props: Props) => async () => {
|
||||
props.setError('');
|
||||
if (!props.zipcode || props.zipcode.length !== 5) {
|
||||
props.setError('Zipcode must be five digits');
|
||||
return;
|
||||
}
|
||||
props.setLoading(true);
|
||||
await AsyncStorage.setItem('zipcode', props.zipcode);
|
||||
props.setLoading(false);
|
||||
getLocation();
|
||||
},
|
||||
toggleGPS: (props: Props) => async (useGPS: boolean) => {
|
||||
props.setLoading(true);
|
||||
const zipcode = useGPS ? 'usegps' : '';
|
||||
props.setZip(zipcode);
|
||||
await AsyncStorage.setItem('zipcode', 'usegps');
|
||||
props.setLoading(false);
|
||||
getLocation();
|
||||
},
|
||||
})
|
||||
)(ProfilePage);
|
||||
|
|
@ -1,18 +1,23 @@
|
|||
// @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 { View, Text, TextInput, Button, AsyncStorage } from 'react-native';
|
||||
import { compose, withState, withHandlers } from 'recompose';
|
||||
import { withRouterContext } from '../enhancers/routeEnhancers';
|
||||
import theme from '../ui-theme';
|
||||
|
||||
type Props = {
|
||||
zipcode: string,
|
||||
setZipcode: (zip: string) => void,
|
||||
router: {
|
||||
history: {
|
||||
replace: (route: string) => void,
|
||||
},
|
||||
},
|
||||
saveZip: (z: string) => Promise<void>,
|
||||
};
|
||||
|
||||
const ZipcodePage = ({ zipcode, setZipcode }: Props) => {
|
||||
const ZipcodePage = ({ zipcode, setZipcode, saveZip }: Props) => {
|
||||
return (
|
||||
<View style={{ flex: 1, justifyContent: 'center', padding: 30 }}>
|
||||
<Text style={{ fontSize: 18 }}>
|
||||
|
|
@ -28,15 +33,23 @@ const ZipcodePage = ({ zipcode, setZipcode }: Props) => {
|
|||
maxLength={5}
|
||||
autoFocus
|
||||
/>
|
||||
<RouterButton
|
||||
to={`/list/food?zipcode=${zipcode}`}
|
||||
<Button
|
||||
onPress={saveZip}
|
||||
title="Save"
|
||||
color={theme.palette.primaryColor}
|
||||
disabled={!(zipcode && zipcode.length === 5)}
|
||||
replace
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default compose(withState('zipcode', 'setZipcode', ''))(ZipcodePage);
|
||||
export default compose(
|
||||
withState('zipcode', 'setZipcode', ''),
|
||||
withRouterContext,
|
||||
withHandlers({
|
||||
saveZip: (props: Props) => async () => {
|
||||
await AsyncStorage.setItem('zipcode', props.zipcode);
|
||||
props.router.history.replace('/list/food');
|
||||
},
|
||||
})
|
||||
)(ZipcodePage);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue