Commit df4fed0c authored by Gayath's avatar Gayath

init with firebase auth

parents
{
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
}
node_modules/**/*
.expo/*
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
# macOS
.DS_Store
import React, { useState } from "react";
import AppNavigator from "./src/navigation/AppNavigator";
import { AuthProvider } from "./src/provider/AuthProvider";
import { ThemeProvider } from "react-native-rapi-ui";
import { LogBox } from "react-native";
import { AppLoading } from 'expo';
import * as Font from 'expo-font';
export default function App(props) {
const [isReady, setIsReady]= useState(false);
const images = [
require("./assets/icon.png"),
require("./assets/splash.png"),
require("./assets/login.png"),
require("./assets/register.png"),
require("./assets/forget.png"),
];
// Ignore firebase v9 AsyncStorage warning
React.useEffect(() => {
loadFonts();
LogBox.ignoreLogs([
"AsyncStorage has been extracted from react-native core and will be removed in a future release. It can now be installed and imported from '@react-native-async-storage/async-storage' instead of 'react-native'. See https://github.com/react-native-async-storage/async-storage",
]);
}, []);
async function loadFonts() {
await Font.loadAsync({
SFPRODISPLAYBOLD: require('./assets/fonts/SFPRODISPLAYBOLD.otf'),
});
setIsReady(true);
}
// if (!isReady) {
// return <AppLoading></AppLoading>;
// }
return (
<ThemeProvider images={images}>
<AuthProvider>
<AppNavigator />
</AuthProvider>
</ThemeProvider>
);
}
# Template Firebase auth flow
Template starter with React Navigation and Firebase auth using React Context
# Preview
![../media/authflow.png](../media/authflow.png)
# Installation
1. Install [node.js](https://nodejs.org/en/)
2. Install Expo
```jsx
npm install --global expo-cli
```
3. Download this repo
4. Install deps on your template folder
```jsx
npm install
```
5. Start the environtment
```jsx
expo start
```
# Auth Flow
### Firebase Setup
- Set up a new firebase project
- Go to Authentication and under Sign-in Method enable Email/Password
- Fill this firebase config to your config inside `./src/navigation/AppNavigator.js`
```jsx
// Better put your these secret keys in .env file
const firebaseConfig = {
apiKey: '',
authDomain: '',
databaseURL: '',
projectId: '',
storageBucket: '',
messagingSenderId: '',
appId: '',
};
```
### Prebuilt UI Screens
There are 3 screens included inside `./src/screens/auth` and one more thing its included with the firebase auth function, so you don't need to create the function. The ilustrations I use [undraw](https://undraw.co/)
- Login screen `./src/screens/auth/login.tsx`
- Register screen `./src/screens/auth/register.tsx`
- Forget password screen `./src/screens/auth/forget.tsx`
I personally use these screens on my project [TiktTeng](https://github.com/codingki/TikTeng) in early stages before the redesign, feel free to use these screens ❤️
### React Navigation Auth Flow
The checking logged users process is inside `./src/provider/AuthProvider` I use React Context, you can add more functions like get the data of the user and store it to the context (better static data, ex: uid)
Inside the navigator `./src/navigation/AppNavigator.js`
There's 2 stack navigator :
- `<Auth/>` → for not logged in users stack
- `<Main/>` → for logged in users stack
- `<Loading/>` → when checking if the user is logged in or not loading screen
```jsx
export default () => {
const auth = useContext(AuthContext);
const user = auth.user;
return (
<NavigationContainer>
{user == null && <Loading />}
{user == false && <Auth />}
{user == true && <Main />}
</NavigationContainer>
);
};
```
## Rapi UI
![../media/hero.png](../media/hero.png)
These UI components are provided by [Rapi UI](https://rapi-ui.kikiding.space/).
Check the [documentation](https://rapi-ui.kikiding.space/docs/) for usage and more components.
# File Managements
These are the folders and the functionality
```jsx
/src/assets -> for media such as images, etc
/src/components -> for components
/src/navigation -> for React Navigation
/src/provider -> for React Context
/src/screens -> for Screens
```
if you find these useful don't forget to give it a star ⭐ and share it to your friends ❤️
Reach me on [twitter](https://twitter.com/kikiding/)
{
"expo": {
"name": "firebaseAuthFlow",
"slug": "firebaseAuthFlow",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": ["**/*"],
"ios": {
"supportsTablet": true
},
"web": {
"favicon": "./assets/favicon.png"
}
}
}
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
This diff is collapsed.
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"@expo/vector-icons": "^12.0.0",
"@react-navigation/native": "^6.0.6",
"@react-navigation/native-stack": "^6.2.5",
"expo": "~44.0.0",
"expo-asset": "~8.4.6",
"expo-font": "~10.0.4",
"expo-status-bar": "~1.2.0",
"firebase": "^9.6.1",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-native": "0.64.3",
"react-native-rapi-ui": "^0.2.1",
"react-native-safe-area-context": "3.3.2",
"react-native-screens": "~3.10.1",
"react-native-web": "0.17.1"
},
"devDependencies": {
"@babel/core": "^7.12.9"
},
"private": true
}
import React, { useContext } from "react";
import { initializeApp, getApps } from "firebase/app";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { AuthContext } from "../provider/AuthProvider";
// Main
import Home from "../screens/Home";
import SecondScreen from "../screens/SecondScreen";
// Auth screens
import Login from "../screens/auth/Login";
import Register from "../screens/auth/Register";
import ForgetPassword from "../screens/auth/ForgetPassword";
import Loading from "../screens/utils/Loading";
// Better put your these secret keys in .env file
const firebaseConfig = {
apiKey: "AIzaSyAKdoJGktykqJ3kZM-ugZNMTrG2ICxhIVI",
authDomain: "traffico-84a9c.firebaseapp.com",
databaseURL: "",
projectId: "traffico-84a9c",
storageBucket: "traffico-84a9c.appspot.com",
messagingSenderId: "212492132739",
appId: "1:212492132739:android:d55dc6f5457646ef90d6b6"
};
if (getApps().length === 0) {
initializeApp(firebaseConfig);
}
const AuthStack = createNativeStackNavigator();
const Auth = () => {
return (
<AuthStack.Navigator
screenOptions={{
headerShown: false,
}}
>
<AuthStack.Screen name="Login" component={Login} />
<AuthStack.Screen name="Register" component={Register} />
<AuthStack.Screen name="ForgetPassword" component={ForgetPassword} />
</AuthStack.Navigator>
);
};
const MainStack = createNativeStackNavigator();
const Main = () => {
return (
<MainStack.Navigator
screenOptions={{
headerShown: false,
}}
>
<MainStack.Screen name="Home" component={Home} />
<MainStack.Screen name="SecondScreen" component={SecondScreen} />
</MainStack.Navigator>
);
};
export default () => {
const auth = useContext(AuthContext);
const user = auth.user;
return (
<NavigationContainer>
{user == null && <Loading />}
{user == false && <Auth />}
{user == true && <Main />}
</NavigationContainer>
);
};
import React, { createContext, useState, useEffect } from "react";
import { getAuth, onAuthStateChanged } from "firebase/auth";
const AuthContext = createContext();
const AuthProvider = (props) => {
const auth = getAuth();
// user null = loading
const [user, setUser] = useState(null);
useEffect(() => {
checkLogin();
}, []);
function checkLogin() {
onAuthStateChanged(auth, (u) => {
if (u) {
setUser(true);
// getUserData();
} else {
setUser(false);
// setUserData(null);
}
});
}
return (
<AuthContext.Provider
value={{
user,
}}
>
{props.children}
</AuthContext.Provider>
);
};
export { AuthContext, AuthProvider };
import React from "react";
import { View, Linking } from "react-native";
import { getAuth, signOut } from "firebase/auth";
import {
Layout,
Button,
Text,
TopNav,
Section,
SectionContent,
useTheme,
themeColor,
} from "react-native-rapi-ui";
import { Ionicons } from "@expo/vector-icons";
export default function ({ navigation }) {
const { isDarkmode, setTheme } = useTheme();
const auth = getAuth();
return (
<Layout>
<TopNav
middleContent="Home"
rightContent={
<Ionicons
name={isDarkmode ? "sunny" : "moon"}
size={20}
color={isDarkmode ? themeColor.white100 : themeColor.dark}
/>
}
rightAction={() => {
if (isDarkmode) {
setTheme("light");
} else {
setTheme("dark");
}
}}
/>
<View
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
}}
>
<Section style={{ marginTop: 20 }}>
<SectionContent>
<Text fontWeight="bold" style={{ textAlign: "center" }}>
These UI components provided by Rapi UI
</Text>
<Button
style={{ marginTop: 10 }}
text="Rapi UI Documentation"
status="info"
onPress={() => Linking.openURL("https://rapi-ui.kikiding.space/")}
/>
<Button
text="Go to second screen"
onPress={() => {
navigation.navigate("SecondScreen");
}}
style={{
marginTop: 10,
}}
/>
<Button
status="danger"
text="Logout"
onPress={() => {
signOut(auth);
}}
style={{
marginTop: 10,
}}
/>
</SectionContent>
</Section>
</View>
</Layout>
);
}
import React from "react";
import { View } from "react-native";
import {
Layout,
TopNav,
Text,
themeColor,
useTheme,
} from "react-native-rapi-ui";
import { Ionicons } from "@expo/vector-icons";
export default function ({ navigation }) {
const { isDarkmode, setTheme } = useTheme();
return (
<Layout>
<TopNav
middleContent="Second Screen"
leftContent={
<Ionicons
name="chevron-back"
size={20}
color={isDarkmode ? themeColor.white100 : themeColor.dark}
/>
}
leftAction={() => navigation.goBack()}
rightContent={
<Ionicons
name={isDarkmode ? "sunny" : "moon"}
size={20}
color={isDarkmode ? themeColor.white100 : themeColor.dark}
/>
}
rightAction={() => {
if (isDarkmode) {
setTheme("light");
} else {
setTheme("dark");
}
}}
/>
<View
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
}}
>
{/* This text using ubuntu font */}
<Text fontWeight="bold">This is the second screen</Text>
</View>
</Layout>
);
}
import React, { useState } from "react";
import {
ScrollView,
TouchableOpacity,
View,
KeyboardAvoidingView,
Image,
} from "react-native";
import { getAuth, sendPasswordResetEmail } from "firebase/auth";
import {
Layout,
Text,
TextInput,
Button,
useTheme,
themeColor,
} from "react-native-rapi-ui";
export default function ({ navigation }) {
const { isDarkmode, setTheme } = useTheme();
const auth = getAuth();
const [email, setEmail] = useState("");
const [loading, setLoading] = useState(false);
async function forget() {
setLoading(true);
await sendPasswordResetEmail(auth, email)
.then(function () {
setLoading(false);
navigation.navigate("Login");
alert("Your password reset has been sent to your email");
})
.catch(function (error) {
setLoading(false);
alert(error);
});
}
return (
<KeyboardAvoidingView behavior="height" enabled style={{ flex: 1 }}>
<Layout>
<ScrollView
contentContainerStyle={{
flexGrow: 1,
}}
>
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: isDarkmode ? "#17171E" : themeColor.white100,
}}
>
<Image
resizeMode="contain"
style={{
height: 220,
width: 220,
}}
source={require("../../../assets/forget.png")}
/>
</View>
<View
style={{
flex: 3,
paddingHorizontal: 20,
paddingBottom: 20,
backgroundColor: isDarkmode ? themeColor.dark : themeColor.white,
}}
>
<Text
size="h3"
fontWeight="bold"
style={{
alignSelf: "center",
padding: 30,
}}
>
Forget Password
</Text>
<Text>Email</Text>
<TextInput
containerStyle={{ marginTop: 15 }}
placeholder="Enter your email"
value={email}
autoCapitalize="none"
autoCompleteType="off"
autoCorrect={false}
keyboardType="email-address"
onChangeText={(text) => setEmail(text)}
/>
<Button
text={loading ? "Loading" : "Send email"}
onPress={() => {
forget();
}}
style={{
marginTop: 20,
}}
disabled={loading}
/>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginTop: 15,
justifyContent: "center",
}}
>
<Text size="md">Already have an account?</Text>
<TouchableOpacity
onPress={() => {
navigation.navigate("Login");
}}
>
<Text
size="md"
fontWeight="bold"
style={{
marginLeft: 5,
}}
>
Login here
</Text>
</TouchableOpacity>
</View>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginTop: 30,
justifyContent: "center",
}}
>
<TouchableOpacity
onPress={() => {
isDarkmode ? setTheme("light") : setTheme("dark");
}}
>
<Text
size="md"
fontWeight="bold"
style={{
marginLeft: 5,
}}
>
{isDarkmode ? "☀️ light theme" : "🌑 dark theme"}
</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
</Layout>
</KeyboardAvoidingView>
);
}
import React, { useState } from "react";
import {
ScrollView,
TouchableOpacity,
View,
Text,
KeyboardAvoidingView,
Image,
} from "react-native";
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
import {
Layout,
TextInput,
Button,
useTheme,
themeColor,
} from "react-native-rapi-ui";
export default function ({ navigation }) {
const { isDarkmode, setTheme } = useTheme();
const auth = getAuth();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
async function login() {
setLoading(true);
await signInWithEmailAndPassword(auth, email, password).catch(function (
error
) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
setLoading(false);
alert(errorMessage);
});
}
return (
<KeyboardAvoidingView behavior="height" enabled style={{ flex: 1 }}>
<Layout>
<ScrollView
contentContainerStyle={{
flexGrow: 1,
}}
>
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: isDarkmode ? "#17171E" : themeColor.white100,
}}
>
<Image
resizeMode="contain"
style={{
height: 220,
width: 220,
}}
source={require("../../../assets/login.png")}
/>
</View>
<View
style={{
flex: 3,
paddingHorizontal: 20,
paddingBottom: 20,
backgroundColor: isDarkmode ? themeColor.dark : themeColor.white,
}}
>
<Text
fontWeight="bold"
style={{
fontFamily: 'SFPRODISPLAYBOLD',
fontSize:30,
alignSelf: "center",
padding: 30,
color: isDarkmode ? "#FFFFFF" : "#000000",
}}
size="h3"
>
Login
</Text>
<Text style={{ fontFamily: 'SFPRODISPLAYBOLD', color: isDarkmode ? "#FFFFFF" : "#000000",
}}>Email</Text>
<TextInput
containerStyle={{ marginTop: 15 }}
placeholder="Enter your email"
value={email}
autoCapitalize="none"
autoCompleteType="off"
autoCorrect={false}
keyboardType="email-address"
onChangeText={(text) => setEmail(text)}
/>
<Text style={{ marginTop: 15, fontFamily: 'SFPRODISPLAYBOLD', color: isDarkmode ? "#FFFFFF" : "#000000",
}}>Password</Text>
<TextInput
containerStyle={{ marginTop: 15 }}
placeholder="Enter your password"
value={password}
autoCapitalize="none"
autoCompleteType="off"
autoCorrect={false}
secureTextEntry={true}
onChangeText={(text) => setPassword(text)}
/>
<Button
text={loading ? "Loading" : "Continue"}
onPress={() => {
login();
}}
style={{
marginTop: 20,
}}
disabled={loading}
/>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginTop: 15,
justifyContent: "center",
}}
>
<Text size="md" style={{ color: isDarkmode ? "#FFFFFF" : "#000000",
}}>Don't have an account?</Text>
<TouchableOpacity
onPress={() => {
navigation.navigate("Register");
}}
>
<Text
size="md"
fontWeight="bold"
style={{
marginLeft: 5,
color: isDarkmode ? "#FFFFFF" : "#000000",
fontWeight:"bold"
}}
>
Register here
</Text>
</TouchableOpacity>
</View>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginTop: 10,
justifyContent: "center",
}}
>
<TouchableOpacity
onPress={() => {
navigation.navigate("ForgetPassword");
}}
>
<Text size="md" fontWeight="bold" style={{ color: isDarkmode ? "#FFFFFF" : "#000000",
}}>
Forget password
</Text>
</TouchableOpacity>
</View>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginTop: 30,
justifyContent: "center",
}}
>
<TouchableOpacity
onPress={() => {
isDarkmode ? setTheme("light") : setTheme("dark");
}}
>
<Text
size="md"
fontWeight="bold"
style={{
marginLeft: 5,
color: isDarkmode ? "#FFFFFF" : "#000000",
}}
>
{isDarkmode ? "☀️ light theme" : "🌑 dark theme"}
</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
</Layout>
</KeyboardAvoidingView>
);
}
import React, { useState } from "react";
import {
ScrollView,
TouchableOpacity,
View,
KeyboardAvoidingView,
Image,
} from "react-native";
import { getAuth, createUserWithEmailAndPassword } from "firebase/auth";
import {
Layout,
Text,
TextInput,
Button,
useTheme,
themeColor,
} from "react-native-rapi-ui";
export default function ({ navigation }) {
const { isDarkmode, setTheme } = useTheme();
const auth = getAuth();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
async function register() {
setLoading(true);
await createUserWithEmailAndPassword(auth, email, password).catch(function (
error
) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
setLoading(false);
alert(errorMessage);
});
}
return (
<KeyboardAvoidingView behavior="height" enabled style={{ flex: 1 }}>
<Layout>
<ScrollView
contentContainerStyle={{
flexGrow: 1,
}}
>
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: isDarkmode ? "#17171E" : themeColor.white100,
}}
>
<Image
resizeMode="contain"
style={{
height: 220,
width: 220,
}}
source={require("../../../assets/register.png")}
/>
</View>
<View
style={{
flex: 3,
paddingHorizontal: 20,
paddingBottom: 20,
backgroundColor: isDarkmode ? themeColor.dark : themeColor.white,
}}
>
<Text
fontWeight="bold"
size="h3"
style={{
alignSelf: "center",
padding: 30,
}}
>
Register
</Text>
<Text>Email</Text>
<TextInput
containerStyle={{ marginTop: 15 }}
placeholder="Enter your email"
value={email}
autoCapitalize="none"
autoCompleteType="off"
autoCorrect={false}
keyboardType="email-address"
onChangeText={(text) => setEmail(text)}
/>
<Text style={{ marginTop: 15 }}>Password</Text>
<TextInput
containerStyle={{ marginTop: 15 }}
placeholder="Enter your password"
value={password}
autoCapitalize="none"
autoCompleteType="off"
autoCorrect={false}
secureTextEntry={true}
onChangeText={(text) => setPassword(text)}
/>
<Button
text={loading ? "Loading" : "Create an account"}
onPress={() => {
register();
}}
style={{
marginTop: 20,
}}
disabled={loading}
/>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginTop: 15,
justifyContent: "center",
}}
>
<Text size="md">Already have an account?</Text>
<TouchableOpacity
onPress={() => {
navigation.navigate("Login");
}}
>
<Text
size="md"
fontWeight="bold"
style={{
marginLeft: 5,
}}
>
Login here
</Text>
</TouchableOpacity>
</View>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginTop: 30,
justifyContent: "center",
}}
>
<TouchableOpacity
onPress={() => {
isDarkmode ? setTheme("light") : setTheme("dark");
}}
>
<Text
size="md"
fontWeight="bold"
style={{
marginLeft: 5,
}}
>
{isDarkmode ? "☀️ light theme" : "🌑 dark theme"}
</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
</Layout>
</KeyboardAvoidingView>
);
}
import React from "react";
import { View, ActivityIndicator } from "react-native";
import { Layout, themeColor } from "react-native-rapi-ui";
export default function ({ navigation }) {
return (
<Layout>
<View
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
}}
>
<ActivityIndicator size="large" color={themeColor.primary} />
</View>
</Layout>
);
}
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