All files / app/features/communities/events EventAttendees.tsx

97.72% Statements 43/44
92.85% Branches 13/14
91.66% Functions 11/12
97.72% Lines 43/44

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139  2x   2x 2x 2x 2x     2x 2x     2x 2x   2x 2x 2x           18x 41x         41x           41x 41x   41x 27x 54x       41x     41x   41x 41x       41x 41x   41x   41x       41x   41x           1x   1x           41x     32x       14x     18x       1x 1x                         5x                   1x               1x 1x   1x                  
import { Add } from "@mui/icons-material";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { EllipsisMenuItem } from "components/EllipsisMenu";
import Snackbar from "components/Snackbar";
import MakeCoOrganizerDialog from "features/communities/events/MakeCoOrganizerDialog";
import { eventOrganizersKey } from "features/queryKeys";
import useCurrentUser from "features/userQueries/useCurrentUser";
import { Empty } from "google-protobuf/google/protobuf/empty_pb";
import { RpcError } from "grpc-web";
import { useTranslation } from "i18n";
import { COMMUNITIES } from "i18n/namespaces";
import { LiteUser } from "proto/api_pb";
import { Event } from "proto/events_pb";
import { useMemo, useState } from "react";
import { service } from "service";
 
import EventAttendeesDialog from "./EventAttendeesDialog";
import EventUsers from "./EventUsers";
import { useEventAttendees, useEventOrganizers } from "./hooks";
 
interface EventAttendeesProps {
  event: Event.AsObject;
}
 
export default function EventAttendees({ event }: EventAttendeesProps) {
  const { attendeesIds, error, hasNextPage } = useEventAttendees({
    eventId: event.eventId,
    type: "summary",
  });
 
  const { organizerIds } = useEventOrganizers({
    eventId: event.eventId,
    type: "all",
  });
 
  // Optimize searching for organizer ids
  const organizerIdSet = useMemo(() => {
    const set = new Set<number>();
 
    if (organizerIds) {
      organizerIds.forEach((id) => {
        set.add(id);
      });
    }
 
    return set;
  }, [organizerIds]);
 
  const currentUser = useCurrentUser();
 
  const isCoOrganizedByCurrentUser = useMemo(
    () => currentUser.data && organizerIdSet.has(currentUser.data.userId),
    [currentUser.data, organizerIdSet],
  );
 
  const { t } = useTranslation([COMMUNITIES]);
  const queryClient = useQueryClient();
 
  const [isDialogOpen, setIsDialogOpen] = useState(false);
 
  const [userToPromote, setUserToPromote] = useState<
    undefined | LiteUser.AsObject
  >();
 
  const [isCoOrganizerDialogOpen, setIsCoOrganizerDialogOpen] = useState(false);
 
  const { mutate: makeEventOrganizer, error: mutationError } = useMutation<
    Empty.AsObject,
    RpcError,
    number
  >({
    mutationFn: (userId) =>
      service.events.inviteEventOrganizer(event.eventId, userId),
    onSuccess: () => {
      queryClient.invalidateQueries({
        queryKey: eventOrganizersKey({ eventId: event.eventId, type: "all" }),
      });
    },
  });
 
  const getUserMenuItems = (
    user: LiteUser.AsObject,
  ): EllipsisMenuItem[] | undefined => {
    if (
      user.userId === currentUser.data?.userId ||
      organizerIdSet.has(user.userId)
    ) {
      return undefined;
    }
 
    return [
      {
        icon: Add,
        onClick: () => {
          setUserToPromote(user);
          setIsCoOrganizerDialogOpen(true);
        },
        label: t("communities:make_co_organizer:title"),
      },
    ];
  };
 
  return (
    <>
      <EventUsers
        emptyState={t("communities:no_attendees")}
        error={error}
        hasNextPage={hasNextPage}
        onSeeAllClick={() => setIsDialogOpen(true)}
        userIds={attendeesIds}
        title={t("communities:attendees")}
        getUserMenuItems={
          isCoOrganizedByCurrentUser ? getUserMenuItems : undefined
        }
      />
      <EventAttendeesDialog
        eventId={event.eventId}
        open={isDialogOpen}
        onClose={() => setIsDialogOpen(false)}
      />
      <MakeCoOrganizerDialog
        username={userToPromote?.name ?? ""}
        eventName={event.title ?? ""}
        open={isCoOrganizerDialogOpen}
        onClose={() => setIsCoOrganizerDialogOpen(false)}
        onSubmit={() => {
          if (userToPromote) {
            makeEventOrganizer(userToPromote.userId);
          }
          setIsCoOrganizerDialogOpen(false);
        }}
      />
      {mutationError && (
        <Snackbar severity="error">{mutationError?.message}</Snackbar>
      )}
    </>
  );
}