Skip to content

Commit

Permalink
[IMPROVE] Message rewrite (#24866)
Browse files Browse the repository at this point in the history
Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com>
  • Loading branch information
juliajforesti and ggazzo committed Apr 2, 2022
1 parent 93546ea commit c4829b5
Show file tree
Hide file tree
Showing 15 changed files with 384 additions and 163 deletions.
2 changes: 1 addition & 1 deletion app/theme/client/imports/general/variables.css
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@
--message-box-container-border-color: var(--color-gray-medium);
--message-box-container-border-width: var(--border);
--message-box-container-border-radius: var(--border-radius);
--message-box-editing-color: #fff5df;
--message-box-editing-color: #fff6d6;
--message-box-popover-title-text-color: var(--color-gray);
--message-box-popover-title-text-size: 0.75rem;

Expand Down
97 changes: 50 additions & 47 deletions client/views/room/MessageList/MessageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useTranslation } from '../../../contexts/TranslationContext';
import { useUserSubscription } from '../../../contexts/UserContext';
import { useFormatDate } from '../../../hooks/useFormatDate';
import { MessageProvider } from '../providers/MessageProvider';
import { SelectedMessagesProvider } from '../providers/SelectedMessagesProvider';
import Message from './components/Message';
import MessageSystem from './components/MessageSystem';
import { ThreadMessagePreview } from './components/ThreadMessagePreview';
Expand All @@ -31,61 +32,63 @@ export const MessageList: FC<{ rid: IRoom['_id'] }> = ({ rid }) => {
return (
<MessageListProvider rid={rid}>
<MessageProvider rid={rid} broadcast={isBroadcast}>
<MessageHighlightProvider>
{messages.map((message, index, arr) => {
const previous = arr[index - 1];
<SelectedMessagesProvider>
<MessageHighlightProvider>
{messages.map((message, index, arr) => {
const previous = arr[index - 1];

const isSequential = isMessageSequential(message, previous, messageGroupingPeriod);
const isSequential = isMessageSequential(message, previous, messageGroupingPeriod);

const isNewDay = isMessageNewDay(message, previous);
const isFirstUnread = isMessageFirstUnread(subscription, message, previous);
const isUserOwnMessage = isOwnUserMessage(message, subscription);
const shouldShowDivider = isNewDay || isFirstUnread;
const isNewDay = isMessageNewDay(message, previous);
const isFirstUnread = isMessageFirstUnread(subscription, message, previous);
const isUserOwnMessage = isOwnUserMessage(message, subscription);
const shouldShowDivider = isNewDay || isFirstUnread;

const shouldShowAsSequential = isSequential && !isNewDay;
const shouldShowAsSequential = isSequential && !isNewDay;

const isSystemMessage = MessageTypes.isSystemMessage(message);
const shouldShowMessage = !isThreadMessage(message) && !isSystemMessage;
const isSystemMessage = MessageTypes.isSystemMessage(message);
const shouldShowMessage = !isThreadMessage(message) && !isSystemMessage;

return (
<Fragment key={message._id}>
{shouldShowDivider && (
<MessageDivider unreadLabel={isFirstUnread ? t('Unread_Messages').toLowerCase() : undefined}>
{isNewDay && format(message.ts)}
</MessageDivider>
)}
return (
<Fragment key={message._id}>
{shouldShowDivider && (
<MessageDivider unreadLabel={isFirstUnread ? t('Unread_Messages').toLowerCase() : undefined}>
{isNewDay && format(message.ts)}
</MessageDivider>
)}

{shouldShowMessage && (
<Message
id={message._id}
data-id={message._id}
data-system-message={Boolean(message.t)}
data-mid={message._id}
data-unread={isFirstUnread}
data-sequential={isSequential}
data-own={isUserOwnMessage}
sequential={shouldShowAsSequential}
message={message}
subscription={subscription}
/>
)}
{shouldShowMessage && (
<Message
id={message._id}
data-id={message._id}
data-system-message={Boolean(message.t)}
data-mid={message._id}
data-unread={isFirstUnread}
data-sequential={isSequential}
data-own={isUserOwnMessage}
sequential={shouldShowAsSequential}
message={message}
subscription={subscription}
/>
)}

{isThreadMessage(message) && (
<ThreadMessagePreview
data-system-message={Boolean(message.t)}
data-mid={message._id}
data-unread={isFirstUnread}
data-sequential={isSequential}
sequential={shouldShowAsSequential}
message={message as IThreadMessage}
/>
)}
{isThreadMessage(message) && (
<ThreadMessagePreview
data-system-message={Boolean(message.t)}
data-mid={message._id}
data-unread={isFirstUnread}
data-sequential={isSequential}
sequential={shouldShowAsSequential}
message={message as IThreadMessage}
/>
)}

{isSystemMessage && <MessageSystem message={message} />}
</Fragment>
);
})}
</MessageHighlightProvider>
{isSystemMessage && <MessageSystem message={message} />}
</Fragment>
);
})}
</MessageHighlightProvider>
</SelectedMessagesProvider>
</MessageProvider>
</MessageListProvider>
);
Expand Down
24 changes: 18 additions & 6 deletions client/views/room/MessageList/components/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
/* eslint-disable complexity */
import { Message as MessageTemplate, MessageLeftContainer, MessageContainer, MessageBody } from '@rocket.chat/fuselage';
import { Message as MessageTemplate, MessageLeftContainer, MessageContainer, MessageBody, CheckBox } from '@rocket.chat/fuselage';
import { useToggle } from '@rocket.chat/fuselage-hooks';
import React, { FC, memo } from 'react';

import { IMessage } from '../../../../../definition/IMessage';
import { ISubscription } from '../../../../../definition/ISubscription';
import UserAvatar from '../../../../components/avatar/UserAvatar';
import { useIsMessageHighlight } from '../contexts/MessageHighlightContext';
import { useIsSelecting, useToggleSelect, useIsSelectedMessage, useCountSelected } from '../contexts/SelectedMessagesContext';
import MessageContent from './MessageContent';
import MessageContentIgnored from './MessageContentIgnored';
import MessageHeader from './MessageHeader';
import { MessageIndicators } from './MessageIndicators';
import Toolbox from './Toolbox';

const style = { backgroundColor: 'var(--message-box-editing-color)' };

const Message: FC<{ message: IMessage; sequential: boolean; subscription?: ISubscription; id: IMessage['_id'] }> = ({
message,
sequential,
Expand All @@ -24,18 +23,31 @@ const Message: FC<{ message: IMessage; sequential: boolean; subscription?: ISubs
const isMessageHighlight = useIsMessageHighlight(message._id);
const [isMessageIgnored, toggleMessageIgnored] = useToggle(message.ignored);

const isSelecting = useIsSelecting();
const toggleSelected = useToggleSelect(message._id);
const isSelected = useIsSelectedMessage(message._id);
useCountSelected();

return (
<MessageTemplate {...props} style={isMessageHighlight ? style : undefined}>
<MessageTemplate
{...props}
onClick={isSelecting ? toggleSelected : undefined}
isSelected={isSelected}
isEditing={isMessageHighlight}
// highlight={isMessageHighlight}
data-qa-editing={isMessageHighlight}
data-qa-selected={isSelected}
>
<MessageLeftContainer>
{!sequential && message.u.username && <UserAvatar username={message.u.username} size={'x36'} />}
{!sequential && message.u.username && !isSelecting && <UserAvatar username={message.u.username} size={'x36'} />}
{isSelecting && <CheckBox checked={isSelected} onChange={toggleSelected} />}
{sequential && <MessageIndicators message={message} />}
</MessageLeftContainer>

<MessageContainer>
{!sequential && <MessageHeader message={message} />}

{!isMessageIgnored && <MessageContent id={message._id} message={message} subscription={subscription} sequential={sequential} />}

{isMessageIgnored && (
<MessageBody>
<MessageContentIgnored onShowMessageIgnored={toggleMessageIgnored} />
Expand Down
7 changes: 7 additions & 0 deletions client/views/room/MessageList/components/Toolbox/Toolbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useSettings } from '../../../../../contexts/SettingsContext';
import { useTranslation } from '../../../../../contexts/TranslationContext';
import { useUser, useUserRoom, useUserSubscription } from '../../../../../contexts/UserContext';
import { getTabBarContext } from '../../../lib/Toolbox/ToolboxContext';
import { useIsSelecting } from '../../contexts/SelectedMessagesContext';
import { MessageActionMenu } from './MessageActionMenu';

export const Toolbox: FC<{ message: IMessage }> = ({ message }) => {
Expand All @@ -31,6 +32,12 @@ export const Toolbox: FC<{ message: IMessage }> = ({ message }) => {

const tabbar = getTabBarContext(message.rid);

const isSelecting = useIsSelecting();

if (isSelecting) {
return null;
}

return (
<MessageToolbox>
{messageActions.map((action) => (
Expand Down
60 changes: 60 additions & 0 deletions client/views/room/MessageList/contexts/SelectedMessagesContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { OffCallbackHandler } from '@rocket.chat/emitter';
import { createContext, useCallback, useContext, useMemo } from 'react';
import { useSubscription } from 'use-subscription';

import { selectedMessageStore } from '../../providers/SelectedMessagesProvider';

type SelectMessageContextValue = {
selectedMessageStore: typeof selectedMessageStore;
};

export const SelectedMessageContext = createContext({
selectedMessageStore,
} as SelectMessageContextValue);

export const useIsSelectedMessage = (mid: string): boolean => {
const { selectedMessageStore } = useContext(SelectedMessageContext);
const subscription = useMemo(
() => ({
getCurrentValue: (): boolean => selectedMessageStore.isSelected(mid),
subscribe: (callback: () => void): OffCallbackHandler => selectedMessageStore.on(mid, callback),
}),
[mid, selectedMessageStore],
);
return useSubscription(subscription);
};

export const useIsSelecting = (): boolean => {
const { selectedMessageStore } = useContext(SelectedMessageContext);

return useSubscription(
useMemo(
() => ({
getCurrentValue: (): boolean => selectedMessageStore.getIsSelecting(),
subscribe: (callback: () => void): OffCallbackHandler => selectedMessageStore.on('toggleIsSelecting', callback),
}),
[selectedMessageStore],
),
);
};

export const useToggleSelect = (mid: string): (() => void) => {
const { selectedMessageStore } = useContext(SelectedMessageContext);
return useCallback(() => {
selectedMessageStore.toggle(mid);
}, [mid, selectedMessageStore]);
};

export const useCountSelected = (): number => {
const { selectedMessageStore } = useContext(SelectedMessageContext);

return useSubscription(
useMemo(
() => ({
getCurrentValue: (): number => selectedMessageStore.count(),
subscribe: (callback: () => void): OffCallbackHandler => selectedMessageStore.on('change', callback),
}),
[selectedMessageStore],
),
);
};
39 changes: 22 additions & 17 deletions client/views/room/Room/Room.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import VerticalBarOldActions from '../components/VerticalBarOldActions';
import { useRoom } from '../contexts/RoomContext';
import AppsContextualBar from '../contextualBar/Apps';
import { useAppsContextualBar } from '../hooks/useAppsContextualBar';
import { SelectedMessagesProvider } from '../providers/SelectedMessagesProvider';
import { useTab, useTabBarOpen, useTabBarClose, useTabBarOpenUserInfo } from '../providers/ToolboxProvider';
import LazyComponent from './LazyComponent';

Expand Down Expand Up @@ -44,27 +45,31 @@ export const Room: FC<{}> = () => {
{tab && (
<RoomTemplate.Aside data-qa-tabbar-name={tab.id}>
<ErrorBoundary>
{typeof tab.template === 'string' && (
<VerticalBarOldActions {...tab} name={tab.template} tabBar={tabBar} rid={room._id} _id={room._id} />
)}
{typeof tab.template !== 'string' && (
<LazyComponent template={tab.template} tabBar={tabBar} rid={room._id} teamId={room.teamId} _id={room._id} />
)}
<SelectedMessagesProvider>
{typeof tab.template === 'string' && (
<VerticalBarOldActions {...tab} name={tab.template} tabBar={tabBar} rid={room._id} _id={room._id} />
)}
{typeof tab.template !== 'string' && (
<LazyComponent template={tab.template} tabBar={tabBar} rid={room._id} teamId={room.teamId} _id={room._id} />
)}
</SelectedMessagesProvider>
</ErrorBoundary>
</RoomTemplate.Aside>
)}
{appsContextualBarContext && (
<RoomTemplate.Aside data-qa-tabbar-name={appsContextualBarContext.viewId}>
<ErrorBoundary>
<LazyComponent
template={AppsContextualBar}
viewId={appsContextualBarContext.viewId}
roomId={appsContextualBarContext.roomId}
payload={appsContextualBarContext.payload}
appInfo={appsContextualBarContext.appInfo}
/>
</ErrorBoundary>
</RoomTemplate.Aside>
<SelectedMessagesProvider>
<RoomTemplate.Aside data-qa-tabbar-name={appsContextualBarContext.viewId}>
<ErrorBoundary>
<LazyComponent
template={AppsContextualBar}
viewId={appsContextualBarContext.viewId}
roomId={appsContextualBarContext.roomId}
payload={appsContextualBarContext.payload}
appInfo={appsContextualBarContext.appInfo}
/>
</ErrorBoundary>
</RoomTemplate.Aside>
</SelectedMessagesProvider>
)}
</RoomTemplate>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { Field, Select, FieldGroup } from '@rocket.chat/fuselage';
import React, { useState, useMemo } from 'react';
import { Field, Select, FieldGroup, SelectOption } from '@rocket.chat/fuselage';
import React, { useState, useMemo, FC } from 'react';

import { IRoom } from '../../../../../definition/IRoom';
import VerticalBar from '../../../../components/VerticalBar';
import { useTranslation } from '../../../../contexts/TranslationContext';
import { useTabBarClose } from '../../providers/ToolboxProvider';
import FileExport from './FileExport';
import MailExportForm from './MailExportForm';

function ExportMessages({ rid }) {
type ExportMessagesProps = {
rid: IRoom['_id'];
};
const ExportMessages: FC<ExportMessagesProps> = ({ rid }) => {
const t = useTranslation();
const close = useTabBarClose();

const [type, setType] = useState('email');

const exportOptions = useMemo(
const exportOptions = useMemo<SelectOption[]>(
() => [
['email', t('Send_via_email')],
['file', t('Export_as_file')],
Expand All @@ -32,7 +36,7 @@ function ExportMessages({ rid }) {
<Field>
<Field.Label>{t('Method')}</Field.Label>
<Field.Row>
<Select value={type} onChange={(value) => setType(value)} placeholder={t('Type')} options={exportOptions} />
<Select value={type} onChange={(value): void => setType(value)} placeholder={t('Type')} options={exportOptions} />
</Field.Row>
</Field>
</FieldGroup>
Expand All @@ -41,6 +45,6 @@ function ExportMessages({ rid }) {
</VerticalBar.ScrollableContent>
</>
);
}
};

export default ExportMessages;
Loading

0 comments on commit c4829b5

Please sign in to comment.