├── .eslintrc
├── src
├── stacks
│ ├── index.js
│ ├── AppStack.js
│ ├── RootStack.js
│ └── AuthStack.js
├── utils
│ ├── index.js
│ └── options.js
├── shared
│ ├── index.js
│ ├── constants.js
│ └── styledComponents.js
├── screens
│ ├── app
│ │ ├── Home.js
│ │ ├── Profile.js
│ │ └── Settings.js
│ ├── index.js
│ ├── Loading.js
│ └── auth
│ │ ├── ForgetPassword.js
│ │ ├── ConfirmPassword.js
│ │ ├── ConfirmSignUp.js
│ │ ├── SignUp.js
│ │ └── SignIn.js
└── components
│ └── Spinner.js
├── assets
├── icon.png
└── splash.png
├── babel.config.js
├── App.js
├── .expo-shared
└── assets.json
├── .gitignore
├── README.md
├── app.json
└── package.json
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["wesbos"]
3 | }
4 |
--------------------------------------------------------------------------------
/src/stacks/index.js:
--------------------------------------------------------------------------------
1 | export { default as RootStack } from './RootStack';
2 |
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youneshenniwrites/authentication/HEAD/assets/icon.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youneshenniwrites/authentication/HEAD/assets/splash.png
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | export { tabOptions } from './options';
2 | export { stackOptions } from './options';
3 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import { createAppContainer } from 'react-navigation';
2 |
3 | import { RootStack } from './src/stacks';
4 |
5 | export default createAppContainer(RootStack);
6 |
--------------------------------------------------------------------------------
/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "f9155ac790fd02fadcdeca367b02581c04a353aa6d5aa84409a59f6804c87acd": true,
3 | "89ed26367cdb9b771858e026f2eb95bfdb90e5ae943e716575327ec325f39c44": true
4 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 | *.jks
5 | *.p8
6 | *.p12
7 | *.key
8 | *.mobileprovision
9 | *.orig.*
10 | web-build/
11 | web-report/
12 |
13 | # macOS
14 | .DS_Store
15 |
16 | # Lock file
17 | package-lock.json
--------------------------------------------------------------------------------
/src/shared/index.js:
--------------------------------------------------------------------------------
1 | export {
2 | titles,
3 | messages,
4 | colors,
5 | placeholders,
6 | routes,
7 | buttons,
8 | } from './constants';
9 | export {
10 | Container,
11 | Centered,
12 | TextInput,
13 | Button,
14 | Text,
15 | } from './styledComponents';
16 |
--------------------------------------------------------------------------------
/src/screens/app/Home.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React from 'react';
3 |
4 | import { FlexCentered, Text } from '../../shared/styledComponents';
5 |
6 | const Home = () => (
7 |
8 | Home
9 |
10 | );
11 | export default Home;
12 |
--------------------------------------------------------------------------------
/src/screens/app/Profile.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React from 'react';
3 |
4 | import { FlexCentered, Text } from '../../shared/styledComponents';
5 |
6 | const Profile = () => (
7 |
8 | Profile
9 |
10 | );
11 | export default Profile;
12 |
--------------------------------------------------------------------------------
/src/components/Spinner.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ActivityIndicator } from 'react-native';
3 |
4 | import { FlexCentered } from '../shared/styledComponents';
5 |
6 | const Spinner = () => (
7 |
8 |
9 |
10 | );
11 | export default Spinner;
12 |
--------------------------------------------------------------------------------
/src/stacks/AppStack.js:
--------------------------------------------------------------------------------
1 | import { createMaterialTopTabNavigator } from 'react-navigation-tabs';
2 |
3 | import { Home, Profile, Settings } from '../screens';
4 | import { tabOptions } from '../utils/options';
5 |
6 | const AppStack = createMaterialTopTabNavigator(
7 | { Home, Profile, Settings },
8 | tabOptions
9 | );
10 |
11 | export default AppStack;
12 |
--------------------------------------------------------------------------------
/src/stacks/RootStack.js:
--------------------------------------------------------------------------------
1 | import { createSwitchNavigator } from 'react-navigation';
2 |
3 | import { Loading } from '../screens';
4 | import AuthStack from './AuthStack';
5 | import AppStack from './AppStack';
6 |
7 | const RootStack = createSwitchNavigator(
8 | {
9 | Loading,
10 | App: AppStack,
11 | Auth: AuthStack,
12 | },
13 | {
14 | initialRouteName: 'Loading',
15 | }
16 | );
17 |
18 | export default RootStack;
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [WIP] User Authentication With React Native and FOO Back End
2 |
3 | 
4 |
5 | ## Features
6 |
7 | * Sign in
8 | * Sing up
9 | * Remember user
10 | * Confirm user by SMS or Email link
11 | * Reset password
12 |
13 | ## Technologies
14 |
15 | * Expo
16 | * React navigation
17 | * Styled components
18 | * Back End to be decided
19 |
--------------------------------------------------------------------------------
/src/screens/index.js:
--------------------------------------------------------------------------------
1 | export { default as Home } from './app/Home';
2 | export { default as Loading } from './Loading';
3 | export { default as Profile } from './app/Profile';
4 | export { default as Settings } from './app/Settings';
5 | export { default as SignIn } from './auth/SignIn';
6 | export { default as SignUp } from './auth/SignUp';
7 | export { default as ForgetPassword } from './auth/ForgetPassword';
8 | export { default as ConfirmPassword } from './auth/ConfirmPassword';
9 | export { default as ConfirmSignUp } from './auth/ConfirmSignUp';
10 |
--------------------------------------------------------------------------------
/src/utils/options.js:
--------------------------------------------------------------------------------
1 | import { colors } from '../shared';
2 |
3 | export const tabOptions = {
4 | tabBarPosition: 'bottom',
5 | tabBarOptions: {
6 | indicatorStyle: {
7 | height: 0,
8 | },
9 | activeTintColor: colors.blue,
10 | inactiveTintColor: colors.dark,
11 | style: {
12 | backgroundColor: colors.bright,
13 | },
14 | },
15 | };
16 |
17 | export const stackOptions = title => ({
18 | title,
19 | headerTintColor: colors.dark,
20 | headerBackTitle: 'Back',
21 | headerStyle: {
22 | backgroundColor: colors.creamy,
23 | borderBottomWidth: 0,
24 | },
25 | });
26 |
--------------------------------------------------------------------------------
/src/screens/Loading.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React, { useEffect, useCallback } from 'react';
3 | import { AsyncStorage } from 'react-native';
4 |
5 | import { routes } from '../shared/constants';
6 | import Spinner from '../components/Spinner';
7 |
8 | const Loading = ({ navigation }) => {
9 | const isUserLoggedIn = useCallback(async () => {
10 | const userToken = await AsyncStorage.getItem('userToken');
11 | navigation.navigate(userToken ? routes.app : routes.auth);
12 | }, [navigation]);
13 |
14 | useEffect(() => {
15 | isUserLoggedIn();
16 | }, [isUserLoggedIn]);
17 |
18 | return ;
19 | };
20 |
21 | export default Loading;
22 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "ReactNativeAuth",
4 | "slug": "ReactNativeAuth",
5 | "privacy": "public",
6 | "sdkVersion": "36.0.0",
7 | "platforms": [
8 | "ios",
9 | "android",
10 | "web"
11 | ],
12 | "version": "1.0.0",
13 | "orientation": "portrait",
14 | "icon": "./assets/icon.png",
15 | "splash": {
16 | "image": "./assets/splash.png",
17 | "resizeMode": "contain",
18 | "backgroundColor": "#ffffff"
19 | },
20 | "updates": {
21 | "fallbackToCacheTimeout": 0
22 | },
23 | "assetBundlePatterns": [
24 | "**/*"
25 | ],
26 | "ios": {
27 | "supportsTablet": true
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/screens/auth/ForgetPassword.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React from 'react';
3 |
4 | import {
5 | Container,
6 | Centered,
7 | TextInput,
8 | Button,
9 | Text,
10 | colors,
11 | placeholders,
12 | buttons,
13 | routes,
14 | } from '../../shared';
15 |
16 | const ForgetPassword = ({ navigation: { navigate } }) => (
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 | );
28 |
29 | export default ForgetPassword;
30 |
--------------------------------------------------------------------------------
/src/screens/auth/ConfirmPassword.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React from 'react';
3 |
4 | import {
5 | Container,
6 | Centered,
7 | TextInput,
8 | Button,
9 | Text,
10 | colors,
11 | placeholders,
12 | buttons,
13 | } from '../../shared';
14 |
15 | const ConfirmPassword = () => (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 | );
28 |
29 | export default ConfirmPassword;
30 |
--------------------------------------------------------------------------------
/src/screens/auth/ConfirmSignUp.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React from 'react';
3 |
4 | import {
5 | Container,
6 | TextInput,
7 | Text,
8 | Button,
9 | Centered,
10 | } from '../../shared/styledComponents';
11 | import { colors, placeholders, routes, buttons } from '../../shared/constants';
12 |
13 | const ConfirmSignUp = ({ navigation: { navigate } }) => (
14 |
15 |
16 |
17 |
18 |
19 |
22 |
25 |
26 |
27 | );
28 |
29 | export default ConfirmSignUp;
30 |
--------------------------------------------------------------------------------
/src/stacks/AuthStack.js:
--------------------------------------------------------------------------------
1 | import { createStackNavigator } from 'react-navigation-stack';
2 |
3 | import {
4 | SignIn,
5 | SignUp,
6 | ForgetPassword,
7 | ConfirmPassword,
8 | ConfirmSignUp,
9 | } from '../screens';
10 | import { stackOptions } from '../utils';
11 | import { titles } from '../shared';
12 |
13 | const AuthStack = createStackNavigator({
14 | SignIn: {
15 | screen: SignIn,
16 | navigationOptions: stackOptions(titles.login),
17 | },
18 | SignUp: {
19 | screen: SignUp,
20 | navigationOptions: stackOptions(titles.register),
21 | },
22 | ConfirmSignUp: {
23 | screen: ConfirmSignUp,
24 | navigationOptions: stackOptions(titles.confirm),
25 | },
26 | ForgetPassword: {
27 | screen: ForgetPassword,
28 | navigationOptions: stackOptions(titles.forget),
29 | },
30 | ConfirmPassword: {
31 | screen: ConfirmPassword,
32 | navigationOptions: stackOptions(titles.newPassword),
33 | },
34 | });
35 |
36 | export default AuthStack;
37 |
--------------------------------------------------------------------------------
/src/screens/auth/SignUp.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React from 'react';
3 |
4 | import {
5 | Container,
6 | Centered,
7 | TextInput,
8 | Button,
9 | Text,
10 | messages,
11 | colors,
12 | placeholders,
13 | routes,
14 | buttons,
15 | } from '../../shared';
16 |
17 | const SignUp = ({ navigation: { navigate } }) => (
18 |
19 |
20 |
21 |
22 |
26 |
27 | navigate(routes.login)}>{messages.already}
28 |
29 |
30 |
33 |
34 |
35 | );
36 |
37 | export default SignUp;
38 |
--------------------------------------------------------------------------------
/src/screens/app/Settings.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React from 'react';
3 | import { AsyncStorage } from 'react-native';
4 |
5 | import {
6 | Container,
7 | Button,
8 | Text,
9 | TextInput,
10 | Centered,
11 | } from '../../shared/styledComponents';
12 | import { routes, colors, buttons, placeholders } from '../../shared/constants';
13 |
14 | const Settings = ({ navigation }) => {
15 | const signOutAsync = async () => {
16 | await AsyncStorage.clear();
17 | navigation.navigate(routes.loader);
18 | };
19 | return (
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
32 |
33 |
34 | );
35 | };
36 | export default Settings;
37 |
--------------------------------------------------------------------------------
/src/shared/constants.js:
--------------------------------------------------------------------------------
1 | export const titles = {
2 | login: 'Sign in',
3 | register: 'Sign up',
4 | confirm: 'Confirm user',
5 | forget: 'Forget password',
6 | newPassword: 'New password',
7 | };
8 |
9 | export const messages = {
10 | forget: 'Forget password ?',
11 | register: 'Create an account',
12 | already: 'Already a member ?',
13 | };
14 |
15 | export const colors = {
16 | bright: '#fafafa',
17 | dark: '#33395F',
18 | blue: '#1760E3',
19 | creamy: '#e6ebf0',
20 | grey: '#b5b1b3',
21 | };
22 |
23 | export const buttons = {
24 | login: 'Sign in',
25 | logout: 'Sing out',
26 | code: 'Send code',
27 | confirm: 'Confirm',
28 | send: 'Send code',
29 | resend: 'Resend code',
30 | };
31 |
32 | export const placeholders = {
33 | username: 'Username',
34 | password: 'Password',
35 | code: 'Confirmation code',
36 | oldPassword: 'Old password',
37 | newPassword: 'New password',
38 | email: 'Email',
39 | phone: 'Phone',
40 | };
41 |
42 | export const routes = {
43 | auth: 'Auth',
44 | app: 'App',
45 | loader: 'Loading',
46 | profile: 'Profile',
47 | register: 'SignUp',
48 | confirm: 'ConfirmSignUp',
49 | login: 'SignIn',
50 | forget: 'ForgetPassword',
51 | password: 'ConfirmPassword',
52 | };
53 |
--------------------------------------------------------------------------------
/src/screens/auth/SignIn.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React from 'react';
3 | import { AsyncStorage } from 'react-native';
4 |
5 | import {
6 | Container,
7 | Centered,
8 | TextInput,
9 | Button,
10 | Text,
11 | messages,
12 | colors,
13 | placeholders,
14 | routes,
15 | buttons,
16 | } from '../../shared';
17 |
18 | const SignIn = ({ navigation: { navigate } }) => {
19 | // TODO: move this to utils
20 | const signInAsync = async () => {
21 | await AsyncStorage.setItem('userToken', 'abc');
22 | navigate(routes.app);
23 | };
24 |
25 | return (
26 |
27 |
28 |
29 |
30 | navigate(routes.forget)}>
31 | {messages.forget}
32 |
33 |
34 |
35 | navigate(routes.register)}>
36 | {messages.register}
37 |
38 |
41 |
42 |
43 | );
44 | };
45 |
46 | export default SignIn;
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "android": "expo start --android",
6 | "ios": "expo start --ios",
7 | "web": "expo start --web",
8 | "eject": "expo eject",
9 | "lint": "eslint .",
10 | "lint:fix": "eslint . --fix"
11 | },
12 | "dependencies": {
13 | "expo": "~36.0.0",
14 | "react": "~16.9.0",
15 | "react-dom": "~16.9.0",
16 | "react-native": "https://github.com/expo/react-native/archive/sdk-36.0.0.tar.gz",
17 | "react-native-gesture-handler": "^1.5.2",
18 | "react-native-reanimated": "^1.4.0",
19 | "react-native-web": "~0.11.7",
20 | "react-navigation": "^4.0.10",
21 | "react-navigation-drawer": "^2.3.3",
22 | "react-navigation-stack": "^1.10.3",
23 | "react-navigation-tabs": "^2.6.2",
24 | "styled-components": "^4.4.1"
25 | },
26 | "devDependencies": {
27 | "@babel/core": "^7.0.0",
28 | "babel-eslint": "^9.0.0",
29 | "babel-preset-expo": "~8.0.0",
30 | "eslint": "^5.16.0",
31 | "eslint-config-airbnb": "^17.1.1",
32 | "eslint-config-prettier": "^4.3.0",
33 | "eslint-config-wesbos": "0.0.19",
34 | "eslint-plugin-html": "^5.0.5",
35 | "eslint-plugin-import": "^2.19.1",
36 | "eslint-plugin-jsx-a11y": "^6.2.3",
37 | "eslint-plugin-prettier": "^3.1.2",
38 | "eslint-plugin-react": "^7.17.0",
39 | "eslint-plugin-react-hooks": "^1.7.0",
40 | "prettier": "^1.19.1"
41 | },
42 | "private": true
43 | }
44 |
--------------------------------------------------------------------------------
/src/shared/styledComponents.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import { Dimensions } from 'react-native';
3 | import { colors } from './constants';
4 |
5 | const { width } = Dimensions.get('screen');
6 |
7 | export const Container = styled.SafeAreaView`
8 | flex: 1;
9 | align-items: center;
10 | justify-content: space-between;
11 | background-color: ${colors.creamy};
12 | `;
13 |
14 | export const Centered = styled.View`
15 | align-items: center;
16 | justify-content: center;
17 | background-color: ${colors.creamy};
18 | `;
19 |
20 | export const FlexCentered = styled(Centered)`
21 | flex: 1;
22 | `;
23 |
24 | export const TextInput = styled.TextInput.attrs(props => ({
25 | autoCorrect: false,
26 | autoCapitalize: 'none',
27 | returnKeyType: 'done',
28 | placeholder: props.placeholder,
29 | secureTextEntry: props.password,
30 | }))`
31 | font-size: 18px;
32 | border-color: ${colors.bright};
33 | border-width: 0.5px;
34 | border-radius: 30px;
35 | background-color: ${colors.bright};
36 | height: 55px;
37 | width: ${width * 0.9}px;
38 | padding: 16px;
39 | padding-left: 24px;
40 | margin-bottom: 16px;
41 | margin-top: 16px;
42 | `;
43 |
44 | export const Button = styled.TouchableOpacity`
45 | background-color: ${colors.blue};
46 | width: ${width * 0.9}px;
47 | height: 55px;
48 | padding: 16px;
49 | border-radius: 30px;
50 | align-items: center;
51 | justify-content: center;
52 | margin-bottom: 16px;
53 | margin-top: 16px;
54 | `;
55 |
56 | export const Text = styled.Text`
57 | font-size: 18px;
58 | font-weight: 500;
59 | color: ${props => (props.color ? props.color : colors.dark)};
60 | `;
61 |
--------------------------------------------------------------------------------