diff --git a/app/(auth)/signup.js b/app/(auth)/signup.js index 44315057..df9ce866 100644 --- a/app/(auth)/signup.js +++ b/app/(auth)/signup.js @@ -1,5 +1,9 @@ import { Link, useLocalSearchParams } from "expo-router"; -import { createUserWithEmailAndPassword, updateProfile } from "firebase/auth"; +import { + createUserWithEmailAndPassword, + sendEmailVerification, + updateProfile, +} from "firebase/auth"; import { doc, setDoc } from "firebase/firestore"; import { useState } from "react"; import { @@ -32,7 +36,7 @@ export default function SignUp() { const [password, setPassword] = useState(""); const [passwordCheck, setPasswordCheck] = useState(""); - const { showDialog } = useAlertContext(); + const { showDialog, showSnackBar } = useAlertContext(); const { height } = useWindowDimensions(); @@ -66,6 +70,15 @@ export default function SignUp() { uniqueDrills: [], }); + try { + await sendEmailVerification(auth.currentUser); + console.log("Verification Email Sent!"); + showSnackBar("Verification Email Sent!"); + } catch { + console.log("Error sending verification email: ", e); + showDialog("Error", getErrorString(e)); + } + setCurrentUserId(userCredential.user.uid); setCurrentUserInfo(userCredential.user); diff --git a/app/content/profile/index.js b/app/content/profile/index.js index ac86b739..0dfc2737 100644 --- a/app/content/profile/index.js +++ b/app/content/profile/index.js @@ -175,7 +175,7 @@ function Index() { newPassword, newPasswordCheck, passwordInputVisible, - userData.name, + userData, userEmail, ], ); diff --git a/app/segments/(team)/chooseTeam.js b/app/segments/(team)/chooseTeam.js index 246ef913..0448e3f1 100644 --- a/app/segments/(team)/chooseTeam.js +++ b/app/segments/(team)/chooseTeam.js @@ -1,15 +1,18 @@ import { useQueryClient } from "@tanstack/react-query"; import { router } from "expo-router"; -import { signOut as signoutFireBase } from "firebase/auth"; +import { + onIdTokenChanged, + sendEmailVerification, + signOut as signoutFireBase, +} from "firebase/auth"; import { doc, getDoc, setDoc } from "firebase/firestore"; -import { useEffect, useState } from "react"; -import { Text, View } from "react-native"; +import { useCallback, useEffect, useState } from "react"; +import { RefreshControl, ScrollView, Text, View } from "react-native"; import { Button } from "react-native-paper"; import { SafeAreaView } from "react-native-safe-area-context"; import { themeColors } from "~/Constants"; import { getErrorString } from "~/Utility"; import ErrorComponent from "~/components/errorComponent"; -import Loading from "~/components/loading"; import { useAlertContext } from "~/context/Alert"; import { useAuthContext } from "~/context/Auth"; import { invalidateMultipleKeys } from "~/dbOperations/invalidateMultipleKeys"; @@ -20,13 +23,15 @@ function ChooseTeam() { useAuthContext(); const queryClient = useQueryClient(); - const { showDialog } = useAlertContext(); + const { showDialog, showSnackBar } = useAlertContext(); const [blacklist, setBlacklist] = useState(false); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [verified, setVerified] = useState(false); + async function handleSignOut() { try { await signoutFireBase(auth); @@ -58,9 +63,41 @@ function ChooseTeam() { } }, [currentUserId]); - if (loading) { - return ; - } + useEffect(() => { + const unregisterAuthObserver = onIdTokenChanged(auth, async (user) => { + if (user) { + if (user.emailVerified) { + setVerified(true); + showSnackBar("Email successfully verified."); + clearInterval(intervalId); // Stop the interval when email is verified + unregisterAuthObserver(); // Unregister the auth observer + } else { + setVerified(false); + console.log("Error: Email Not Verified Yet, Try Again"); + } + } + }); + + // Set up an interval to check email verification every 10 seconds + const intervalId = setInterval(async () => { + if (auth.currentUser) { + await auth.currentUser.reload(); + } + }, 10000); // 10,000 ms = 10 seconds + + return () => { + clearInterval(intervalId); // Clean up the interval when component unmounts + unregisterAuthObserver(); // Unregister the auth observer + }; + }, []); + + const [refreshing, setRefreshing] = useState(false); + + const onRefresh = useCallback(async () => { + setRefreshing(true); + await auth.currentUser.reload(); + setRefreshing(false); + }, []); if (error) { return ; @@ -73,38 +110,35 @@ function ChooseTeam() { justifyContent: "center", }} > - + } + contentContainerStyle={{ flex: 1, justifyContent: "center" }} > - {blacklist ? ( - - You've been banned from this team. - - ) : ( - + + {blacklist ? ( + + You've been banned from this team. + + ) : verified ? ( - - )} - + ) : ( + + Waiting for email verification... + + + )} - + ); } diff --git a/context/Auth.js b/context/Auth.js index aabf79e3..e73d4f4f 100644 --- a/context/Auth.js +++ b/context/Auth.js @@ -1,5 +1,5 @@ import { useRouter, useSegments } from "expo-router"; -import { onAuthStateChanged } from "firebase/auth"; +import { onIdTokenChanged } from "firebase/auth"; import { createContext, useContext, useEffect, useState } from "react"; import { auth } from "~/firebaseConfig"; @@ -11,6 +11,7 @@ const AuthContext = createContext({ currentUserId: null, currentTeamId: null, currentUserInfo: null, + currentUserVerified: false, }); export function useAuthContext() { @@ -35,12 +36,13 @@ export const AuthProvider = ({ children }) => { const [currentUserId, setCurrentUserId] = useState(null); const [currentUserInfo, setCurrentUserInfo] = useState(null); const [currentTeamId, setCurrentTeamId] = useState("1"); + const [currentUserVerified, setCurrentUserVerified] = useState(false); useProtectedRoute(currentUserId); useEffect(() => { //if this code is not in here, it'll run for infinite times - onAuthStateChanged(auth, (newlyLoggedInUser) => { + onIdTokenChanged(auth, (newlyLoggedInUser) => { // test user login (yarn test) // If you sign out, reload or click "sign in" to login as test user // Signout functionality for test user is buggy, chance of auto-logging back in @@ -55,10 +57,13 @@ export const AuthProvider = ({ children }) => { setCurrentUserId(newlyLoggedInUser["uid"] ?? "Error (uid)"); setCurrentUserInfo(newlyLoggedInUser ?? {}); console.log("user changed. userId:", newlyLoggedInUser["uid"]); + if (auth.currentUser.emailVerified) { + setCurrentUserVerified(true); + } } } }); - }, []); + }, [currentUserVerified]); return ( { setCurrentUserInfo(userInfo); }, currentUserInfo, + currentUserVerified, }} > {children} diff --git a/dbOperations/hooks/useUserInfo.js b/dbOperations/hooks/useUserInfo.js index 6efd0d2d..a6f9c96c 100644 --- a/dbOperations/hooks/useUserInfo.js +++ b/dbOperations/hooks/useUserInfo.js @@ -17,7 +17,8 @@ export const useUserInfo = ({ role = null, enabled = true, } = {}) => { - const { currentTeamId, currentUserId } = useAuthContext(); + const { currentTeamId, currentUserId, currentUserVerified } = + useAuthContext(); const week_milliseconds = 604800000; const currentDate = new Date(); const currentDateTime = currentDate.getTime(); @@ -31,7 +32,8 @@ export const useUserInfo = ({ doc(db, "teams", currentTeamId, "users", userId), ); const data = querySnapshot.data(); - if (!data) { + + if (!data || !currentUserVerified) { if (currentUserId === userId) { router.replace("segments/(team)/chooseTeam"); }