apps/meteor/client/sidebarv2/RoomList/RoomList.tsx
import type { IRoom } from '@rocket.chat/core-typings';
import { css } from '@rocket.chat/css-in-js';
import { Box } from '@rocket.chat/fuselage';
import { useResizeObserver } from '@rocket.chat/fuselage-hooks';
import { useUserPreference, useUserId, useTranslation } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React, { useMemo } from 'react';
import { Virtuoso } from 'react-virtuoso';
import { VirtuosoScrollbars } from '../../components/CustomScrollbars';
import { useOpenedRoom } from '../../lib/RoomManager';
import { useAvatarTemplate } from '../hooks/useAvatarTemplate';
import { usePreventDefault } from '../hooks/usePreventDefault';
import { useRoomList } from '../hooks/useRoomList';
import { useShortcutOpenMenu } from '../hooks/useShortcutOpenMenu';
import { useTemplateByViewMode } from '../hooks/useTemplateByViewMode';
import RoomListRow from './RoomListRow';
import RoomListRowWrapper from './RoomListRowWrapper';
import RoomListWrapper from './RoomListWrapper';
const computeItemKey = (index: number, room: IRoom): IRoom['_id'] | number => room._id || index;
const RoomList = () => {
const t = useTranslation();
const isAnonymous = !useUserId();
const roomsList = useRoomList();
const avatarTemplate = useAvatarTemplate();
const sideBarItemTemplate = useTemplateByViewMode();
const { ref } = useResizeObserver({ debounceDelay: 100 });
const openedRoom = useOpenedRoom() ?? '';
const sidebarViewMode = useUserPreference<'extended' | 'medium' | 'condensed'>('sidebarViewMode') || 'extended';
const extended = sidebarViewMode === 'extended';
const itemData = useMemo(
() => ({
extended,
t,
SideBarItemTemplate: sideBarItemTemplate,
AvatarTemplate: avatarTemplate,
openedRoom,
sidebarViewMode,
isAnonymous,
}),
[avatarTemplate, extended, isAnonymous, openedRoom, sideBarItemTemplate, sidebarViewMode, t],
);
usePreventDefault(ref);
useShortcutOpenMenu(ref);
const roomsListStyle = css`
position: relative;
display: flex;
overflow-x: hidden;
overflow-y: hidden;
flex: 1 1 auto;
height: 100%;
&--embedded {
margin-top: 2rem;
}
&__list:not(:last-child) {
margin-bottom: 22px;
}
&__type {
display: flex;
flex-direction: row;
padding: 0 var(--sidebar-default-padding) 1rem var(--sidebar-default-padding);
color: var(--rooms-list-title-color);
font-size: var(--rooms-list-title-text-size);
align-items: center;
justify-content: space-between;
&-text--livechat {
flex: 1;
}
}
&__empty-room {
padding: 0 var(--sidebar-default-padding);
color: var(--rooms-list-empty-text-color);
font-size: var(--rooms-list-empty-text-size);
}
&__toolbar-search {
position: absolute;
z-index: 10;
left: 0;
overflow-y: scroll;
height: 100%;
background-color: var(--sidebar-background);
padding-block-start: 12px;
}
@media (max-width: 400px) {
padding: 0 calc(var(--sidebar-small-default-padding) - 4px);
&__type,
&__empty-room {
padding: 0 calc(var(--sidebar-small-default-padding) - 4px) 0.5rem calc(var(--sidebar-small-default-padding) - 4px);
}
}
`;
return (
<Box className={[roomsListStyle, 'sidebar--custom-colors'].filter(Boolean)}>
<Box h='full' w='full' ref={ref}>
<Virtuoso
totalCount={roomsList.length}
data={roomsList}
components={{ Item: RoomListRowWrapper, List: RoomListWrapper, Scroller: VirtuosoScrollbars }}
computeItemKey={computeItemKey}
itemContent={(_, data): ReactElement => <RoomListRow data={itemData} item={data} />}
/>
</Box>
</Box>
);
};
export default RoomList;