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           25x 55x         55x           55x 55x   55x 34x 68x       55x     55x   55x 55x       55x 55x   55x   55x       55x   55x           1x   1x           55x     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>
      )}
    </>
  );
}