diff --git a/.expo-shared/assets.json b/.expo-shared/assets.json deleted file mode 100644 index 17ad2288..00000000 --- a/.expo-shared/assets.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "f9155ac790fd02fadcdeca367b02581c04a353aa6d5aa84409a59f6804c87acd": true, - "89ed26367cdb9b771858e026f2eb95bfdb90e5ae943e716575327ec325f39c44": true -} \ No newline at end of file diff --git a/.gitignore b/.gitignore index df55f5cb..4a1ce429 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,7 @@ Temporary Items .apdisk # Project related -.env \ No newline at end of file +.env +.expo +.expo-shared +.vscode \ No newline at end of file diff --git a/.watchmanconfig b/.watchmanconfig deleted file mode 100644 index 0967ef42..00000000 --- a/.watchmanconfig +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/App.js b/App.js index 9d8ff211..4b18a04d 100644 --- a/App.js +++ b/App.js @@ -1,8 +1,8 @@ -import React from 'react'; -import { SafeAreaProvider } from 'react-native-safe-area-context'; +import React from "react"; +import { SafeAreaProvider } from "react-native-safe-area-context"; -import { RootNavigator } from './navigation/RootNavigator'; -import { AuthenticatedUserProvider } from './providers'; +import { RootNavigator } from "./navigation/RootNavigator"; +import { AuthenticatedUserProvider } from "./providers"; const App = () => { return ( diff --git a/README.md b/README.md index dbed4a1d..1f601d1c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Is a quicker way to start with Expo + Firebase (using JS SDK) projects. It includes: -- based on Expo SDK `46` +- based on Expo SDK `50` - navigation using `react-navigation` 6.x.x - Firebase JS SDK v9 - Firebase as the backend for email auth @@ -14,9 +14,9 @@ Is a quicker way to start with Expo + Firebase (using JS SDK) projects. It inclu - custom hook to toggle password field visibility on a TextInput - handles server errors using Formik - Login, Signup & Password Reset form built using Formik & yup -- show/hide Password Field's visibility 👁 +- show/hide the Password Field's visibility 👁 - uses a custom Provider using Context API & Firebase's `onAuthStateChanged` handler to check the user's auth state with -- handles Forgot Password Reset using Firebase email method +- handles Forgot Password Reset using the Firebase email method - uses [Expo Vector Icons](https://icons.expo.fyi/) - uses [KeyboardAwareScrollView](https://github.com/APSL/react-native-keyboard-aware-scroll-view) package to handle keyboard appearance and automatically scrolls to focused TextInput - uses `dotenv` and `expo-constants` packages to manage environment variables (so that they are not exposed on public repositories) @@ -44,10 +44,25 @@ MESSAGING_SENDER_ID=XXXX APP_ID=XXXX ``` -4. Start the project: +## Run project -- `yarn ios` -- open on iOS -- `yarn android` -- open on Android +To start the development server and run your project: + +``` +npx expo start +``` + +Alternate to using Expo Go, if you are building more than a hobby project or a prototype, make sure you [create a development build](https://docs.expo.dev/develop/development-builds/introduction/). You can either [locally compile your project](https://docs.expo.dev/guides/local-app-development/#local-builds-with-expo-dev-client) or [use EAS](https://docs.expo.dev/develop/development-builds/create-a-build/). + +To locally compile your app, run: + +``` +# Build native Android project +npx expo run:android + +# Build native iOS project +npx expo run:ios +``` ## File Structure @@ -93,17 +108,17 @@ Main screens: - Forgot password - Home (Bare Minimum) with a logout button -![Login screen with validation](https://i.imgur.com/cydaOYN.png) +Login screen with validation>
 
