RocketChat/Rocket.Chat

View on GitHub
apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx

Summary

Maintainability
A
1 hr
Test Coverage
import type { IRoom, IUser } from '@rocket.chat/core-typings';
import type { SelectOption } from '@rocket.chat/fuselage';
import { Box, Icon, TextInput, Select, Throbber, ButtonGroup, Button, Callout } from '@rocket.chat/fuselage';
import { useAutoFocus, useDebouncedCallback } from '@rocket.chat/fuselage-hooks';
import { useTranslation, useSetting } from '@rocket.chat/ui-contexts';
import type { ReactElement, FormEventHandler, ComponentProps, MouseEvent } from 'react';
import React, { useMemo } from 'react';
import { Virtuoso } from 'react-virtuoso';

import {
    ContextualbarHeader,
    ContextualbarIcon,
    ContextualbarTitle,
    ContextualbarClose,
    ContextualbarContent,
    ContextualbarFooter,
    ContextualbarEmptyContent,
    ContextualbarSection,
} from '../../../../components/Contextualbar';
import { VirtuosoScrollbars } from '../../../../components/CustomScrollbars';
import InfiniteListAnchor from '../../../../components/InfiniteListAnchor';
import RoomMembersRow from './RoomMembersRow';

type RoomMemberUser = Pick<IUser, 'username' | '_id' | 'name' | 'status'>;

type RoomMembersProps = {
    rid: IRoom['_id'];
    isTeam?: boolean;
    isDirect?: boolean;
    loading: boolean;
    text: string;
    type: string;
    setText: FormEventHandler<HTMLElement>;
    setType: (type: 'online' | 'all') => void;
    members: RoomMemberUser[];
    total: number;
    error?: Error;
    onClickClose: () => void;
    onClickView: (e: MouseEvent<HTMLElement>) => void;
    onClickAdd?: () => void;
    onClickInvite?: () => void;
    loadMoreItems: () => void;
    renderRow?: (props: ComponentProps<typeof RoomMembersRow>) => ReactElement | null;
    reload: () => void;
};

const RoomMembers = ({
    loading,
    members = [],
    text,
    type,
    setText,
    setType,
    onClickClose,
    onClickView,
    onClickAdd,
    onClickInvite,
    total,
    error,
    loadMoreItems,
    renderRow: RowComponent = RoomMembersRow,
    rid,
    isTeam,
    isDirect,
    reload,
}: RoomMembersProps): ReactElement => {
    const t = useTranslation();
    const inputRef = useAutoFocus<HTMLInputElement>(true);
    const itemData = useMemo(() => ({ onClickView, rid }), [onClickView, rid]);

    const options: SelectOption[] = useMemo(
        () => [
            ['online', t('Online')],
            ['all', t('All')],
        ],
        [t],
    );

    const loadMoreMembers = useDebouncedCallback(
        () => {
            loadMoreItems();
        },
        300,
        [loadMoreItems, members],
    );

    const useRealName = Boolean(useSetting('UI_Use_Real_Name'));

    return (
        <>
            <ContextualbarHeader data-qa-id='RoomHeader-Members'>
                <ContextualbarIcon name='members' />
                <ContextualbarTitle>{isTeam ? t('Teams_members') : t('Members')}</ContextualbarTitle>
                {onClickClose && <ContextualbarClose onClick={onClickClose} />}
            </ContextualbarHeader>
            <ContextualbarSection>
                <TextInput
                    placeholder={t('Search_by_username')}
                    value={text}
                    ref={inputRef}
                    onChange={setText}
                    addon={<Icon name='magnifier' size='x20' />}
                />
                <Box w='x144' mis={8}>
                    <Select onChange={(value): void => setType(value as 'online' | 'all')} value={type} options={options} />
                </Box>
            </ContextualbarSection>
            <ContextualbarContent p={12}>
                {loading && (
                    <Box pi={24} pb={12}>
                        <Throbber size='x12' />
                    </Box>
                )}

                {error && (
                    <Box pi={12} pb={12}>
                        <Callout type='danger'>{error.message}</Callout>
                    </Box>
                )}

                {!loading && members.length <= 0 && <ContextualbarEmptyContent title={t('No_members_found')} />}

                {!loading && members.length > 0 && (
                    <>
                        <Box pi={18} pb={12}>
                            <Box is='span' color='hint' fontScale='p2'>
                                {t('Showing_current_of_total', { current: members.length, total })}
                            </Box>
                        </Box>

                        <Box w='full' h='full' overflow='hidden' flexShrink={1}>
                            <Virtuoso
                                style={{
                                    height: '100%',
                                    width: '100%',
                                }}
                                totalCount={total}
                                overscan={50}
                                data={members}
                                // eslint-disable-next-line react/no-multi-comp
                                components={{ Scroller: VirtuosoScrollbars, Footer: () => <InfiniteListAnchor loadMore={loadMoreMembers} /> }}
                                itemContent={(index, data): ReactElement => (
                                    <RowComponent useRealName={useRealName} data={itemData} user={data} index={index} reload={reload} />
                                )}
                            />
                        </Box>
                    </>
                )}
            </ContextualbarContent>
            {!isDirect && (onClickInvite || onClickAdd) && (
                <ContextualbarFooter>
                    <ButtonGroup stretch>
                        {onClickInvite && (
                            <Button icon='link' onClick={onClickInvite} width='50%'>
                                {t('Invite_Link')}
                            </Button>
                        )}
                        {onClickAdd && (
                            <Button icon='user-plus' onClick={onClickAdd} width='50%' primary>
                                {t('Add')}
                            </Button>
                        )}
                    </ButtonGroup>
                </ContextualbarFooter>
            )}
        </>
    );
};

export default RoomMembers;