Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Email Verification upon Signup #288

Merged
merged 26 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fb5a857
email verify on signup poc
solderq35 Jul 14, 2024
1cb95c7
Merge branch 'main' into solderq35/signup-email-verify
solderq35 Jul 21, 2024
f41a856
added resend verif email button
solderq35 Jul 22, 2024
adeabd6
completed email verif feature?
solderq35 Jul 23, 2024
2385e62
update todos
solderq35 Jul 23, 2024
22b084f
Merge branch 'main' into solderq35/signup-email-verify
FrankreedX Jul 23, 2024
ff8b442
move to try / await syntax
solderq35 Jul 24, 2024
7ba8efa
refresh pulldown for user reload
solderq35 Jul 26, 2024
9569ee0
Merge branch 'main' into solderq35/signup-email-verify
FrankreedX Aug 2, 2024
1c5b906
fix signout error
solderq35 Aug 3, 2024
66d76ad
Merge branch 'main' into solderq35/signup-email-verify
FrankreedX Aug 4, 2024
823c599
Merge branch 'main' into solderq35/signup-email-verify
FrankreedX Aug 4, 2024
cfd9e15
fix - added email to blacklist record
Gehrkej Aug 4, 2024
6bff91c
fix - added assertion if pfp exists when removing a user
Gehrkej Aug 4, 2024
a6dcbc8
Merge branch 'main' into Gehrkej/fix/add-email-to-blacklist
FrankreedX Aug 4, 2024
4b1712e
Merge branch 'main' into solderq35/signup-email-verify
FrankreedX Aug 4, 2024
2959ed7
Merge branch 'Gehrkej/fix/add-email-to-blacklist' into solderq35/sign…
FrankreedX Aug 5, 2024
c05092d
polished email verification flow
FrankreedX Aug 5, 2024
2d6ed42
Merge branch 'main' into solderq35/signup-email-verify
FrankreedX Aug 6, 2024
9a7936e
Merge branch 'main' into solderq35/signup-email-verify
FrankreedX Aug 7, 2024
e8d28d4
loading spinner kind of works for send email
solderq35 Aug 7, 2024
71323ad
spinner color + useEffect dependency
solderq35 Aug 8, 2024
e9cb46b
useEffect verify status checker works
solderq35 Aug 9, 2024
69b6322
fix setCurrentUserVerified and clean up logs
solderq35 Aug 10, 2024
1cc81dc
remove settimeout from chooseteam pulldown refresh
solderq35 Aug 10, 2024
6d2d01a
Merge branch 'main' into solderq35/signup-email-verify
solderq35 Aug 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions app/(auth)/signup.js
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -66,6 +70,15 @@ export default function SignUp() {
uniqueDrills: [],
});

try {
await sendEmailVerification(auth.currentUser);
solderq35 marked this conversation as resolved.
Show resolved Hide resolved
console.log("Verification Email Sent!");
showSnackBar("Verification Email Sent!");
} catch {
console.log("Error sending verification email: ", e);
showDialog("Error", getErrorString(e));
}
solderq35 marked this conversation as resolved.
Show resolved Hide resolved

setCurrentUserId(userCredential.user.uid);
setCurrentUserInfo(userCredential.user);

Expand Down
2 changes: 1 addition & 1 deletion app/content/profile/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
setEmail(userEmail);
}, [userData, userEmail]);

const handleUpdate = useCallback(

Check warning on line 99 in app/content/profile/index.js

View workflow job for this annotation

GitHub Actions / prettier-check

React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead
debounce(
async () => {
setUpdateLoading(true);
Expand Down Expand Up @@ -175,7 +175,7 @@
newPassword,
newPasswordCheck,
passwordInputVisible,
userData.name,
userData,
userEmail,
],
);
Expand Down
158 changes: 114 additions & 44 deletions app/segments/(team)/chooseTeam.js
solderq35 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -20,13 +23,15 @@
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);
Expand Down Expand Up @@ -58,9 +63,44 @@
}
}, [currentUserId]);

if (loading) {
return <Loading />;
}
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");
solderq35 marked this conversation as resolved.
Show resolved Hide resolved
}
}
});

// 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
};
}, []);

Check warning on line 92 in app/segments/(team)/chooseTeam.js

View workflow job for this annotation

GitHub Actions / prettier-check

React Hook useEffect has a missing dependency: 'showSnackBar'. Either include it or remove the dependency array

const [refreshing, setRefreshing] = useState(false);

const onRefresh = useCallback(() => {
FrankreedX marked this conversation as resolved.
Show resolved Hide resolved
setRefreshing(true);
setTimeout(async () => {
await auth.currentUser.reload();
console.log("user reloaded");
setRefreshing(false);
}, 2000);
}, []);

