Skip to content

Commit

Permalink
IWB-5: Added invite tab (#300)
Browse files Browse the repository at this point in the history
* 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 <gehrkej@oregonstate.edu>
  • Loading branch information
3 people authored Aug 15, 2024
1 parent 65b8beb commit b2e5a6a
Show file tree
Hide file tree
Showing 11 changed files with 276 additions and 14 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ ios
android
webstorm.config.js
firebaseApiKey.js
/.git-branches.toml
3 changes: 1 addition & 2 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions app/content/team/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
42 changes: 36 additions & 6 deletions app/segments/(team)/chooseTeam.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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 });

Expand All @@ -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 }],
Expand Down Expand Up @@ -119,12 +131,16 @@ function ChooseTeam() {
setRefreshing(false);
}, []);

if (blacklistIsLoading || waitlistIsLoading) {
if (blacklistIsLoading || waitlistIsLoading || invitelistIsLoading) {
return <Loading />;
}

if (blacklistError || waitlistError) {
return <ErrorComponent errorList={[blacklistError, waitlistError]} />;
if (blacklistError || waitlistError || invitelistError) {
return (
<ErrorComponent
errorList={[blacklistError, waitlistError, invitelistError]}
/>
);
}

return (
Expand All @@ -135,10 +151,14 @@ function ChooseTeam() {
}}
>
<ScrollView
contentContainerStyle={{
justifyContent: "center",
alignItems: "center",
flexGrow: 1,
}}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
contentContainerStyle={{ flex: 1, justifyContent: "center" }}
>
{!verified ? (
<View
Expand Down Expand Up @@ -200,17 +220,27 @@ function ChooseTeam() {
>
Your request to join the team has been received
</Text>
) : state === "invited" ? (
) : state === "invitelist" ? (
<View
style={{
alignItems: "center",
justifyContent: "center",
}}
>
<Text
style={{
fontSize: 16,
textAlign: "center",
color: "gray",
}}
>
You have been invited to the team
</Text>
<Button
onPress={async () => {
//temporary, should be replaced with multiple team functionality
await addToTeam(currentTeamId, currentUserId, currentUserInfo);
await removeInvitelist(currentTeamId, invitelist["id"]);
setCurrentUserId(currentUserId);
await invalidateMultipleKeys(queryClient, invalidateKeys);
router.replace("/");
Expand Down
154 changes: 154 additions & 0 deletions app/segments/(team)/invitelist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { useQueryClient } from "@tanstack/react-query";
import { useMemo, useState } from "react";
import {
KeyboardAvoidingView,
Platform,
ScrollView,
Text,
TextInput,
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";
import { useAuthContext } from "~/context/Auth";
import { addToInvitelist } from "~/dbOperations/addToInvitelist";
import { useInvitelist } from "~/dbOperations/hooks/useInviteList";
import { invalidateMultipleKeys } from "~/dbOperations/invalidateMultipleKeys";
import { removeInvitelist } from "~/dbOperations/removeInvitelist";

export function Invitelist() {
const {
data: invitelist,
error: inviteError,
isLoading: inviteIsLoading,
} = useInvitelist();

const queryClient = useQueryClient();

const { currentTeamId } = useAuthContext();

const invitedEmail = useMemo(() => {
if (invitelist)
return Object.values(invitelist).map((invite) => invite["email"]);
}, [invitelist]);

const invalidateKeys = [["invitelist"]];

const [currentEmailInput, setCurrentEmailInput] = useState("");
const [currentEmailValid, setCurrentEmailValid] = useState(false);
const [statusText, setStatusText] = useState("");

const onInvite = async () => {
if (invitedEmail.includes(currentEmailInput)) {
setStatusText("Email already invited");
return;
}
await addToInvitelist(currentTeamId, currentEmailInput);
setCurrentEmailInput("");
await invalidateMultipleKeys(queryClient, invalidateKeys);
};

if (inviteError) {
return <ErrorComponent error={getErrorString(inviteError)} />;
}

if (inviteIsLoading) {
return <Loading />;
}

const emailRegex = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w\w+)+$/;

return (
<KeyboardAvoidingView
style={{
flex: 1,
}}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
<ScrollView
refreshControl={<RefreshInvalidate invalidateKeys={invalidateKeys} />}
>
<List.Section
style={{
margin: 5,
borderRadius: 5,
}}
>
{Object.keys(invitelist).map((inviteId) => {
return (
<List.Item
title={invitelist[inviteId].email}
key={inviteId}
right={() => (
<View
style={{
flexDirection: "row",
alignItems: "center",
paddingLeft: 10,
}}
>
<Button
onPress={async () => {
await removeInvitelist(currentTeamId, inviteId);
await invalidateMultipleKeys(
queryClient,
invalidateKeys,
);
}}
textColor={themeColors.accent}
>
Remove
</Button>
</View>
)}
/>
);
})}
</List.Section>
</ScrollView>
<Text>{statusText}</Text>
<View
style={{
flexDirection: "row",
padding: 5,
paddingLeft: 20,
paddingRight: 35,
}}
>
<TextInput
value={currentEmailInput}
onChangeText={(text) => {
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,
}}
/>
<Button
disabled={!currentEmailValid}
onPress={onInvite}
textColor={themeColors.accent}
>
Invite
</Button>
</View>
</KeyboardAvoidingView>
);
}

export default Invitelist;
11 changes: 6 additions & 5 deletions app/segments/(team)/manageForeignRequests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -26,7 +27,7 @@ function ManageForeignRequests() {

const segmentedTextColor = (tab) => {
switch (tab) {
case "invites":
case "invitelist":
case "blacklist":
return "white";
case "waitlist":
Expand All @@ -38,7 +39,7 @@ function ManageForeignRequests() {

const tabComponent = useMemo(
() => ({
invites: <></>,
invitelist: <Invitelist />,
waitlist: <Waitlist />,
blacklist: <Blacklist />,
}),
Expand Down Expand Up @@ -79,7 +80,7 @@ function ManageForeignRequests() {
}}
buttons={[
{
value: "invites",
value: "invitelist",
label: "Invites",
},
{
Expand Down
1 change: 1 addition & 0 deletions components/bottomSheetWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const BottomSheetWrapper = forwardRef(
backgroundStyle={{ backgroundColor: themeColors.background }}
topInset={insets.top}
keyboardBlurBehavior={"restore"}
android_keyboardInputMode={"adjustResize"}
backdropComponent={({ animatedIndex, style }) => {
return (
<BottomSheetBackdrop
Expand Down
14 changes: 14 additions & 0 deletions dbOperations/addToInvitelist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { collection, doc, setDoc } from "firebase/firestore";
import { db } from "~/firebaseConfig";

export async function addToInvitelist(currentTeamId, email) {
try {
const newRequestRef = doc(
collection(db, "teams", currentTeamId, "invitelist"),
);
await setDoc(newRequestRef, { email });
} catch (e) {
console.log("Add to Waitlist failed: ", e);
throw e; // Rethrow the error to handle it at the caller's level if needed
}
}
Loading

0 comments on commit b2e5a6a

Please sign in to comment.