feat(chat): improve messages rendering (#21323)

* feat(chat): improve messages rendering

This commit adds memoization and comparsion functions to the chat pages
and chat messages to prevent whole chat re-renders and unnecessary
message re-renders caused by React's default shallow comparsion mechanism.
The comparsion functions ensure that only the pertinent message attributes
trigger a component's re-render.

Messages sent by a user are not updated (re-rendered) when their role
change. In other words, the chat messages reflect the user's role at the
time the message was sent. Altering the message to reflect the user's
current role could confuse participants, as it would modify the context
of the past conversation. This behavior has been validated by the UI/UX
team and brings benefits such as performance improvements and consistent
behavior with how playback handles such messages.

* fix linter errors
This commit is contained in:
Arthur B. Grossi 2024-10-03 10:10:52 -03:00 committed by GitHub
parent 09fcb3ad42
commit 1dbc773aee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 38 additions and 4 deletions

View File

@ -1,4 +1,9 @@
import React, { useCallback, useEffect, useMemo } from 'react';
import React, {
memo,
useCallback,
useEffect,
useMemo,
} from 'react';
import { UpdatedEventDetailsForChatMessageDomElements } from 'bigbluebutton-html-plugin-sdk/dist/cjs/dom-element-manipulation/chat/message/types';
import { Message } from '/imports/ui/Types/message';
import { defineMessages, useIntl } from 'react-intl';
@ -304,4 +309,12 @@ const ChatMesssage: React.FC<ChatMessageProps> = ({
);
};
export default ChatMesssage;
function areChatMessagesEqual(prevProps: ChatMessageProps, nextProps: ChatMessageProps) {
const prevMessage = prevProps?.message;
const nextMessage = nextProps?.message;
return prevMessage?.createdAt === nextMessage?.createdAt
&& prevMessage?.user?.currentlyInMeeting === nextMessage?.user?.currentlyInMeeting
&& prevMessage?.recipientHasSeen === nextMessage.recipientHasSeen;
}
export default memo(ChatMesssage, areChatMessagesEqual);

View File

@ -1,5 +1,10 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import React, { useContext, useEffect, useState } from 'react';
import React, {
useContext,
useEffect,
useState,
memo,
} from 'react';
import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context';
import { UpdatedEventDetailsForChatMessageDomElements } from 'bigbluebutton-html-plugin-sdk/dist/cjs/dom-element-manipulation/chat/message/types';
import { HookEvents } from 'bigbluebutton-html-plugin-sdk/dist/cjs/core/enum';
@ -35,6 +40,20 @@ interface ChatListPageProps {
scrollRef: React.RefObject<HTMLDivElement>;
}
const areChatPagesEqual = (prevProps: ChatListPageProps, nextProps: ChatListPageProps) => {
const nextMessages = nextProps?.messages || [];
const prevMessages = prevProps?.messages || [];
if (nextMessages.length !== prevMessages.length) return false;
return nextMessages.every((nextMessage, idx) => {
const prevMessage = prevMessages[idx];
return (prevMessage.messageId === nextMessage.messageId
&& prevMessage.createdAt === nextMessage.createdAt
&& prevMessage?.user?.currentlyInMeeting === nextMessage?.user?.currentlyInMeeting
&& prevMessage?.recipientHasSeen === nextMessage?.recipientHasSeen
);
});
};
const ChatListPage: React.FC<ChatListPageProps> = ({
messages,
messageReadFeedbackEnabled,
@ -86,6 +105,8 @@ const ChatListPage: React.FC<ChatListPageProps> = ({
);
};
const MemoizedChatListPage = memo(ChatListPage, areChatPagesEqual);
const ChatListPageContainer: React.FC<ChatListPageContainerProps> = ({
page,
pageSize,
@ -128,7 +149,7 @@ const ChatListPageContainer: React.FC<ChatListPageContainerProps> = ({
}
setLoadedMessageGathering(page, chatMessageData);
return (
<ChatListPage
<MemoizedChatListPage
messages={chatMessageData}
lastSenderPreviousPage={lastSenderPreviousPage}
messageReadFeedbackEnabled={isPrivateReadFeedbackEnabled}