From 65b8beb701f8c14376d056584666cfe39c3f6aff Mon Sep 17 00:00:00 2001 From: Frank Nguyen <41023671+FrankreedX@users.noreply.github.com> Date: Thu, 15 Aug 2024 20:06:27 +0700 Subject: [PATCH 1/4] IWB-4: User is navigated off of chooseTeam once they're accepted through waitlist (#312) * 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 * navigation off chooseTeam works. Tech debt +1 * 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 * prevent double navigation to chooseTeam screen * fixed "invalid-argument" * added key * removed redundant remove user fix * pretty * 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 --- app/segments/(team)/chooseTeam.js | 4 ++++ dbOperations/hooks/useUserInfo.js | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/segments/(team)/chooseTeam.js b/app/segments/(team)/chooseTeam.js index 1d11662a..72d6a842 100644 --- a/app/segments/(team)/chooseTeam.js +++ b/app/segments/(team)/chooseTeam.js @@ -18,6 +18,7 @@ import { useAuthContext } from "~/context/Auth"; import { addToTeam } from "~/dbOperations/addToTeam"; import { addToWaitlist } from "~/dbOperations/addToWaitlist"; import { useBlackList } from "~/dbOperations/hooks/useBlackList"; +import { useUserInfo } from "~/dbOperations/hooks/useUserInfo"; import { useWaitlist } from "~/dbOperations/hooks/useWaitlist"; import { invalidateMultipleKeys } from "~/dbOperations/invalidateMultipleKeys"; import { auth } from "~/firebaseConfig"; @@ -46,6 +47,9 @@ function ChooseTeam() { isLoading: waitlistIsLoading, } = useWaitlist(); + //basically to trigger the side effect that navigates off this page + useUserInfo({ userId: currentUserId }); + const state = useMemo(() => { if (blacklist && blacklist[currentUserId]) { return "blacklist"; diff --git a/dbOperations/hooks/useUserInfo.js b/dbOperations/hooks/useUserInfo.js index 6e626e82..bacd5898 100644 --- a/dbOperations/hooks/useUserInfo.js +++ b/dbOperations/hooks/useUserInfo.js @@ -1,5 +1,5 @@ import { useQuery } from "@tanstack/react-query"; -import { router } from "expo-router"; +import { router, useSegments } from "expo-router"; import { collection, doc, @@ -18,6 +18,7 @@ export const useUserInfo = ({ role = null, enabled = true, } = {}) => { + const segments = useSegments(); const { currentTeamId, currentUserId, currentUserVerified } = useAuthContext(); const week_milliseconds = 604800000; @@ -44,8 +45,10 @@ export const useUserInfo = ({ console.log("e", getErrorString(e)); } + const inChooseTeam = segments.at(-1) === "chooseTeam"; + if (!data || !currentUserVerified) { - if (currentUserId === userId) { + if (currentUserId === userId && !inChooseTeam) { router.replace("segments/(team)/chooseTeam"); } return { @@ -57,6 +60,10 @@ export const useUserInfo = ({ uniqueDrills: [], }; } + if (inChooseTeam) { + router.replace("content/assignments"); + } + const filteredAssignedData = data.assigned_data.filter((assignment) => { const timeDifference = currentDateTime - assignment.assignedTime; return timeDifference <= week_milliseconds; From b2e5a6a4dde68d33e0e0cb1014fc3f525ab696a2 Mon Sep 17 00:00:00 2001 From: Frank Nguyen <41023671+FrankreedX@users.noreply.github.com> Date: Thu, 15 Aug 2024 20:08:01 +0700 Subject: [PATCH 2/4] IWB-5: Added invite tab (#300) * 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 * navigation off chooseTeam works. Tech debt +1 * half finished progress * invite works * added waitlistError to ErrorComponent * pretty * text input moves above keyboard now * 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 * can't invite already invited email * accepting invite will remove the invite * added invitelist bug fix * redundant comment Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> * :/ Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> * prevent double navigation to chooseTeam screen * fixed "invalid-argument" * added key * Fixed Android keyboard avoiding for invitelist * removed redundant remove user fix * pretty * pretty * pretty * pretty * pretty * added missing state from merge * simple fix * better align invite input with above elements --------- Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> Co-authored-by: Jake Gehrke --- .gitignore | 1 + app.json | 3 +- app/content/team/index.js | 1 + app/segments/(team)/chooseTeam.js | 42 ++++- app/segments/(team)/invitelist.js | 154 +++++++++++++++++++ app/segments/(team)/manageForeignRequests.js | 11 +- components/bottomSheetWrapper.js | 1 + dbOperations/addToInvitelist.js | 14 ++ dbOperations/hooks/useInviteList.js | 48 ++++++ dbOperations/removeInvitelist.js | 13 ++ package.json | 2 +- 11 files changed, 276 insertions(+), 14 deletions(-) create mode 100644 app/segments/(team)/invitelist.js create mode 100644 dbOperations/addToInvitelist.js create mode 100644 dbOperations/hooks/useInviteList.js create mode 100644 dbOperations/removeInvitelist.js diff --git a/.gitignore b/.gitignore index dfc823b5..17160714 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ ios android webstorm.config.js firebaseApiKey.js +/.git-branches.toml diff --git a/app.json b/app.json index 0d730b57..f2db2e97 100644 --- a/app.json +++ b/app.json @@ -22,8 +22,7 @@ "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#ffffff" }, - "package": "com.golfteam.golfapp", - "softwareKeyboardLayoutMode": "pan" + "package": "com.golfteam.golfapp" }, "web": { "favicon": "./assets/favicon.png" diff --git a/app/content/team/index.js b/app/content/team/index.js index f90d4f88..5c41c6b7 100644 --- a/app/content/team/index.js +++ b/app/content/team/index.js @@ -5,6 +5,7 @@ import { BottomSheetTextInput, } from "@gorhom/bottom-sheet"; import { useQueryClient } from "@tanstack/react-query"; +import { router } from "expo-router"; import { doc, updateDoc } from "firebase/firestore"; import { useEffect, useRef, useState } from "react"; import { diff --git a/app/segments/(team)/chooseTeam.js b/app/segments/(team)/chooseTeam.js index 72d6a842..8b8b18e5 100644 --- a/app/segments/(team)/chooseTeam.js +++ b/app/segments/(team)/chooseTeam.js @@ -18,9 +18,11 @@ import { useAuthContext } from "~/context/Auth"; import { addToTeam } from "~/dbOperations/addToTeam"; import { addToWaitlist } from "~/dbOperations/addToWaitlist"; import { useBlackList } from "~/dbOperations/hooks/useBlackList"; +import { useInvitelist } from "~/dbOperations/hooks/useInviteList"; import { useUserInfo } from "~/dbOperations/hooks/useUserInfo"; import { useWaitlist } from "~/dbOperations/hooks/useWaitlist"; import { invalidateMultipleKeys } from "~/dbOperations/invalidateMultipleKeys"; +import { removeInvitelist } from "~/dbOperations/removeInvitelist"; import { auth } from "~/firebaseConfig"; function ChooseTeam() { @@ -47,6 +49,12 @@ function ChooseTeam() { isLoading: waitlistIsLoading, } = useWaitlist(); + const { + data: invitelist, + error: invitelistError, + isLoading: invitelistIsLoading, + } = useInvitelist({ email: currentUserInfo.email }); + //basically to trigger the side effect that navigates off this page useUserInfo({ userId: currentUserId }); @@ -57,14 +65,18 @@ function ChooseTeam() { if (waitlist && waitlist[currentUserId]) { return "waitlist"; } + if (invitelist && invitelist["id"] !== undefined) { + return "invitelist"; + } return "neutral"; - }, [blacklist, currentUserId, waitlist]); //blacklist, waitlist, invited, neutral + }, [blacklist, currentUserId, invitelist, waitlist]); //blacklist, waitlist, invitelist, 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 = [ + ["invitelist"], ["blacklist"], ["waitlist"], ["userInfo", { userId: currentUserId }], @@ -119,12 +131,16 @@ function ChooseTeam() { setRefreshing(false); }, []); - if (blacklistIsLoading || waitlistIsLoading) { + if (blacklistIsLoading || waitlistIsLoading || invitelistIsLoading) { return ; } - if (blacklistError || waitlistError) { - return ; + if (blacklistError || waitlistError || invitelistError) { + return ( + + ); } return ( @@ -135,10 +151,14 @@ function ChooseTeam() { }} > } - contentContainerStyle={{ flex: 1, justifyContent: "center" }} > {!verified ? ( Your request to join the team has been received - ) : state === "invited" ? ( + ) : state === "invitelist" ? ( + + You have been invited to the team + + + )} + /> + ); + })} + + + {statusText} + + { + setCurrentEmailInput(text); + const included = invitedEmail.includes(text); + if (included) setStatusText("Email already invited"); + else setStatusText(""); + + setCurrentEmailValid(!included && emailRegex.test(text)); + }} + autoCapitalize={"none"} + autoComplete={"email"} + autoCorrect={false} + inputMode={"email"} + keyboardType={"email-address"} + placeholder={"Enter email to invite"} + style={{ + flexGrow: 1, + }} + /> + + + + ); +} + +export default Invitelist; diff --git a/app/segments/(team)/manageForeignRequests.js b/app/segments/(team)/manageForeignRequests.js index 85a93192..6381e9e6 100644 --- a/app/segments/(team)/manageForeignRequests.js +++ b/app/segments/(team)/manageForeignRequests.js @@ -4,16 +4,17 @@ import { Appbar, SegmentedButtons } from "react-native-paper"; import { SafeAreaView } from "react-native-safe-area-context"; import { themeColors } from "~/Constants"; import Blacklist from "~/app/segments/(team)/blacklist"; +import Invitelist from "~/app/segments/(team)/invitelist"; import Waitlist from "~/app/segments/(team)/waitlist"; import Header from "~/components/header"; function ManageForeignRequests() { - const [tabValue, setTabValue] = useState("invites"); + const [tabValue, setTabValue] = useState("invitelist"); const navigation = useNavigation(); const segmentedColor = (tab) => { switch (tab) { - case "invites": + case "invitelist": return "#008001"; case "waitlist": return "#FFE900"; @@ -26,7 +27,7 @@ function ManageForeignRequests() { const segmentedTextColor = (tab) => { switch (tab) { - case "invites": + case "invitelist": case "blacklist": return "white"; case "waitlist": @@ -38,7 +39,7 @@ function ManageForeignRequests() { const tabComponent = useMemo( () => ({ - invites: <>, + invitelist: , waitlist: , blacklist: , }), @@ -79,7 +80,7 @@ function ManageForeignRequests() { }} buttons={[ { - value: "invites", + value: "invitelist", label: "Invites", }, { diff --git a/components/bottomSheetWrapper.js b/components/bottomSheetWrapper.js index 398ebbb8..f36cc084 100644 --- a/components/bottomSheetWrapper.js +++ b/components/bottomSheetWrapper.js @@ -14,6 +14,7 @@ const BottomSheetWrapper = forwardRef( backgroundStyle={{ backgroundColor: themeColors.background }} topInset={insets.top} keyboardBlurBehavior={"restore"} + android_keyboardInputMode={"adjustResize"} backdropComponent={({ animatedIndex, style }) => { return ( { + const { currentTeamId } = useAuthContext(); + const { data, error, isLoading } = useQuery({ + queryKey: ["invitelist"], + queryFn: async () => { + console.log("fetching invitelist", { enabled, email }); + if (email) { + const inviteQuery = query( + collection(db, "teams", currentTeamId, "invitelist"), + where("email", "==", email), + ); + + const invite = {}; + + const attemptSnapshot = await getDocs(inviteQuery); + + attemptSnapshot.forEach((doc) => { + invite["id"] = doc.id; + invite["email"] = doc.data()["email"]; + }); + + return invite; + } else { + const newWaitlist = {}; + // Fetch entries on the Waitlist + const querySnapshot = await getDocs( + collection(db, "teams", currentTeamId, "invitelist"), + ); + querySnapshot.forEach((doc) => { + newWaitlist[doc.id] = doc.data(); + }); + return newWaitlist; + } + }, + enabled, + }); + + return { + data, + error, + isLoading, + }; +}; diff --git a/dbOperations/removeInvitelist.js b/dbOperations/removeInvitelist.js new file mode 100644 index 00000000..4970863b --- /dev/null +++ b/dbOperations/removeInvitelist.js @@ -0,0 +1,13 @@ +import { deleteDoc, doc } from "firebase/firestore"; +import { db } from "~/firebaseConfig"; + +async function removeInvitelist(teamId, inviteId) { + try { + await deleteDoc(doc(db, "teams", teamId, "invitelist", inviteId)); + } catch (e) { + console.log("Remove Invite failed: ", e); + throw e; // Rethrow the error to handle it at the caller's level if needed + } +} + +module.exports = { removeInvitelist }; diff --git a/package.json b/package.json index a12d3d8f..328ac8ee 100644 --- a/package.json +++ b/package.json @@ -44,8 +44,8 @@ "expo-status-bar": "~1.12.1", "expo-system-ui": "~3.0.4", "firebase": "^10.8.0", - "react": "18.2.0", "moment-timezone": "^0.5.45", + "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.74.1", "react-native-dropdown-picker": "^5.4.6", From 4ce5a047143dbd62414b18cdb611566e9d47d028 Mon Sep 17 00:00:00 2001 From: Frank Nguyen <41023671+FrankreedX@users.noreply.github.com> Date: Thu, 15 Aug 2024 20:08:53 +0700 Subject: [PATCH 3/4] IWB-6: Added more loading spinners (#304) * 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 * navigation off chooseTeam works. Tech debt +1 * half finished progress * invite works * added waitlistError to ErrorComponent * pretty * text input moves above keyboard now * 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 * added loading spinner to all buttons * can't invite already invited email * accepting invite will remove the invite * workin on remove user * added invitelist bug fix * migrating to react native paper Button's loading property for loading spinner * redundant comment Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> * :/ Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> * prevent double navigation to chooseTeam screen * remove half baked loading spinner on dialog component (will be its own pr) * changed dialog component's signature to contain loading * fixed "invalid-argument" * added key * last usage of Activity Indicator in a simple button replaced temp * Fixed Android keyboard avoiding for invitelist * removed redundant remove user fix * pretty * pretty * pretty * pretty * pretty * pretty * IWB-7: allow using the "loading" property in DialogComponent (#313) * added missing state from merge * simple fix * better align invite input with above elements * changed more "white" to use themecolors, and added spinner color to some buttons --------- Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> Co-authored-by: Jake Gehrke --- app/(auth)/signin.js | 6 +- app/(auth)/signup.js | 5 ++ app/content/assignments/players.js | 18 +---- app/content/profile/index.js | 22 +++--- app/content/team/index.js | 30 +++++--- app/content/team/users/[user]/index.js | 83 ++++++++++++--------- app/segments/(team)/blacklist.js | 6 ++ app/segments/(team)/chooseTeam.js | 17 ++++- app/segments/(team)/invitelist.js | 16 ++++ app/segments/(team)/waitlist.js | 75 +++++++++++++------ app/segments/drill/[id]/assignment/index.js | 39 ++++------ components/dialog.js | 12 +-- 12 files changed, 202 insertions(+), 127 deletions(-) diff --git a/app/(auth)/signin.js b/app/(auth)/signin.js index 58383fdf..fc20dbe7 100644 --- a/app/(auth)/signin.js +++ b/app/(auth)/signin.js @@ -33,6 +33,7 @@ export default function SignIn() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [forgotLoading, setForgotLoading] = useState(false); + const [loginLoading, setLoginLoading] = useState(false); const { setCurrentUserId } = useAuthContext(); const { height } = useWindowDimensions(); @@ -62,6 +63,7 @@ export default function SignIn() { }, [timeIntervalPassword]); async function handleSignIn() { + setLoginLoading(true); if (process.env.EXPO_PUBLIC_TEST_UID) { // Only allow login as test user while using `yarn test` to reduce errors setCurrentUserId(process.env.EXPO_PUBLIC_TEST_UID); @@ -74,6 +76,7 @@ export default function SignIn() { showDialog("Error", getErrorString(e)); } } + setLoginLoading(false); } async function handleForgotPassword() { @@ -204,6 +207,7 @@ export default function SignIn() { onPress={handleSignIn} buttonColor={themeColors.accent} labelStyle={styles.buttonText} + loading={loginLoading} > Login @@ -216,7 +220,7 @@ export default function SignIn() { style={styles.button} labelStyle={styles.buttonText} > - Sign Up + Sign up diff --git a/app/(auth)/signup.js b/app/(auth)/signup.js index 3fcef494..0287c6d4 100644 --- a/app/(auth)/signup.js +++ b/app/(auth)/signup.js @@ -36,11 +36,14 @@ export default function SignUp() { const [password, setPassword] = useState(""); const [passwordCheck, setPasswordCheck] = useState(""); + const [signUpLoading, setSignUpLoading] = useState(false); + const { showDialog, showSnackBar } = useAlertContext(); const { height } = useWindowDimensions(); async function handleSubmit() { + setSignUpLoading(true); try { if (password !== passwordCheck) { throw "Passwords don't match"; @@ -77,6 +80,7 @@ export default function SignUp() { console.log(e); showDialog("Error", getErrorString(e)); } + setSignUpLoading(false); } const styles = StyleSheet.create({ @@ -186,6 +190,7 @@ export default function SignUp() { onPress={handleSubmit} buttonColor={themeColors.accent} labelStyle={styles.buttonText} + loading={signUpLoading} > Submit diff --git a/app/content/assignments/players.js b/app/content/assignments/players.js index fe51c651..0ce34173 100644 --- a/app/content/assignments/players.js +++ b/app/content/assignments/players.js @@ -3,14 +3,7 @@ import { router, useLocalSearchParams, useNavigation } from "expo-router"; import { doc, getDoc, runTransaction, updateDoc } from "firebase/firestore"; import { useCallback, useEffect, useMemo, useState } from "react"; import { ScrollView, View } from "react-native"; -import { - ActivityIndicator, - Appbar, - Button, - Icon, - List, - Text, -} from "react-native-paper"; +import { Appbar, Button, Icon, List, Text } from "react-native-paper"; import { SafeAreaView } from "react-native-safe-area-context"; import { once } from "underscore"; import { themeColors } from "~/Constants"; @@ -409,7 +402,7 @@ function Index() { }} mode="contained" buttonColor={themeColors.accent} - textColor="white" + textColor={themeColors.highlight} disabled={ !assignmentList.some((assignment) => assignment.markedForDelete) } @@ -420,12 +413,9 @@ function Index() { }, }} onPress={handleDelete} + loading={loadingDelete} > - {loadingDelete ? ( - - ) : ( - "Delete" - )} + Delete )} diff --git a/app/content/profile/index.js b/app/content/profile/index.js index 0dfc2737..e6764a80 100644 --- a/app/content/profile/index.js +++ b/app/content/profile/index.js @@ -20,7 +20,7 @@ import { TouchableOpacity, View, } from "react-native"; -import { ActivityIndicator, Appbar, Switch } from "react-native-paper"; +import { ActivityIndicator, Appbar, Button, Switch } from "react-native-paper"; import { SafeAreaView } from "react-native-safe-area-context"; import { debounce } from "underscore"; import { themeColors } from "~/Constants"; @@ -267,11 +267,8 @@ function Index() { }, saveChangesButton: { backgroundColor: themeColors.accent, - paddingHorizontal: 20, - paddingVertical: 10, - borderRadius: 20, + width: 100, marginBottom: 20, - width: 100, // Increase the width of the button alignSelf: "center", }, saveChangesButtonText: { @@ -451,16 +448,17 @@ function Index() { )} {/* Save Button */} - - {updateLoading ? ( - - ) : ( - Update - )} - + Update + {/* Sign Out Button */} diff --git a/app/content/team/index.js b/app/content/team/index.js index 5c41c6b7..f29d6529 100644 --- a/app/content/team/index.js +++ b/app/content/team/index.js @@ -82,6 +82,7 @@ function Index() { const [resetDialogVisible, setResetDialogVisible] = useState(false); const hideResetDialog = () => setResetDialogVisible(false); + const [resetLoading, setResetLoading] = useState(false); const [newName, setNewName] = useState(""); @@ -222,18 +223,23 @@ function Index() { content="Resetting the season will wipe all leaderboards" visible={resetDialogVisible} onHide={hideResetDialog} - buttons={["Cancel", "Reset Season"]} - buttonsFunctions={[ - hideResetDialog, - async () => { - try { - await resetLeaderboards(currentTeamId); - await invalidateMultipleKeys(queryClient, [["best_attempts"]]); - hideResetDialog(); - } catch (e) { - console.log("Error resetting season:", e); - showDialog("Error", getErrorString(e)); - } + buttons={[ + { children: "Cancel", pressHandler: hideResetDialog }, + { + children: "Reset Season", + pressHandler: async () => { + setResetLoading(true); + try { + await resetLeaderboards(currentTeamId); + await invalidateMultipleKeys(queryClient, [["best_attempts"]]); + hideResetDialog(); + } catch (e) { + console.log("Error resetting season:", e); + showDialog("Error", getErrorString(e)); + } + setResetLoading(false); + }, + loading: resetLoading, }, ]} /> diff --git a/app/content/team/users/[user]/index.js b/app/content/team/users/[user]/index.js index a5c0ba64..4d934265 100644 --- a/app/content/team/users/[user]/index.js +++ b/app/content/team/users/[user]/index.js @@ -65,9 +65,11 @@ function Index() { const [removeDialogVisible, setRemoveDialogVisible] = useState(false); const hideRemoveDialog = () => setRemoveDialogVisible(false); + const [removeLoading, setRemoveLoading] = useState(false); const [banDialogVisible, setBanDialogVisible] = useState(false); const hideBanDialog = () => setBanDialogVisible(false); + const [banLoading, setBanLoading] = useState(false); const { showDialog, showSnackBar } = useAlertContext(); @@ -307,23 +309,31 @@ function Index() { content="All data will be lost when this user is removed." visible={removeDialogVisible} onHide={hideRemoveDialog} - buttons={["Cancel", "Remove User"]} - buttonsFunctions={[ - hideRemoveDialog, - async () => { - try { - await removeUser(currentTeamId, userId); - await queryClient.removeQueries(["userInfo", userId]); - await invalidateMultipleKeys(queryClient, [ - ["userInfo"], - ["best_attempts"], - ]); - navigation.goBack(); - } catch (e) { - console.log("Error removing user:", e); - hideRemoveDialog(); - showDialog("Error", getErrorString(e)); - } + buttons={[ + { + children: "Cancel", + pressHandler: hideRemoveDialog, + }, + { + children: "Remove User", + pressHandler: async () => { + setRemoveLoading(true); + try { + await removeUser(currentTeamId, userId); + await queryClient.removeQueries(["userInfo", userId]); + await invalidateMultipleKeys(queryClient, [ + ["userInfo"], + ["best_attempts"], + ]); + navigation.goBack(); + } catch (e) { + console.log("Error removing user:", e); + hideRemoveDialog(); + showDialog("Error", getErrorString(e)); + setRemoveLoading(false); + } + }, + loading: removeLoading, }, ]} /> @@ -333,23 +343,28 @@ function Index() { content="Banning this user will delete all their data and prevent them from joining the team again." visible={banDialogVisible} onHide={hideBanDialog} - buttons={["Cancel", "Ban User"]} - buttonsFunctions={[ - hideBanDialog, - async () => { - try { - await blacklistUser(currentTeamId, userId, userInfo, userEmail); - await queryClient.removeQueries(["userInfo", userId]); - await invalidateMultipleKeys(queryClient, [ - ["userInfo"], - ["best_attempts"], - ]); //invalidate cache - navigation.goBack(); - } catch (e) { - console.log("Error banning user:", e); - hideBanDialog(); - showDialog("Error", getErrorString(e)); - } + buttons={[ + { children: "Cancel", pressHandler: hideBanDialog }, + { + children: "Ban User", + pressHandler: async () => { + setBanLoading(true); + try { + await blacklistUser(currentTeamId, userId, userInfo, userEmail); + await queryClient.removeQueries(["userInfo", userId]); + await invalidateMultipleKeys(queryClient, [ + ["userInfo"], + ["best_attempts"], + ]); //invalidate cache + navigation.goBack(); + } catch (e) { + console.log("Error banning user:", e); + hideBanDialog(); + showDialog("Error", getErrorString(e)); + setBanLoading(false); + } + }, + loading: banLoading, }, ]} /> diff --git a/app/segments/(team)/blacklist.js b/app/segments/(team)/blacklist.js index d16eec3e..e4d54a6f 100644 --- a/app/segments/(team)/blacklist.js +++ b/app/segments/(team)/blacklist.js @@ -1,4 +1,5 @@ import { useQueryClient } from "@tanstack/react-query"; +import { useState } from "react"; import { ScrollView, View } from "react-native"; import { Button, List } from "react-native-paper"; import { themeColors } from "~/Constants"; @@ -21,6 +22,8 @@ function Blacklist() { const queryClient = useQueryClient(); // also called here for updating name + const [unbanLoading, setUnbanLoading] = useState({}); + const invalidateKeys = [["blacklist"]]; if (blacklistIsLoading) return ; @@ -52,10 +55,13 @@ function Blacklist() { > diff --git a/app/segments/(team)/chooseTeam.js b/app/segments/(team)/chooseTeam.js index 8b8b18e5..25c9e331 100644 --- a/app/segments/(team)/chooseTeam.js +++ b/app/segments/(team)/chooseTeam.js @@ -35,6 +35,9 @@ function ChooseTeam() { } = useAuthContext(); const queryClient = useQueryClient(); + const [buttonLoading, setButtonLoading] = useState(false); + const [signoutLoading, setSignoutLoading] = useState(false); + const { showDialog, showSnackBar } = useAlertContext(); const { @@ -83,6 +86,7 @@ function ChooseTeam() { ]; async function handleSignOut() { + setSignoutLoading(true); try { await signoutFireBase(auth); signOut(); @@ -90,6 +94,7 @@ function ChooseTeam() { console.log(e); showDialog("Error", getErrorString(e)); } + setSignoutLoading(false); } useEffect(() => { @@ -187,7 +192,7 @@ function ChooseTeam() { setLoading(false); }} loading={loading} - textColor="white" + textColor={themeColors.highlight} > @@ -143,6 +158,7 @@ export function Invitelist() { disabled={!currentEmailValid} onPress={onInvite} textColor={themeColors.accent} + loading={inviteLoading} > Invite diff --git a/app/segments/(team)/waitlist.js b/app/segments/(team)/waitlist.js index 012ec848..4b7fa43f 100644 --- a/app/segments/(team)/waitlist.js +++ b/app/segments/(team)/waitlist.js @@ -1,6 +1,8 @@ import { useQueryClient } from "@tanstack/react-query"; +import { useState } from "react"; import { ScrollView, View } from "react-native"; -import { Button, List } from "react-native-paper"; +import { ActivityIndicator, Button, List } from "react-native-paper"; +import { themeColors } from "~/Constants"; import { getErrorString } from "~/Utility"; import ErrorComponent from "~/components/errorComponent"; import Loading from "~/components/loading"; @@ -21,6 +23,9 @@ function Waitlist() { const { currentTeamId } = useAuthContext(); const queryClient = useQueryClient(); + //this is a pretty blunt implementation... I'm using the same variable here because each entry will disappear after either buttons are pressed + const [loading, setLoading] = useState({}); + if (waitlistError) { return ; } @@ -50,30 +55,56 @@ function Waitlist() { right={() => ( - - + {loading[userId] ? ( + + ) : ( + + + + + )} )} /> diff --git a/app/segments/drill/[id]/assignment/index.js b/app/segments/drill/[id]/assignment/index.js index 5ff8da71..ce1090f2 100644 --- a/app/segments/drill/[id]/assignment/index.js +++ b/app/segments/drill/[id]/assignment/index.js @@ -5,14 +5,7 @@ import { doc, runTransaction } from "firebase/firestore"; import { useCallback, useMemo, useState } from "react"; import { StyleSheet, TouchableOpacity, View } from "react-native"; import { ScrollView } from "react-native-gesture-handler"; -import { - ActivityIndicator, - Appbar, - Button, - List, - Text, - TouchableRipple, -} from "react-native-paper"; +import { Appbar, Button, List, Text } from "react-native-paper"; import { SafeAreaView } from "react-native-safe-area-context"; import Icon from "react-native-vector-icons/MaterialCommunityIcons"; import { once } from "underscore"; @@ -237,31 +230,27 @@ export default function Index() { /> )} - - {loading ? ( - - ) : ( - - Assign - - )} - + Assign + ); } diff --git a/components/dialog.js b/components/dialog.js index 463b5ee4..3efa4725 100644 --- a/components/dialog.js +++ b/components/dialog.js @@ -16,10 +16,9 @@ export default function DialogComponent({ content, visible, onHide, - buttons = ["Ok"], - buttonsFunctions = [() => onHide()], + buttons = [{ children: "Ok", pressHandler: () => onHide(), loading: false }], //should I also include style in here? }) { - const Buttons = buttons.map((item, index) => { + const Buttons = buttons.map((button, index) => { let style; let labelStyle; if (index === 0) { @@ -27,17 +26,18 @@ export default function DialogComponent({ labelStyle = { color: themeColors.accent }; } else { style = { backgroundColor: themeColors.accent }; - labelStyle = { color: "white" }; + labelStyle = { color: themeColors.highlight }; } return ( ); }); From 93695d3c18d1727f1cad7d466fe2f958ed2a7a95 Mon Sep 17 00:00:00 2001 From: Frank Nguyen <41023671+FrankreedX@users.noreply.github.com> Date: Thu, 15 Aug 2024 20:11:28 +0700 Subject: [PATCH 4/4] IWB-8: debounce some buttons (#309) * 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 * navigation off chooseTeam works. Tech debt +1 * half finished progress * invite works * added waitlistError to ErrorComponent * pretty * text input moves above keyboard now * 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 * added loading spinner to all buttons * can't invite already invited email * accepting invite will remove the invite * workin on remove user * added invitelist bug fix * migrating to react native paper Button's loading property for loading spinner * redundant comment Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> * :/ Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> * prevent double navigation to chooseTeam screen * remove half baked loading spinner on dialog component (will be its own pr) * changed dialog component's signature to contain loading * fixed "invalid-argument" * debounced invite button * added key * last usage of Activity Indicator in a simple button replaced temp * Fixed Android keyboard avoiding for invitelist * removed redundant remove user fix * pretty * pretty * pretty * pretty * pretty * pretty * IWB-7: allow using the "loading" property in DialogComponent (#313) * added missing state from merge * simple fix * better align invite input with above elements * debounced invitelist without changing the color of the loading wheel * changed more "white" to use themecolors, and added spinner color to some buttons * debounced and error handled Unban --------- Co-authored-by: Jake Gehrke <91503842+Gehrkej@users.noreply.github.com> Co-authored-by: Jake Gehrke --- app/segments/(team)/blacklist.js | 19 +++++++++++++++---- app/segments/(team)/invitelist.js | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/segments/(team)/blacklist.js b/app/segments/(team)/blacklist.js index e4d54a6f..49b3f9b3 100644 --- a/app/segments/(team)/blacklist.js +++ b/app/segments/(team)/blacklist.js @@ -3,6 +3,7 @@ import { useState } from "react"; import { ScrollView, View } from "react-native"; import { Button, List } from "react-native-paper"; import { themeColors } from "~/Constants"; +import { getErrorString } from "~/Utility"; import ErrorComponent from "~/components/errorComponent"; import Loading from "~/components/loading"; import RefreshInvalidate from "~/components/refreshInvalidate"; @@ -55,10 +56,20 @@ function Blacklist() { >