From 075a410d5790844cba862ad2c7f4112a25762fde Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Mon, 3 Jun 2024 13:29:25 +0300 Subject: [PATCH 1/2] CW-Message-performance Increase message performance Add useDeepCompareEffect. Restructure component --- .../ChatComponent/ChatComponent.tsx | 51 ++++- .../components/ChatContent/ChatContent.tsx | 214 +++++++++--------- .../components/ChatContent/index.ts | 1 + .../Chat/ChatMessage/ChatMessage.tsx | 12 +- .../useCases/useDiscussionMessagesById.ts | 6 +- 5 files changed, 168 insertions(+), 116 deletions(-) diff --git a/src/pages/common/components/ChatComponent/ChatComponent.tsx b/src/pages/common/components/ChatComponent/ChatComponent.tsx index 0e790cba97..f3c0788469 100644 --- a/src/pages/common/components/ChatComponent/ChatComponent.tsx +++ b/src/pages/common/components/ChatComponent/ChatComponent.tsx @@ -15,16 +15,22 @@ import { debounce, delay, omit } from "lodash"; import { v4 as uuidv4 } from "uuid"; import { selectUser } from "@/pages/Auth/store/selectors"; import { ChatService, DiscussionMessageService, FileService } from "@/services"; +import { Separator } from "@/shared/components"; import { ChatType, DiscussionMessageOwnerType, GovernanceActions, LastSeenEntity, + QueryParamKey, } from "@/shared/constants"; import { FILES_ACCEPTED_EXTENSIONS } from "@/shared/constants"; import { HotKeys } from "@/shared/constants/keyboardKeys"; import { ChatMessageToUserDiscussionMessageConverter } from "@/shared/converters"; -import { useZoomDisabling, useImageSizeCheck } from "@/shared/hooks"; +import { + useZoomDisabling, + useImageSizeCheck, + useQueryParams, +} from "@/shared/hooks"; import { ArrowInCircleIcon, PlusIcon, SendIcon } from "@/shared/icons"; import { LinkPreviewData } from "@/shared/interfaces"; import { CreateDiscussionMessageDto } from "@/shared/interfaces/api/discussionMessages"; @@ -73,7 +79,9 @@ import { MessageLinkPreview, MessageReply, ChatFilePreview, + MessageInfoWithDateList, } from "./components"; +import { checkIsLastSeenInPreviousDay } from "./components/ChatContent/utils"; import { useChatChannelChatAdapter, useDiscussionChatAdapter } from "./hooks"; import { getLastNonUserMessage } from "./utils"; import styles from "./ChatComponent.module.scss"; @@ -152,6 +160,9 @@ export default function ChatComponent({ onInternalLinkClick, }: ChatComponentInterface) { const dispatch = useDispatch(); + const queryParams = useQueryParams(); + const shouldDisplayMessagesOnlyWithUncheckedItems = + queryParams[QueryParamKey.Unchecked] === "true"; const { checkImageSize } = useImageSizeCheck(); useZoomDisabling(); const editorRef = useRef(null); @@ -289,7 +300,43 @@ export default function ChatComponent({ ), [discussionMessages], ); - const dateList = useMemo(() => Object.keys(messages), [messages]); + + const dateList: MessageInfoWithDateList = useMemo(() => { + const messagesDates = Object.keys(messages); + const messagesWithInfo = messagesDates.map((day, dayIndex) => { + const date = new Date(Number(day)); + const currentMessages = shouldDisplayMessagesOnlyWithUncheckedItems + ? messages[Number(day)].filter((message) => message.hasUncheckedItems) + : messages[Number(day)]; + const previousDayMessages = + messages[Number(messagesDates[dayIndex + 1])] || []; + const isLastSeenInPreviousDay = checkIsLastSeenInPreviousDay( + previousDayMessages, + lastSeenItem?.id, + ); + const isMyMessageFirst = + checkIsUserDiscussionMessage(currentMessages[0]) && + currentMessages[0].ownerId === userId; + const newSeparatorEl = ( +
  • + New +
  • + ); + + return { + day, + date, + currentMessages, + isLastSeenInPreviousDay, + isMyMessageFirst, + newSeparatorEl, + }; + }); + + return messagesWithInfo; + }, [messages]); + + // const dateListWith const [newMessages, setMessages] = useState< CreateDiscussionMessageDtoWithFilesPreview[] diff --git a/src/pages/common/components/ChatComponent/components/ChatContent/ChatContent.tsx b/src/pages/common/components/ChatComponent/components/ChatContent/ChatContent.tsx index 831f9feea0..41c6772672 100644 --- a/src/pages/common/components/ChatComponent/components/ChatContent/ChatContent.tsx +++ b/src/pages/common/components/ChatComponent/components/ChatContent/ChatContent.tsx @@ -6,7 +6,6 @@ import React, { ForwardRefRenderFunction, useImperativeHandle, forwardRef, - memo, } from "react"; import { useSelector } from "react-redux"; import { scroller, animateScroll } from "react-scroll"; @@ -34,14 +33,23 @@ import { import { Loader } from "@/shared/ui-kit"; import { InternalLinkData } from "@/shared/utils"; import { formatDate } from "@/shared/utils"; -import { Separator } from "./components"; -import { checkIsLastSeenInPreviousDay } from "./utils"; import styles from "./ChatContent.module.scss"; export interface ChatContentRef { scrollToContainerBottom: () => void; } +export type MessageInfoWithDate = { + day: string; + date: Date; + currentMessages: DiscussionMessageWithParsedText[]; + isLastSeenInPreviousDay: boolean; + isMyMessageFirst: boolean; + newSeparatorEl: JSX.Element; +}; + +export type MessageInfoWithDateList = MessageInfoWithDate[]; + interface ChatContentInterface { type: ChatType; commonMember: CommonMember | null; @@ -49,7 +57,7 @@ interface ChatContentInterface { chatWrapperId: string; messages: Record; discussionMessages: DiscussionMessageWithParsedText[] | null; - dateList: string[]; + dateList: MessageInfoWithDateList; lastSeenItem?: CommonFeedObjectUserUnique["lastSeen"]; hasPermissionToHide: boolean; users: User[]; @@ -101,7 +109,6 @@ const ChatContent: ForwardRefRenderFunction< onFeedItemClick, onInternalLinkClick, isEmpty, - messages, isChatChannel, isMessageEditAllowed, fetchReplied, @@ -115,8 +122,6 @@ const ChatContent: ForwardRefRenderFunction< const isTabletView = useIsTabletView(); const queryParams = useQueryParams(); const messageIdParam = queryParams[QueryParamKey.Message]; - const shouldDisplayMessagesOnlyWithUncheckedItems = - queryParams[QueryParamKey.Unchecked] === "true"; const [highlightedMessageId, setHighlightedMessageId] = useState( () => (typeof messageIdParam === "string" && messageIdParam) || null, @@ -238,107 +243,102 @@ const ChatContent: ForwardRefRenderFunction< return ( <> - {dateListReverse.map((day, dayIndex) => { - const date = new Date(Number(day)); - const currentMessages = shouldDisplayMessagesOnlyWithUncheckedItems - ? messages[Number(day)].filter((message) => message.hasUncheckedItems) - : messages[Number(day)]; - const previousDayMessages = - messages[Number(dateListReverse[dayIndex + 1])] || []; - const isLastSeenInPreviousDay = checkIsLastSeenInPreviousDay( - previousDayMessages, - lastSeenItem?.id, - ); - const isMyMessageFirst = - checkIsUserDiscussionMessage(currentMessages[0]) && - currentMessages[0].ownerId === userId; - const newSeparatorEl = ( -
  • - New -
  • - ); + {dateListReverse.map( + ( + { + day, + currentMessages, + date, + isLastSeenInPreviousDay, + isMyMessageFirst, + newSeparatorEl, + }, + dayIndex, + ) => { + return ( + 0} + transition={isTabletView ? ModalTransition.FadeIn : null} + className={styles.messageListTransitionContainer} + style={{ zIndex: dateListReverse.length - dayIndex }} + > + {currentMessages.length > 0 && ( +
      + {isLastSeenInPreviousDay && + !isMyMessageFirst && + newSeparatorEl} +
    • + {isToday(date) ? "Today" : formatDate(date)} +
    • + {currentMessages.map((message, messageIndex) => { + const nextMessage = currentMessages[messageIndex + 1]; + const isMyMessageNext = + checkIsUserDiscussionMessage(nextMessage) && + nextMessage.ownerId === userId; + const messageEl = isChatChannel ? ( + + ) : ( + + ); - return ( - 0} - transition={isTabletView ? ModalTransition.FadeIn : null} - className={styles.messageListTransitionContainer} - style={{ zIndex: dateListReverse.length - dayIndex }} - > - {currentMessages.length > 0 && ( -
        - {isLastSeenInPreviousDay && !isMyMessageFirst && newSeparatorEl} -
      • - {isToday(date) ? "Today" : formatDate(date)} -
      • - {currentMessages.map((message, messageIndex) => { - const nextMessage = currentMessages[messageIndex + 1]; - const isMyMessageNext = - checkIsUserDiscussionMessage(nextMessage) && - nextMessage.ownerId === userId; - const messageEl = isChatChannel ? ( - - ) : ( - - ); + if ( + message.id !== lastSeenItem?.id || + messageIndex === currentMessages.length - 1 || + isMyMessageNext + ) { + return messageEl; + } - if ( - message.id !== lastSeenItem?.id || - messageIndex === currentMessages.length - 1 || - isMyMessageNext - ) { - return messageEl; - } - - return ( - - {messageEl} - {newSeparatorEl} - - ); - })} -
      - )} -
      - ); - })} + return ( + + {messageEl} + {newSeparatorEl} + + ); + })} +
    + )} +
    + ); + }, + )} {isLoading && (
    @@ -356,4 +356,4 @@ const ChatContent: ForwardRefRenderFunction< ); }; -export default memo(forwardRef(ChatContent)); +export default forwardRef(ChatContent); diff --git a/src/pages/common/components/ChatComponent/components/ChatContent/index.ts b/src/pages/common/components/ChatComponent/components/ChatContent/index.ts index f3f074bdcc..a0eed16f6b 100644 --- a/src/pages/common/components/ChatComponent/components/ChatContent/index.ts +++ b/src/pages/common/components/ChatComponent/components/ChatContent/index.ts @@ -1,2 +1,3 @@ export { default as ChatContent } from "./ChatContent"; export type { ChatContentRef } from "./ChatContent"; +export type { MessageInfoWithDateList } from "./ChatContent"; diff --git a/src/shared/components/Chat/ChatMessage/ChatMessage.tsx b/src/shared/components/Chat/ChatMessage/ChatMessage.tsx index f56596634f..67b6734e29 100644 --- a/src/shared/components/Chat/ChatMessage/ChatMessage.tsx +++ b/src/shared/components/Chat/ChatMessage/ChatMessage.tsx @@ -94,7 +94,7 @@ const getStaticLinkByChatType = (chatType: ChatType): StaticLinkType => { const FILE_NAME_LIMIT = 20; -export default function ChatMessage({ +const ChatMessage = ({ discussionMessage, chatType, highlighted = false, @@ -112,7 +112,7 @@ export default function ChatMessage({ onFeedItemClick, onInternalLinkClick, isMessageEditAllowed, -}: ChatMessageProps) { +}: ChatMessageProps) => { const dispatch = useDispatch(); const { notify } = useNotification(); const updateMessageRef = useRef<{ @@ -268,7 +268,7 @@ export default function ChatMessage({ setIsMessageEditLoading(true); try { - const updatedMessage = await ChatService.updateChatMessage({ + await ChatService.updateChatMessage({ chatMessageId: discussionMessage.id, text: JSON.stringify(message), hasUncheckedItems: checkUncheckedItemsInTextEditorValue(message), @@ -582,4 +582,8 @@ export default function ChatMessage({ ); -} +}; + +const MemoizedChatMessage = React.memo(ChatMessage) + +export default MemoizedChatMessage; diff --git a/src/shared/hooks/useCases/useDiscussionMessagesById.ts b/src/shared/hooks/useCases/useDiscussionMessagesById.ts index 4c37c792a2..1b7370a5b5 100644 --- a/src/shared/hooks/useCases/useDiscussionMessagesById.ts +++ b/src/shared/hooks/useCases/useDiscussionMessagesById.ts @@ -1,6 +1,6 @@ -import { useEffect, useState, useCallback } from "react"; +import { useState, useCallback } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { useUpdateEffect } from "react-use"; +import { useDeepCompareEffect, useUpdateEffect } from "react-use"; import { DiscussionMessageService, MESSAGES_NUMBER_IN_BATCH, @@ -309,7 +309,7 @@ export const useDiscussionMessagesById = ({ ); }; - useEffect(() => { + useDeepCompareEffect(() => { (async () => { if (!state.data || state.data.length === 0) { setDiscussionMessagesWithOwners([]); From 358c128a3e37473cf538454053fb81eea7e3f3ce Mon Sep 17 00:00:00 2001 From: elatif2020 <72435714+elatif2020@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:03:59 +0000 Subject: [PATCH 2/2] reverted the removal of the overflow-y line --- src/shared/components/Modal/index.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shared/components/Modal/index.scss b/src/shared/components/Modal/index.scss index 81538076b2..48eae28020 100644 --- a/src/shared/components/Modal/index.scss +++ b/src/shared/components/Modal/index.scss @@ -145,6 +145,7 @@ $modal-mobile-padding: 1.5rem; .modal__content { flex: 1; padding: 0 $modal-padding; + overflow-y: auto; @include big-phone { padding-left: $modal-mobile-padding;