Skip to content

Commit

Permalink
Chore: Add error boundary to message component (#25223)
Browse files Browse the repository at this point in the history
  • Loading branch information
gabriellsh committed Apr 19, 2022
1 parent 9e2bb3c commit f6cac21
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 58 deletions.
10 changes: 7 additions & 3 deletions apps/meteor/client/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { Component, ReactNode } from 'react';
import { Component, ReactNode, ErrorInfo } from 'react';

export class ErrorBoundary extends Component<{}, { hasError: boolean }> {
export class ErrorBoundary extends Component<{ fallback?: ReactNode }, { hasError: boolean }> {
state = { hasError: false };

static getDerivedStateFromError(): { hasError: boolean } {
return { hasError: true };
}

componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
console.error('Uncaught Error:', error, errorInfo);
}

render(): ReactNode {
if (this.state.hasError) {
return null;
return this.props.fallback || null;
}

return this.props.children;
Expand Down
113 changes: 58 additions & 55 deletions apps/meteor/client/views/room/MessageList/MessageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useUserSubscription } from '../../../contexts/UserContext';
import { useFormatDate } from '../../../hooks/useFormatDate';
import { MessageProvider } from '../providers/MessageProvider';
import { SelectedMessagesProvider } from '../providers/SelectedMessagesProvider';
import MessageListErrorBoundary from './MessageListErrorBoundary';
import Message from './components/Message';
import MessageSystem from './components/MessageSystem';
import { ThreadMessagePreview } from './components/ThreadMessagePreview';
Expand All @@ -29,69 +30,71 @@ export const MessageList: FC<{ rid: IRoom['_id'] }> = ({ rid }) => {
const format = useFormatDate();

return (
<MessageListProvider rid={rid}>
<MessageProvider rid={rid} broadcast={isBroadcast}>
<SelectedMessagesProvider>
<MessageHighlightProvider>
{messages.map((message, index, arr) => {
const previous = arr[index - 1];
<MessageListErrorBoundary>
<MessageListProvider rid={rid}>
<MessageProvider rid={rid} broadcast={isBroadcast}>
<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}
data-qa-type='message'
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}
data-qa-type='message'
sequential={shouldShowAsSequential}
message={message}
subscription={subscription}
/>
)}

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

{isSystemMessage && <MessageSystem message={message} />}
</Fragment>
);
})}
</MessageHighlightProvider>
</SelectedMessagesProvider>
</MessageProvider>
</MessageListProvider>
{isSystemMessage && <MessageSystem message={message} />}
</Fragment>
);
})}
</MessageHighlightProvider>
</SelectedMessagesProvider>
</MessageProvider>
</MessageListProvider>
</MessageListErrorBoundary>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { States, StatesIcon, StatesTitle, StatesSubtitle, StatesActions, StatesAction, Icon } from '@rocket.chat/fuselage';
import React, { ReactElement, ReactNode } from 'react';

import { ErrorBoundary } from '../../../components/ErrorBoundary';
import { useTranslation } from '../../../contexts/TranslationContext';

const MessageListErrorBoundary = ({ children }: { children: ReactNode }): ReactElement => {
const t = useTranslation();
return (
<ErrorBoundary
children={children}
fallback={
<States>
<StatesIcon name='circle-exclamation' variation='danger' />
<StatesTitle>{t('Error')}</StatesTitle>
<StatesSubtitle>{t('Error_something_went_wrong')}</StatesSubtitle>
<StatesActions>
<StatesAction
onClick={(): void => {
location.reload();
}}
>
<Icon name='reload' /> {t('Reload')}
</StatesAction>
</StatesActions>
</States>
}
/>
);
};

export default MessageListErrorBoundary;
1 change: 1 addition & 0 deletions apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1679,6 +1679,7 @@
"Enterprise_License_Description": "If your workspace is registered and license is provided by Rocket.Chat Cloud you don't need to manually update the license here.",
"Entertainment": "Entertainment",
"Error": "Error",
"Error_something_went_wrong": "Oops! Something went wrong. Please reload the page or contact an administrator.",
"Error_404": "Error:404",
"Error_changing_password": "Error changing password",
"Error_loading_pages": "Error loading pages",
Expand Down

0 comments on commit f6cac21

Please sign in to comment.