Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions apps/meteor/client/components/RoomIcon/RoomIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { IRoom, isDirectMessageRoom, isOmnichannelRoom } from '@rocket.chat/core-typings';
import { Icon } from '@rocket.chat/fuselage';
import React, { ComponentProps, FC } from 'react';
import React, { ComponentProps, ReactElement } from 'react';

import { ReactiveUserStatus } from '../UserStatus';
import { OmnichannelRoomIcon } from './OmnichannelRoomIcon';

export const RoomIcon: FC<{
export const RoomIcon = ({
room,
size = 'x16',
placement,
}: {
room: IRoom;
size: ComponentProps<typeof Icon>['size'];
highlighted?: boolean;
size?: ComponentProps<typeof Icon>['size'];
placement: 'sidebar' | 'default';
}> = ({ room, size = 'x16', placement }) => {
}): ReactElement | null => {
if (room.prid) {
return <Icon name='baloons' size={size} />;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Box } from '@rocket.chat/fuselage';
import { useResizeObserver } from '@rocket.chat/fuselage-hooks';
import { useSession, useUserPreference, useUserId, useTranslation } from '@rocket.chat/ui-contexts';
import React, { useRef, useEffect, useMemo } from 'react';
import React, { useMemo, ReactElement } from 'react';
import { Virtuoso } from 'react-virtuoso';

import { useAvatarTemplate } from '../hooks/useAvatarTemplate';
Expand All @@ -13,14 +13,14 @@ import { useTemplateByViewMode } from '../hooks/useTemplateByViewMode';
import Row from './Row';
import ScrollerWithCustomProps from './ScrollerWithCustomProps';

const RoomList = () => {
const RoomList = (): ReactElement => {
useSidebarPaletteColor();
const listRef = useRef();

const { ref } = useResizeObserver({ debounceDelay: 100 });

const openedRoom = useSession('openedRoom');
const openedRoom = (useSession('openedRoom') as string) || '';

const sidebarViewMode = useUserPreference('sidebarViewMode');
const sidebarViewMode = useUserPreference<'extended' | 'medium' | 'condensed'>('sidebarViewMode') || 'extended';
const sideBarItemTemplate = useTemplateByViewMode();
const avatarTemplate = useAvatarTemplate();
const extended = sidebarViewMode === 'extended';
Expand All @@ -44,18 +44,13 @@ const RoomList = () => {

usePreventDefault(ref);
useShortcutOpenMenu(ref);

useEffect(() => {
listRef.current?.resetAfterIndex(0);
}, [sidebarViewMode]);

return (
<Box h='full' w='full' ref={ref}>
<Virtuoso
totalCount={roomsList.length}
data={roomsList}
components={{ Scroller: ScrollerWithCustomProps }}
itemContent={(index, data) => <Row data={itemData} item={data} />}
itemContent={(_, data): ReactElement => <Row data={itemData} item={data} />}
/>
</Box>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import { IRoom, ISubscription } from '@rocket.chat/core-typings';
import { SidebarSection } from '@rocket.chat/fuselage';
import React, { memo } from 'react';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { ComponentType, memo, ReactElement } from 'react';

import { useAvatarTemplate } from '../hooks/useAvatarTemplate';
import { useTemplateByViewMode } from '../hooks/useTemplateByViewMode';
import OmnichannelSection from '../sections/OmnichannelSection';
import SideBarItemTemplateWithData from './SideBarItemTemplateWithData';

const sections = {
const sections: {
[key: string]: ComponentType<any>;
} = {
Omnichannel: OmnichannelSection,
};

const Row = ({ data, item }) => {
type RoomListRowProps = {
extended: boolean;
t: ReturnType<typeof useTranslation>;
SideBarItemTemplate: ReturnType<typeof useTemplateByViewMode>;
AvatarTemplate: ReturnType<typeof useAvatarTemplate>;
openedRoom: string;
sidebarViewMode: 'extended' | 'condensed' | 'medium';
isAnonymous: boolean;
};

const Row = ({ data, item }: { data: RoomListRowProps; item: ISubscription & IRoom }): ReactElement => {
const { extended, t, SideBarItemTemplate, AvatarTemplate, openedRoom, sidebarViewMode } = data;

if (typeof item === 'string') {
Expand Down
16 changes: 0 additions & 16 deletions apps/meteor/client/sidebar/RoomList/ScrollerWithCustomProps.js

This file was deleted.

14 changes: 14 additions & 0 deletions apps/meteor/client/sidebar/RoomList/ScrollerWithCustomProps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React, { forwardRef, ReactElement } from 'react';

import ScrollableContentWrapper from '../../components/ScrollableContentWrapper';

export default forwardRef<HTMLElement>(function ScrollerWithCustomProps(props, ref): ReactElement {
return (
<ScrollableContentWrapper
{...props}
ref={ref}
renderView={({ style, ...props }): ReactElement => <div {...props} style={{ ...style }} />}
renderTrackHorizontal={(props): ReactElement => <div {...props} style={{ display: 'none' }} className='track-horizontal' />}
/>
);
});
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
/* eslint-disable react/display-name */
import {
IMessage,
IRoom,
isDirectMessageRoom,
isMultipleDirectMessageRoom,
isOmnichannelRoom,
ISubscription,
} from '@rocket.chat/core-typings';
import { Badge, Sidebar } from '@rocket.chat/fuselage';
import { useLayout } from '@rocket.chat/ui-contexts';
import React, { memo } from 'react';
import { useLayout, useTranslation } from '@rocket.chat/ui-contexts';
import React, { AllHTMLAttributes, ComponentType, memo, ReactElement, ReactNode } from 'react';

import { RoomIcon } from '../../components/RoomIcon';
import { roomCoordinator } from '../../lib/rooms/roomCoordinator';
import RoomMenu from '../RoomMenu';
import { useAvatarTemplate } from '../hooks/useAvatarTemplate';
import { normalizeSidebarMessage } from './normalizeSidebarMessage';

const getMessage = (room, lastMessage, t) => {
const getMessage = (room: IRoom, lastMessage: IMessage | undefined, t: ReturnType<typeof useTranslation>): string | undefined => {
if (!lastMessage) {
return t('No_messages_yet');
}
Expand All @@ -17,28 +27,67 @@ const getMessage = (room, lastMessage, t) => {
if (lastMessage.u?.username === room.u?.username) {
return `${t('You')}: ${normalizeSidebarMessage(lastMessage, t)}`;
}
if (room.t === 'd' && room.uids && room.uids.length <= 2) {
if (isDirectMessageRoom(room) && !isMultipleDirectMessageRoom(room)) {
return normalizeSidebarMessage(lastMessage, t);
}
return `${lastMessage.u.name || lastMessage.u.username}: ${normalizeSidebarMessage(lastMessage, t)}`;
};

type RoomListRowProps = {
extended: boolean;
t: ReturnType<typeof useTranslation>;
SideBarItemTemplate: ComponentType<
{
icon: ReactNode;
title: ReactNode;
avatar: ReactNode;
// actions: unknown;
href: string;
time?: Date;
menu?: ReactNode;
menuOptions?: unknown;
subtitle?: ReactNode;
titleIcon?: string;
badges?: ReactNode;
threadUnread?: boolean;
unread?: boolean;
selected?: boolean;
is?: string;
} & AllHTMLAttributes<HTMLElement>
>;
AvatarTemplate: ReturnType<typeof useAvatarTemplate>;
openedRoom?: string;
// sidebarViewMode: 'extended';
isAnonymous?: boolean;

room: ISubscription & IRoom;
lastMessage?: IMessage;
id?: string;
/* @deprecated */
style?: AllHTMLAttributes<HTMLElement>['style'];

selected: boolean;

sidebarViewMode: unknown;
};

function SideBarItemTemplateWithData({
room,
id,
extended,
selected,
style,

extended,
SideBarItemTemplate,
AvatarTemplate,
t,
style,
// sidebarViewMode,
isAnonymous,
}) {
}: RoomListRowProps): ReactElement {
const { sidebar } = useLayout();

const href = roomCoordinator.getRouteLink(room.t, room);
const title = roomCoordinator.getRoomName(room.t, room);
const href = roomCoordinator.getRouteLink(room.t, room) || '';
const title = roomCoordinator.getRoomName(room.t, room) || '';

const {
lastMessage,
Expand All @@ -55,14 +104,15 @@ function SideBarItemTemplateWithData({
cl,
} = room;

const highlighted = !hideUnreadStatus && (alert || unread);
const highlighted = Boolean(!hideUnreadStatus && (alert || unread));
const icon = (
<Sidebar.Item.Icon highlighted={highlighted}>
<RoomIcon highlighted={highlighted} room={room} placement='sidebar' />
// TODO: Remove icon='at'
<Sidebar.Item.Icon highlighted={highlighted} icon='at'>
<RoomIcon room={room} placement='sidebar' />
</Sidebar.Item.Icon>
);

const isQueued = room.status === 'queued';
const isQueued = isOmnichannelRoom(room) && room.status === 'queued';

const threadUnread = tunread.length > 0;
const message = extended && getMessage(room, lastMessage, t);
Expand All @@ -74,7 +124,8 @@ function SideBarItemTemplateWithData({
const showBadge = !hideUnreadStatus || (!hideMentionStatus && userMentions);
const badges =
showBadge && isUnread ? (
<Badge style={{ flexShrink: 0 }} variant={variant}>
// TODO: Remove any
<Badge {...({ style: { flexShrink: 0 } } as any)} variant={variant}>
{unread + tunread?.length}
</Badge>
) : null;
Expand All @@ -84,12 +135,12 @@ function SideBarItemTemplateWithData({
is='a'
id={id}
data-qa='sidebar-item'
aria-level='2'
aria-level={2}
unread={highlighted}
threadUnread={threadUnread}
selected={selected}
href={href}
onClick={() => !selected && sidebar.toggle()}
onClick={(): void => !selected && sidebar.toggle()}
aria-label={title}
title={title}
time={lastMessage?.ts}
Expand All @@ -101,7 +152,7 @@ function SideBarItemTemplateWithData({
menu={
!isAnonymous &&
!isQueued &&
(() => (
((): ReactElement => (
<RoomMenu
alert={alert}
threadUnread={threadUnread}
Expand All @@ -111,27 +162,34 @@ function SideBarItemTemplateWithData({
type={type}
cl={cl}
name={title}
status={room.status}
/>
))
}
/>
);
}

function safeDateNotEqualCheck(a, b) {
function safeDateNotEqualCheck(a: Date | string | undefined, b: Date | string | undefined): boolean {
if (!a || !b) {
return a !== b;
}
return new Date(a).toISOString() !== new Date(b).toISOString();
}

const propsAreEqual = (prevProps, nextProps) => {
if (
['id', 'style', 'extended', 'selected', 'SideBarItemTemplate', 'AvatarTemplate', 't', 'sidebarViewMode'].some(
(key) => prevProps[key] !== nextProps[key],
)
) {
const keys: (keyof RoomListRowProps)[] = [
'id',
'style',
'extended',
'selected',
'SideBarItemTemplate',
'AvatarTemplate',
't',
'sidebarViewMode',
];

// eslint-disable-next-line react/no-multi-comp
export default memo(SideBarItemTemplateWithData, (prevProps, nextProps) => {
if (keys.some((key) => prevProps[key] !== nextProps[key])) {
return false;
}

Expand All @@ -151,14 +209,12 @@ const propsAreEqual = (prevProps, nextProps) => {
if (prevProps.room.alert !== nextProps.room.alert) {
return false;
}
if (prevProps.room.v?.status !== nextProps.room.v?.status) {
if (isOmnichannelRoom(prevProps.room) && isOmnichannelRoom(nextProps.room) && prevProps.room.v.status !== nextProps.room.v.status) {
return false;
}
if (prevProps.room.teamMain !== nextProps.room.teamMain) {
return false;
}

return true;
};

export default memo(SideBarItemTemplateWithData, propsAreEqual);
});
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { IMessage } from '@rocket.chat/core-typings';
import { escapeHTML } from '@rocket.chat/string-helpers';
import { useTranslation } from '@rocket.chat/ui-contexts';

import { filterMarkdown } from '../../../app/markdown/lib/markdown';

export const normalizeSidebarMessage = (message, t) => {
export const normalizeSidebarMessage = (message: IMessage, t: ReturnType<typeof useTranslation>): string | undefined => {
if (message.msg) {
return escapeHTML(filterMarkdown(message.msg));
}

if (message.attachments) {
const attachment = message.attachments.find((attachment) => attachment.title || attachment.description);

if (attachment && attachment.description) {
if (attachment?.description) {
return escapeHTML(attachment.description);
}

if (attachment && attachment.title) {
if (attachment?.title) {
return escapeHTML(attachment.title);
}

Expand Down
Loading