if (error) {
return <ErrorComponent errorList={[error]} />;
Expand All @@ -73,38 +113,35 @@
justifyContent: "center",
}}
>
<View
style={{
justifyContent: "center",
alignItems: "center",
}}
<ScrollView
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
contentContainerStyle={{ flex: 1, justifyContent: "center" }}
>
{blacklist ? (
<Text
style={{
fontSize: 16,
textAlign: "center",
color: "gray",
}}
>
You've been banned from this team.
</Text>
) : (
<View
style={{
flexGrow: 1,
alignItems: "center",
justifyContent: "center",
}}
>
<View
style={{
justifyContent: "center",
alignItems: "center",
}}
>
{blacklist ? (
<Text
style={{
fontSize: 16,
textAlign: "center",
color: "gray",
}}
>
You've been banned from this team.
</Text>
) : verified ? (
<Button
onPress={async () => {
//temporary, should be replaced with multiple team functionality
// Update Firestore document
await setDoc(doc(db, "teams", "1", "users", currentUserId), {
name: currentUserInfo["displayName"],
// hardcoded pfp string for now, add pfp upload to profile settings in future PR
pfp: "",
// hardcoded "player" role for now, add role selection to profile settings in future PR
role: "player",
uid: currentUserId,
assigned_data: [],
Expand All @@ -114,6 +151,7 @@
await invalidateMultipleKeys(queryClient, [
["userInfo", { userId: currentUserId }],
]);
// Navigate to the next page
router.replace("/");
}}
style={{
Expand All @@ -132,15 +170,47 @@
Join Team
</Text>
</Button>
</View>
)}
<View
style={{
flexGrow: 1,
alignItems: "center",
justifyContent: "center",
}}
>
) : (
<View
style={{
alignItems: "center",
justifyContent: "center",
}}
>
<Text>Waiting for email verification...</Text>
<Button
style={{
backgroundColor: themeColors.accent,
borderRadius: 12,
marginTop: 20,
}}
onPress={async () => {
setLoading(true);
try {
await sendEmailVerification(auth.currentUser);
console.log("Verification Email Sent!");
showSnackBar("Verification Email Sent!");
} catch (e) {
console.log("Error sending verification email: ", e);
showDialog("Error", getErrorString(e));
}
setLoading(false);
}}
loading={loading}
textColor="white"
>
<Text
style={{
color: themeColors.highlight,
fontSize: 18,
textAlign: "center",
}}
>
Resend Verification Email
</Text>
</Button>
</View>
)}
<Button
onPress={handleSignOut}
style={{
Expand All @@ -160,7 +230,7 @@
</Text>
</Button>
</View>
</View>
</ScrollView>
</SafeAreaView>
);
}
Expand Down
6 changes: 6 additions & 0 deletions context/Auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const AuthContext = createContext({
currentUserId: null,
currentTeamId: null,
currentUserInfo: null,
currentUserVerified: false,
});

export function useAuthContext() {
Expand All @@ -35,6 +36,7 @@ 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);

Expand All @@ -55,6 +57,9 @@ export const AuthProvider = ({ children }) => {
setCurrentUserId(newlyLoggedInUser["uid"] ?? "Error (uid)");
setCurrentUserInfo(newlyLoggedInUser ?? {});
console.log("user changed. userId:", newlyLoggedInUser["uid"]);
if (auth.currentUser.emailVerified) {
setCurrentUserVerified(true);
}
}
}
});
Expand All @@ -78,6 +83,7 @@ export const AuthProvider = ({ children }) => {
setCurrentUserInfo(userInfo);
},
currentUserInfo,
currentUserVerified,
}}
>
{children}
Expand Down
14 changes: 11 additions & 3 deletions dbOperations/hooks/useUserInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import {
where,
} from "firebase/firestore";
import { useAuthContext } from "~/context/Auth";
import { db } from "~/firebaseConfig";
import { auth, db } from "~/firebaseConfig";

export const useUserInfo = ({
userId = null,
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();
Expand All @@ -31,7 +32,14 @@ export const useUserInfo = ({
doc(db, "teams", currentTeamId, "users", userId),
);
const data = querySnapshot.data();
if (!data) {

// DEBUG REMOVE BELOW LOG LINES LATER
solderq35 marked this conversation as resolved.
Show resolved Hide resolved
console.log("DATA");
console.log(data);
console.log("VERIFIED STATUS");
console.log(auth.currentUser.emailVerified);

if (!data || !auth.currentUser.emailVerified) {
solderq35 marked this conversation as resolved.
Show resolved Hide resolved
if (currentUserId === userId) {
router.replace("segments/(team)/chooseTeam");
}
Expand Down
Loading