-![Successful Signup](https://i.imgur.com/62kcirI.png)
+<img src= -![Forgot Password](https://i.imgur.com/9J9a4Nl.png) +Forgot password screen -![Validation on Signup screens](https://i.imgur.com/DG0wTjG.png) +Validation on Signup screens ## Development builds and React Native Firebase library -This project uses Firebase JS SDK which doesn't support all services (such as Crashlytics, Dynamic Links, and Analytics). However, you can use `react-native-firebase` library in an Expo project by [creating a development build](https://docs.expo.dev/develop/development-builds/introduction/). +This project uses Firebase JS SDK which doesn't support all services (such as Crashlytics, Dynamic Links, and Analytics). However, you can use the `react-native-firebase` library in an Expo project by [creating a development build](https://docs.expo.dev/develop/development-builds/introduction/). Both of these libraries can satisfy different project requirements. To learn about the differences between using Firebase JS SDK and React Native Firebase library when building your app with Expo, see the following sections from Expo's official documentation: diff --git a/app.config.js b/app.config.js index 188e85a5..b46fcf17 100644 --- a/app.config.js +++ b/app.config.js @@ -1,25 +1,25 @@ -import 'dotenv/config'; +import "dotenv/config"; export default { expo: { - name: 'Expo Firebase Starter', - slug: 'expo-firebase', - privacy: 'public', - platforms: ['ios', 'android'], - version: '0.15.0', - orientation: 'portrait', - icon: './assets/flame.png', + name: "Expo Firebase Starter", + slug: "expo-firebase", + privacy: "public", + platforms: ["ios", "android"], + version: "0.15.0", + orientation: "portrait", + icon: "./assets/flame.png", splash: { - image: './assets/splash.png', - resizeMode: 'cover', - backgroundColor: '#F57C00' + image: "./assets/splash.png", + resizeMode: "cover", + backgroundColor: "#F57C00", }, updates: { - fallbackToCacheTimeout: 0 + fallbackToCacheTimeout: 0, }, - assetBundlePatterns: ['**/*'], + assetBundlePatterns: ["**/*"], ios: { - supportsTablet: true + supportsTablet: true, }, extra: { apiKey: process.env.API_KEY, @@ -27,7 +27,7 @@ export default { projectId: process.env.PROJECT_ID, storageBucket: process.env.STORAGE_BUCKET, messagingSenderId: process.env.MESSAGING_SENDER_ID, - appId: process.env.APP_ID - } - } + appId: process.env.APP_ID, + }, + }, }; diff --git a/babel.config.js b/babel.config.js index 17131ee3..73ebf58e 100644 --- a/babel.config.js +++ b/babel.config.js @@ -2,6 +2,5 @@ module.exports = function (api) { api.cache(true); return { presets: ["babel-preset-expo"], - plugins: ["react-native-reanimated/plugin"], }; }; diff --git a/config/firebase.js b/config/firebase.js index 1a07d660..b43724c8 100644 --- a/config/firebase.js +++ b/config/firebase.js @@ -1,8 +1,7 @@ import { initializeApp } from "firebase/app"; -// import { initializeAuth, getReactNativePersistence } from "firebase/auth"; -import { initializeAuth } from "firebase/auth"; +import { initializeAuth, getReactNativePersistence } from "firebase/auth"; import Constants from "expo-constants"; -// import AsyncStorage from "@react-native-async-storage/async-storage"; +import AsyncStorage from "@react-native-async-storage/async-storage"; // add firebase config const firebaseConfig = { @@ -17,15 +16,9 @@ const firebaseConfig = { // initialize firebase const app = initializeApp(firebaseConfig); -// -// Need to upgrade to Expo with typescrip inorder getReactNativePersistence to work. -// - -//initialize auth -// const auth = initializeAuth(app, { -// persistence: getReactNativePersistence(AsyncStorage), -// }); - -const auth = initializeAuth(app); +// initialize auth +const auth = initializeAuth(app, { + persistence: getReactNativePersistence(AsyncStorage), +}); export { auth }; diff --git a/config/images.js b/config/images.js index 41686e85..a115a95a 100644 --- a/config/images.js +++ b/config/images.js @@ -1,3 +1,3 @@ export const Images = { - logo: require('../assets/flame.png') + logo: require("../assets/flame.png"), }; diff --git a/config/index.js b/config/index.js index 1e7a4f02..2d10b03a 100644 --- a/config/index.js +++ b/config/index.js @@ -1,5 +1,5 @@ -import { Images } from './images'; -import { Colors } from './theme'; -import { auth } from './firebase'; +import { Images } from "./images"; +import { Colors } from "./theme"; +import { auth } from "./firebase"; export { Images, Colors, auth }; diff --git a/config/theme.js b/config/theme.js index 717663a8..d124af16 100644 --- a/config/theme.js +++ b/config/theme.js @@ -1,8 +1,8 @@ export const Colors = { - orange: '#f57c00', - blue: '#039be5', - black: '#222222', - white: '#ffffff', - mediumGray: '#6e6869', - red: '#fc5c65' + orange: "#f57c00", + blue: "#039be5", + black: "#222222", + white: "#ffffff", + mediumGray: "#6e6869", + red: "#fc5c65", }; diff --git a/metro.config.js b/metro.config.js index b40c0b7c..3aac6b57 100644 --- a/metro.config.js +++ b/metro.config.js @@ -1,10 +1,7 @@ -// Learn more https://docs.expo.dev/guides/customizing-metro/ -const { getDefaultConfig } = require("expo/metro-config"); +// Learn more at https://docs.expo.dev/guides/using-firebase/#configure-metro +const { getDefaultConfig } = require("@expo/metro-config"); -/** @type {import('expo/metro-config').MetroConfig} */ const defaultConfig = getDefaultConfig(__dirname); defaultConfig.resolver.sourceExts.push("cjs"); -defaultConfig.resolver.mainFields = ["react-native", "browser", "main"]; - module.exports = defaultConfig; diff --git a/navigation/AppStack.js b/navigation/AppStack.js index c49052b8..c489dbd2 100644 --- a/navigation/AppStack.js +++ b/navigation/AppStack.js @@ -1,14 +1,14 @@ -import * as React from 'react'; -import { createStackNavigator } from '@react-navigation/stack'; +import * as React from "react"; +import { createStackNavigator } from "@react-navigation/stack"; -import { HomeScreen } from '../screens'; +import { HomeScreen } from "../screens"; const Stack = createStackNavigator(); export const AppStack = () => { return ( - + ); }; diff --git a/navigation/AuthStack.js b/navigation/AuthStack.js index b59b24f1..82591363 100644 --- a/navigation/AuthStack.js +++ b/navigation/AuthStack.js @@ -1,19 +1,19 @@ -import * as React from 'react'; -import { createStackNavigator } from '@react-navigation/stack'; +import * as React from "react"; +import { createStackNavigator } from "@react-navigation/stack"; -import { LoginScreen, SignupScreen, ForgotPasswordScreen } from '../screens'; +import { LoginScreen, SignupScreen, ForgotPasswordScreen } from "../screens"; const Stack = createStackNavigator(); export const AuthStack = () => { return ( - - - + + + ); }; diff --git a/navigation/RootNavigator.js b/navigation/RootNavigator.js index 0c8a8059..a6ae15d0 100644 --- a/navigation/RootNavigator.js +++ b/navigation/RootNavigator.js @@ -1,12 +1,12 @@ -import React, { useState, useContext, useEffect } from 'react'; -import { NavigationContainer } from '@react-navigation/native'; -import { onAuthStateChanged } from 'firebase/auth'; +import React, { useState, useContext, useEffect } from "react"; +import { NavigationContainer } from "@react-navigation/native"; +import { onAuthStateChanged } from "firebase/auth"; -import { AuthStack } from './AuthStack'; -import { AppStack } from './AppStack'; -import { AuthenticatedUserContext } from '../providers'; -import { LoadingIndicator } from '../components'; -import { auth } from '../config'; +import { AuthStack } from "./AuthStack"; +import { AppStack } from "./AppStack"; +import { AuthenticatedUserContext } from "../providers"; +import { LoadingIndicator } from "../components"; +import { auth } from "../config"; export const RootNavigator = () => { const { user, setUser } = useContext(AuthenticatedUserContext); @@ -16,7 +16,7 @@ export const RootNavigator = () => { // onAuthStateChanged returns an unsubscriber const unsubscribeAuthStateChanged = onAuthStateChanged( auth, - authenticatedUser => { + (authenticatedUser) => { authenticatedUser ? setUser(authenticatedUser) : setUser(null); setIsLoading(false); } diff --git a/navigation/index.js b/navigation/index.js index ae9e03f1..2553c647 100644 --- a/navigation/index.js +++ b/navigation/index.js @@ -1,3 +1,3 @@ -import { RootNavigator } from './RootNavigator'; +import { RootNavigator } from "./RootNavigator"; export { RootNavigator }; diff --git a/package.json b/package.json index 2a3d6803..e3edbe1a 100644 --- a/package.json +++ b/package.json @@ -4,35 +4,34 @@ "start": "expo start", "android": "expo start --android", "ios": "expo start --ios", - "web": "expo start --web", - "eject": "expo eject" + "web": "expo start --web" }, "dependencies": { "@expo/webpack-config": "^19.0.0", - "@react-native-async-storage/async-storage": "1.18.2", - "@react-native-masked-view/masked-view": "^0.2.7", + "@react-native-async-storage/async-storage": "1.21.0", + "@react-native-masked-view/masked-view": "0.3.0", "@react-navigation/native": "^6.1.16", "@react-navigation/stack": "^6.3.28", "dotenv": "^10.0.0", - "expo": "^49.0.15", - "expo-constants": "~14.4.2", + "expo": "^50.0.0", + "expo-constants": "~15.4.5", "firebase": "^10.5.2", "formik": "2.1.4", "react": "18.2.0", "react-dom": "18.2.0", - "react-native": "0.72.10", - "react-native-gesture-handler": "2.12.0", + "react-native": "0.73.6", + "react-native-gesture-handler": "~2.14.0", "react-native-keyboard-aware-scroll-view": "^0.9.4", - "react-native-reanimated": "3.3.0", - "react-native-safe-area-context": "4.6.3", - "react-native-screens": "3.22.0", + "react-native-reanimated": "~3.6.2", + "react-native-safe-area-context": "4.8.2", + "react-native-screens": "~3.29.0", "react-native-web": "0.19.10", "yup": "^0.27.0" }, "devDependencies": { "@babel/core": "^7.20.0", "@babel/runtime": "^7.9.0", - "babel-preset-expo": "^9.5.0", + "babel-preset-expo": "^10.0.0" }, "private": true } diff --git a/screens/ForgotPasswordScreen.js b/screens/ForgotPasswordScreen.js index fbc519fd..19adab46 100644 --- a/screens/ForgotPasswordScreen.js +++ b/screens/ForgotPasswordScreen.js @@ -1,24 +1,24 @@ -import React, { useState } from 'react'; -import { StyleSheet, Text } from 'react-native'; -import { Formik } from 'formik'; -import { sendPasswordResetEmail } from 'firebase/auth'; +import React, { useState } from "react"; +import { StyleSheet, Text } from "react-native"; +import { Formik } from "formik"; +import { sendPasswordResetEmail } from "firebase/auth"; -import { passwordResetSchema } from '../utils'; -import { Colors, auth } from '../config'; -import { View, TextInput, Button, FormErrorMessage } from '../components'; +import { passwordResetSchema } from "../utils"; +import { Colors, auth } from "../config"; +import { View, TextInput, Button, FormErrorMessage } from "../components"; export const ForgotPasswordScreen = ({ navigation }) => { - const [errorState, setErrorState] = useState(''); + const [errorState, setErrorState] = useState(""); - const handleSendPasswordResetEmail = values => { + const handleSendPasswordResetEmail = (values) => { const { email } = values; sendPasswordResetEmail(auth, email) .then(() => { - console.log('Success: Password Reset Email sent.'); - navigation.navigate('Login'); + console.log("Success: Password Reset Email sent."); + navigation.navigate("Login"); }) - .catch(error => setErrorState(error.message)); + .catch((error) => setErrorState(error.message)); }; return ( @@ -27,9 +27,9 @@ export const ForgotPasswordScreen = ({ navigation }) => { Reset your password handleSendPasswordResetEmail(values)} + onSubmit={(values) => handleSendPasswordResetEmail(values)} > {({ values, @@ -37,24 +37,24 @@ export const ForgotPasswordScreen = ({ navigation }) => { errors, handleChange, handleSubmit, - handleBlur + handleBlur, }) => ( <> {/* Email input field */} {/* Display Screen Error Mesages */} - {errorState !== '' ? ( + {errorState !== "" ? ( ) : null} {/* Password Reset Send Email button */} @@ -68,8 +68,8 @@ export const ForgotPasswordScreen = ({ navigation }) => {