From ad073837ed50e41d10448d12e524950da3694b1d Mon Sep 17 00:00:00 2001 From: Frank Nguyen <41023671+FrankreedX@users.noreply.github.com> Date: Thu, 15 Aug 2024 20:04:23 +0700 Subject: [PATCH] IWB-3: added waitlist functionality (#311) * added blacklist hook * black list feature works * some unused stuff removal * waitlist seems to work * simplified removeBlacklist.js * pretty * removed transaction and hope that it works * fixed useBlackList.js hook firestore error, and made blacklist check functional * added refresh spinner for chooseTeam.js * updated invalidateKeys and fixed some styling * updated faulty invalidateKeys lists * kinda works lmao * updated the invalidateKeys again * added waitlistError to ErrorComponent * minor comment change Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> * changed console log wording in removeBlacklist Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> * removed copied over comment * updated tiny comment Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> * removed redundant log Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> * fix - added email to blacklist record * fix - added assertion if pfp exists when removing a user * fixed "invalid-argument" * added key * removed redundant remove user fix * pretty * pretty * added missing state from merge * simple fix --------- Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> Co-authored-by: Jake Gehrke --- Utility.js | 3 + app/(auth)/signup.js | 12 +- app/content/team/index.js | 14 +- app/segments/(team)/chooseTeam.js | 209 ++++++++++++------- app/segments/(team)/manageForeignRequests.js | 3 +- app/segments/(team)/waitlist.js | 87 ++++++++ dbOperations/addToTeam.js | 22 ++ dbOperations/addToWaitlist.js | 27 +++ dbOperations/hooks/useWaitlist.js | 30 +++ dbOperations/removeWaitlist.js | 13 ++ 10 files changed, 334 insertions(+), 86 deletions(-) create mode 100644 app/segments/(team)/waitlist.js create mode 100644 dbOperations/addToTeam.js create mode 100644 dbOperations/addToWaitlist.js create mode 100644 dbOperations/hooks/useWaitlist.js create mode 100644 dbOperations/removeWaitlist.js diff --git a/Utility.js b/Utility.js index e4764d51..7411a9a3 100644 --- a/Utility.js +++ b/Utility.js @@ -113,6 +113,9 @@ export function getIconByKey(key) { } export function getInitials(fullName) { + if (!fullName) { + return "?"; + } const nameParts = fullName.trim().split(/\s+/); return nameParts.map((part) => part.charAt(0).toUpperCase()).join(""); } diff --git a/app/(auth)/signup.js b/app/(auth)/signup.js index cd38b0e8..3fcef494 100644 --- a/app/(auth)/signup.js +++ b/app/(auth)/signup.js @@ -59,16 +59,6 @@ export default function SignUp() { await setDoc(doc(db, "users", userCredential.user.uid), { email: email, }); - await setDoc(doc(db, "teams", "1", "users", userCredential.user.uid), { - name: name, - // 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: userCredential.user.uid, - assigned_data: [], - uniqueDrills: [], - }); try { await sendEmailVerification(auth.currentUser); @@ -80,7 +70,7 @@ export default function SignUp() { } setCurrentUserId(userCredential.user.uid); - setCurrentUserInfo(userCredential.user); + setCurrentUserInfo({ ...userCredential.user, displayName: name }); // console.log(userCredential.user); } catch (e) { diff --git a/app/content/team/index.js b/app/content/team/index.js index 6ae0b7dc..f90d4f88 100644 --- a/app/content/team/index.js +++ b/app/content/team/index.js @@ -109,9 +109,12 @@ function Index() { /> ); const foundUsers = Object.values(userInfo) - .filter((user) => - user.name.toLowerCase().includes(searchQuery.toLowerCase()), - ) + .filter((user) => { + if (!user.name) { + return true; + } + return user.name.toLowerCase().includes(searchQuery.toLowerCase()); + }) .sort((user1, user2) => { // Assign priorities based on conditions const getPriority = (user) => { @@ -136,6 +139,11 @@ function Index() { return priority1 - priority2; } + //doesn't softlock in case displayName is null for some reason + if (!user1.name) { + return -1; + } + // If priorities are the same, then sort alphabetically by name return user1.name.localeCompare(user2.name); }); diff --git a/app/segments/(team)/chooseTeam.js b/app/segments/(team)/chooseTeam.js index a2cd92a5..1d11662a 100644 --- a/app/segments/(team)/chooseTeam.js +++ b/app/segments/(team)/chooseTeam.js @@ -5,8 +5,7 @@ import { sendEmailVerification, signOut as signoutFireBase, } from "firebase/auth"; -import { doc, setDoc } from "firebase/firestore"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useMemo, 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"; @@ -16,13 +15,21 @@ import ErrorComponent from "~/components/errorComponent"; import Loading from "~/components/loading"; import { useAlertContext } from "~/context/Alert"; import { useAuthContext } from "~/context/Auth"; +import { addToTeam } from "~/dbOperations/addToTeam"; +import { addToWaitlist } from "~/dbOperations/addToWaitlist"; import { useBlackList } from "~/dbOperations/hooks/useBlackList"; +import { useWaitlist } from "~/dbOperations/hooks/useWaitlist"; import { invalidateMultipleKeys } from "~/dbOperations/invalidateMultipleKeys"; -import { auth, db } from "~/firebaseConfig"; +import { auth } from "~/firebaseConfig"; function ChooseTeam() { - const { signOut, currentUserId, currentUserInfo, setCurrentUserId } = - useAuthContext(); + const { + signOut, + currentUserId, + currentUserInfo, + setCurrentUserId, + currentTeamId, + } = useAuthContext(); const queryClient = useQueryClient(); const { showDialog, showSnackBar } = useAlertContext(); @@ -33,12 +40,32 @@ function ChooseTeam() { isLoading: blacklistIsLoading, } = useBlackList(); - const invalidateKeys = [["blacklist"]]; + const { + data: waitlist, + error: waitlistError, + isLoading: waitlistIsLoading, + } = useWaitlist(); + + const state = useMemo(() => { + if (blacklist && blacklist[currentUserId]) { + return "blacklist"; + } + if (waitlist && waitlist[currentUserId]) { + return "waitlist"; + } + return "neutral"; + }, [blacklist, currentUserId, waitlist]); //blacklist, waitlist, invited, neutral const [verified, setVerified] = useState(false); const [refreshing, setRefreshing] = useState(false); //for Refresh Control const [loading, setLoading] = useState(false); //for resend email button + const invalidateKeys = [ + ["blacklist"], + ["waitlist"], + ["userInfo", { userId: currentUserId }], + ]; + async function handleSignOut() { try { await signoutFireBase(auth); @@ -88,12 +115,12 @@ function ChooseTeam() { setRefreshing(false); }, []); - if (blacklistIsLoading) { + if (blacklistIsLoading || waitlistIsLoading) { return ; } - if (blacklistError) { - return ; + if (blacklistError || waitlistError) { + return ; } return ( @@ -109,39 +136,79 @@ function ChooseTeam() { } contentContainerStyle={{ flex: 1, justifyContent: "center" }} > - - {blacklist[currentUserId] ? ( - + Waiting for email verification... + + + ) : state === "blacklist" ? ( + + You've been banned from this team. + + ) : state === "waitlist" ? ( + + Your request to join the team has been received + + ) : state === "invited" ? ( + - ) : ( - + ) : ( + + - - )} + Request to Join Team + + + + )} + + + + )} + /> + ); + })} + + + ); +} + +export default Waitlist; diff --git a/dbOperations/addToTeam.js b/dbOperations/addToTeam.js new file mode 100644 index 00000000..d7551129 --- /dev/null +++ b/dbOperations/addToTeam.js @@ -0,0 +1,22 @@ +import { doc, setDoc } from "firebase/firestore"; +import { db } from "~/firebaseConfig"; + +//temporary, should be replaced with multiple team functionality +export async function addToTeam(currentTeamId, currentUserId, currentUserInfo) { + console.log("currentUserInfo", currentUserInfo); + try { + await setDoc(doc(db, "teams", currentTeamId, "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: [], + uniqueDrills: [], + }); + } catch (e) { + console.log("Add to Team failed: ", e); + throw e; // Rethrow the error to handle it at the caller's level if needed + } +} diff --git a/dbOperations/addToWaitlist.js b/dbOperations/addToWaitlist.js new file mode 100644 index 00000000..e7a2593c --- /dev/null +++ b/dbOperations/addToWaitlist.js @@ -0,0 +1,27 @@ +import { doc, setDoc } from "firebase/firestore"; +import { db } from "~/firebaseConfig"; + +export async function addToWaitlist( + currentTeamId, + currentUserId, + currentUserInfo, +) { + try { + const newRequestRef = doc( + db, + "teams", + currentTeamId, + "waitlist", + currentUserId, + ); + const currentTime = Date.now(); + await setDoc(newRequestRef, { + displayName: currentUserInfo["displayName"], + email: currentUserInfo["email"], + time: currentTime, + }); + } catch (e) { + console.log("Add to Waitlist failed: ", e); + throw e; // Rethrow the error to handle it at the caller's level if needed + } +} diff --git a/dbOperations/hooks/useWaitlist.js b/dbOperations/hooks/useWaitlist.js new file mode 100644 index 00000000..add4fde6 --- /dev/null +++ b/dbOperations/hooks/useWaitlist.js @@ -0,0 +1,30 @@ +import { useQuery } from "@tanstack/react-query"; +import { collection, getDocs } from "firebase/firestore"; +import { useAuthContext } from "~/context/Auth"; +import { db } from "~/firebaseConfig"; + +export const useWaitlist = ({ enabled = true } = {}) => { + const { currentTeamId } = useAuthContext(); + const { data, error, isLoading } = useQuery({ + queryKey: ["waitlist"], + queryFn: async () => { + console.log("fetching waitlist"); + const newWaitlist = {}; + // Fetch all Entries on the Waitlist + const querySnapshot = await getDocs( + collection(db, "teams", currentTeamId, "waitlist"), + ); + querySnapshot.forEach((doc) => { + newWaitlist[doc.id] = doc.data(); + }); + return newWaitlist; + }, + enabled, + }); + + return { + data, + error, + isLoading, + }; +}; diff --git a/dbOperations/removeWaitlist.js b/dbOperations/removeWaitlist.js new file mode 100644 index 00000000..5605dde4 --- /dev/null +++ b/dbOperations/removeWaitlist.js @@ -0,0 +1,13 @@ +import { deleteDoc, doc } from "firebase/firestore"; +import { db } from "~/firebaseConfig"; + +async function removeWaitlist(teamId, userId) { + try { + await deleteDoc(doc(db, "teams", teamId, "waitlist", userId)); + } catch (e) { + console.log("Remove Blacklist Transaction failed: ", e); + throw e; // Rethrow the error to handle it at the caller's level if needed + } +} + +module.exports = { removeWaitlist };