All files / app/components UsersList.tsx

100% Statements 22/22
94.11% Branches 16/17
100% Functions 8/8
100% Lines 19/19

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 1019x 9x 9x         9x     188x       135x                                                   227x                       188x       188x   281x     188x 188x   183x   183x 46x 137x       98x       90x       177x                                        
import { CircularProgress, styled } from "@mui/material";
import UserSummary from "components/UserSummary";
import { useLiteUsers } from "features/userQueries/useLiteUsers";
import { RpcError } from "grpc-web";
import { LiteUser } from "proto/api_pb";
import { ReactNode } from "react";
 
import Alert from "./Alert";
import { EllipsisMenuItem } from "./EllipsisMenu";
 
const ContainingDiv = styled("div")(({ theme }) => ({
  padding: theme.spacing(2),
}));
 
const StyledUsersDiv = styled("div")(({ theme }) => ({
  display: "grid",
  marginBlockStart: theme.spacing(2),
  rowGap: theme.spacing(1),
}));
 
export interface UsersListProps {
  userIds: number[] | undefined;
  emptyListChildren?: ReactNode;
  endChildren?: ReactNode;
  error?: RpcError | null;
  titleIsLink?: boolean;
  getUserMenuItems?: (
    user: LiteUser.AsObject,
  ) => EllipsisMenuItem[] | undefined;
}
 
/**
 * A cute list of <UserSummary> components for each userId. Automatically fetches the user info.
 *
 * A spinner shows up while `userIds` is `undefined`. When this component is fetching the lite users, it will show skeletons (the right number).
 *
 * If any users are not found or userIds is an empty list, this will show `emptyListChildren`.
 *
 * The end of the list will show `endChildren` if the list is not empty (this is a good place to add a "load more" button)
 */
export default function UsersList({
  userIds,
  emptyListChildren,
  endChildren,
  error,
  titleIsLink = false,
  getUserMenuItems,
}: UsersListProps) {
  const {
    data: users,
    isLoading: isLoadingLiteUsers,
    error: usersError,
  } = useLiteUsers(userIds);
 
  // this is undefined if userIds is undefined or users hasn't loaded, otherwise it's an actual list
  const foundUsers =
    userIds &&
    (userIds.length > 0
      ? userIds.map((userId) => users?.get(userId)).filter((user) => !!user)
      : []);
 
  const inner = () => {
    if (error) {
      return <Alert severity="error">{error.message}</Alert>;
    } else Iif (usersError) {
      return <Alert severity="error">{usersError.message}</Alert>;
    } else if (!userIds) {
      return <CircularProgress />;
    } else if (isLoadingLiteUsers) {
      return (
        <StyledUsersDiv>
          {userIds.map((userId) => (
            <UserSummary headlineComponent="h3" key={userId} user={undefined} />
          ))}
        </StyledUsersDiv>
      );
    } else if (foundUsers && foundUsers.length > 0) {
      return (
        <StyledUsersDiv>
          {foundUsers.map((user) => {
            return (
              <UserSummary
                headlineComponent="h3"
                key={user.userId}
                user={user}
                titleIsLink={titleIsLink}
                menuItems={getUserMenuItems?.(user)}
              />
            );
          })}
          <>{endChildren}</>
        </StyledUsersDiv>
      );
    } else {
      return <>{emptyListChildren}</>;
    }
  };
 
  return <ContainingDiv>{inner()}</ContainingDiv>;
}