Commit f555737a authored by Milan John Paul Digiuseppe's avatar Milan John Paul Digiuseppe

Merge branch 'milan/login-screen' into 'master'

Auth redux, Boot/SignIn screens

See merge request !2
parents a2e87e73 cc6d1fe5
......@@ -45,6 +45,7 @@
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/ban-ts-ignore": "off",
"import/extensions": [0, {}],
"import/prefer-default-export": "off",
"import/no-extraneous-dependencies": [
......
......@@ -1170,6 +1170,14 @@
"@types/yargs": "^13.0.0"
}
},
"@react-native-community/async-storage": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/@react-native-community/async-storage/-/async-storage-1.10.3.tgz",
"integrity": "sha512-f3G3dX5aoFZZC1G7alnaFmlTpXn8HPfpR8H3Hf7wbbQrQvZmW7mW2fXHv/oj99FNPd228bneL3GOnjGe2Epkww==",
"requires": {
"deep-assign": "^3.0.0"
}
},
"@react-native-community/cli-debugger-ui": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-3.0.0.tgz",
......@@ -1281,6 +1289,15 @@
"nanoid": "^3.1.5"
}
},
"@react-navigation/stack": {
"version": "5.3.9",
"resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-5.3.9.tgz",
"integrity": "sha512-Hcp44zILnlgtl9ZsSroMUPpU2iZNZWml2FcGmF69SvZXP3PT/bfp0pRzshv31oSEnlLvwqYmHYwUQnFwNAlLmg==",
"requires": {
"color": "^3.1.2",
"react-native-iphone-x-helper": "^1.2.1"
}
},
"@reduxjs/toolkit": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.3.6.tgz",
......
import React from 'react';
import { Icon } from 'react-native-elements';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createStackNavigator } from '@react-navigation/stack';
import { NavigationContainer, DefaultTheme } from '@react-navigation/native';
import { useSelector } from 'react-redux';
import DiscoverScreen from './screens/DiscoverScreen';
import MessageCreateScreen from './screens/MessageCreateScreen';
import SavedMessagesScreen from './screens/SavedMessagesScreen';
import SignInScreen from './screens/SignInScreen';
import theme from './theme';
import BootScreen from './screens/BootScreen';
import { getAuthLoading, getAuthToken } from './store/auth/AuthSelectors';
const NavTheme = {
...DefaultTheme,
......@@ -24,7 +29,7 @@ const TabIcon: React.FC<{ name: string}> = ({ name }) => (
);
const Tab = createBottomTabNavigator();
const TabNavigator = () => (
const AppNavigator = () => (
<Tab.Navigator
initialRouteName={'Saved'}
tabBarOptions={{
......@@ -55,9 +60,37 @@ const TabNavigator = () => (
</Tab.Navigator>
);
const Stack = createStackNavigator();
const RootNavigator = () => {
const loading = useSelector(getAuthLoading);
const token = useSelector(getAuthToken);
if (loading) {
return <BootScreen />;
}
return (
<Stack.Navigator headerMode={'none'}>
{token
? (
<Stack.Screen component={AppNavigator} name={'AppNavigator'} />
)
: (
<Stack.Screen
name={'SignIn'}
component={SignInScreen}
options={{
animationTypeForReplace: 'pop',
}}
/>
)}
</Stack.Navigator>
);
};
const AppContainer = () => (
<NavigationContainer theme={NavTheme}>
<TabNavigator />
<RootNavigator />
</NavigationContainer>
);
......
import React, { useCallback } from 'react';
import { StyleSheet, View } from 'react-native';
import { Overlay, Button } from 'react-native-elements';
import { useDispatch, useSelector } from 'react-redux';
import { geoSearch } from '../store/caches/CachesActions';
import { getUserLatLng } from '../store/location/LocationSelectors';
import { signOutAsync } from '../store/auth/AuthActions';
const styles = StyleSheet.create({
overlay: {
......@@ -29,6 +29,10 @@ const DevModal: React.FC<Props> = ({ isOpen, onClose }) => {
dispatch(geoSearch(userLocation, '1'));
onClose();
}, [onClose, dispatch, userLocation]);
const signout = useCallback(() => {
dispatch(signOutAsync());
onClose();
}, []);
return (
<Overlay isVisible={isOpen} fullScreen>
......@@ -39,6 +43,12 @@ const DevModal: React.FC<Props> = ({ isOpen, onClose }) => {
type={'solid'}
containerStyle={styles.button}
/>
<Button
title={'sign out'}
onPress={signout}
type={'solid'}
containerStyle={styles.button}
/>
<Button
title={'Close'}
onPress={onClose}
......
import React, { useEffect } from 'react';
import { View, Image } from 'react-native';
import { useDispatch } from 'react-redux';
// @ts-ignore
import SplashImage from '../../assets/splash.png';
import { authBootstrap } from '../store/auth/AuthActions';
const BootScreen = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(authBootstrap());
}, []);
return (
<View style={{ flex: 1, backgroundColor: 'black' }}>
<Image
source={SplashImage}
style={{
flex: 1,
width: '100%',
resizeMode: 'contain',
}}
/>
</View>
);
};
export default BootScreen;
import React, { useCallback } from 'react';
import { View } from 'react-native';
import { Button } from 'react-native-elements';
import { useDispatch } from 'react-redux';
import { signInAsync } from '../store/auth/AuthActions';
const SignInScreen = () => {
const dispatch = useDispatch();
const signIn = useCallback(() => {
dispatch(signInAsync());
}, []);
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Button title={'Sign in'} onPress={signIn} />
</View>
);
};
export default SignInScreen;
import { combineReducers } from '@reduxjs/toolkit';
import authSlice from './auth/AuthSlice';
import cachesSlice from './caches/CachesSlice';
import locationSlice from './location/LocationSlice';
const rootReducer = combineReducers({
auth: authSlice.reducer,
caches: cachesSlice.reducer,
location: locationSlice.reducer,
});
......
import { Dispatch } from '@reduxjs/toolkit';
import { AsyncStorage } from 'react-native';
import AuthSlice from './AuthSlice';
const {
restoreToken,
signIn,
signOut,
} = AuthSlice.actions;
export const authBootstrap = () => async (dispatch: Dispatch) => {
let userToken;
try {
userToken = await AsyncStorage.getItem('userToken');
} catch (e) {
console.warn('token not found');
}
dispatch(restoreToken(userToken));
};
const persistToken = async (token: string) => {
try {
await AsyncStorage.setItem('userToken', token);
} catch (e) {
console.warn('error persisting token');
}
};
export const signInAsync = () => async (dispatch: Dispatch) => {
// TODO: get token from signing in via third-party auth
const token = 'dummy_token';
persistToken(token);
dispatch(signIn(token));
};
export const signUpAsync = () => async (dispatch: Dispatch) => {
// TODO: get token from signing up via third-party auth
const token = 'dummy_token';
persistToken(token);
dispatch(signIn(token));
};
export const signOutAsync = () => async (dispatch: Dispatch) => {
try {
await AsyncStorage.removeItem('userToken');
} catch (e) {
console.warn('error removing token');
}
dispatch(signOut());
};
import { RootState } from '../RootReducer';
export const getAuthToken = (state: RootState) => state.auth.userToken;
export const getAuthLoading = (state: RootState) => state.auth.isLoading;
import { createSlice } from '@reduxjs/toolkit';
import { AuthState } from './AuthState';
const authSlice = createSlice({
name: 'auth',
initialState: {
isLoading: true,
userToken: undefined,
} as AuthState,
reducers: {
restoreToken(state, action) {
state.userToken = action.payload;
state.isLoading = false;
},
signIn(state, action) {
state.userToken = action.payload;
},
signOut(state) {
state.userToken = undefined;
},
},
});
export default authSlice;
export interface AuthState {
isLoading: boolean;
userToken: string | undefined;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment