From 1a6f0bb28f66d94e8c8a8e35bbc9eb297c04c943 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Sat, 27 Nov 2021 17:40:52 +0100 Subject: [PATCH] [Perf] Chat List: Some optimizations --- src/components/left/main/ChatList.tsx | 28 +++-- .../left/main/hooks/useChatAnimationType.ts | 4 +- src/components/main/DownloadManager.tsx | 4 +- src/modules/helpers/chats.ts | 102 ++++++++++-------- 4 files changed, 78 insertions(+), 60 deletions(-) diff --git a/src/components/left/main/ChatList.tsx b/src/components/left/main/ChatList.tsx index f91a0c91e..8f3febb7e 100644 --- a/src/components/left/main/ChatList.tsx +++ b/src/components/left/main/ChatList.tsx @@ -14,7 +14,9 @@ import { ALL_CHATS_PRELOAD_DISABLED, CHAT_HEIGHT_PX, CHAT_LIST_SLICE } from '../ import { IS_ANDROID, IS_MAC_OS, IS_PWA } from '../../../util/environment'; import usePrevious from '../../../hooks/usePrevious'; import { mapValues, pick } from '../../../util/iteratees'; -import { getChatOrder, prepareChatList, prepareFolderListIds } from '../../../modules/helpers'; +import { + getChatOrder, prepareChatList, prepareFolderListIds, reduceChatList, +} from '../../../modules/helpers'; import { selectChatFolder, selectNotifyExceptions, selectNotifySettings, } from '../../../modules/selectors'; @@ -80,19 +82,20 @@ const ChatList: FC = ({ : [listIds, orderedPinnedIds]; }, [folderType, chatFolder, chatsById, usersById, notifySettings, notifyExceptions, listIds, orderedPinnedIds]); - const [orderById, orderedIds] = useMemo(() => { + const [orderById, orderedIds, chatArrays] = useMemo(() => { if (!currentListIds || (folderType === 'folder' && !chatFolder)) { return []; } + const newChatArrays = prepareChatList(chatsById, currentListIds, currentPinnedIds, folderType); - const singleList = [...newChatArrays.pinnedChats, ...newChatArrays.otherChats]; + const singleList = ([] as ApiChat[]).concat(newChatArrays.pinnedChats, newChatArrays.otherChats); const newOrderedIds = singleList.map(({ id }) => id); const newOrderById = singleList.reduce((acc, chat, i) => { acc[chat.id] = i; return acc; }, {} as Record); - return [newOrderById, newOrderedIds]; + return [newOrderById, newOrderedIds, newChatArrays]; }, [currentListIds, currentPinnedIds, folderType, chatFolder, chatsById]); const prevOrderById = usePrevious(orderById); @@ -119,8 +122,13 @@ const ChatList: FC = ({ folderType === 'all' && !ALL_CHATS_PRELOAD_DISABLED, ); - // TODO Refactor to not call `prepareChatList` twice - const chatArrays = viewportIds && prepareChatList(chatsById, viewportIds, currentPinnedIds, folderType); + const viewportChatArrays = useMemo(() => { + if (!viewportIds || !chatArrays) { + return undefined; + } + + return reduceChatList(chatArrays, viewportIds); + }, [chatArrays, viewportIds]); useEffect(() => { if (lastSyncTime && folderType === 'all') { @@ -133,7 +141,7 @@ const ChatList: FC = ({ function renderChats() { const viewportOffset = orderedIds!.indexOf(viewportIds![0]); - const pinnedOffset = viewportOffset + chatArrays!.pinnedChats.length; + const pinnedOffset = viewportOffset + viewportChatArrays!.pinnedChats.length; return (
= ({ style={IS_ANDROID ? `height: ${orderedIds!.length * CHAT_HEIGHT_PX}px` : undefined} teactFastList > - {chatArrays!.pinnedChats.map(({ id }, i) => ( + {viewportChatArrays!.pinnedChats.map(({ id }, i) => ( = ({ style={`top: ${(viewportOffset + i) * CHAT_HEIGHT_PX}px;`} /> ))} - {chatArrays!.otherChats.map((chat, i) => ( + {viewportChatArrays!.otherChats.map((chat, i) => ( = ({ noFastList noScrollRestore > - {viewportIds?.length && chatArrays ? ( + {viewportIds?.length && viewportChatArrays ? ( renderChats() ) : viewportIds && !viewportIds.length ? ( ( diff --git a/src/components/left/main/hooks/useChatAnimationType.ts b/src/components/left/main/hooks/useChatAnimationType.ts index b7a131a8e..4f89be4a3 100644 --- a/src/components/left/main/hooks/useChatAnimationType.ts +++ b/src/components/left/main/hooks/useChatAnimationType.ts @@ -24,8 +24,8 @@ export function useChatAnimationType(orderDiffById: Record) { if ( orderDiff === Infinity || orderDiff === -Infinity - || (movesUp(chatId) && numberOfUp <= numberOfDown) - || (movesDown(chatId) && numberOfDown < numberOfUp) + || (numberOfUp <= numberOfDown && movesUp(chatId)) + || (numberOfDown < numberOfUp && movesDown(chatId)) ) { return ChatAnimationTypes.Opacity; } diff --git a/src/components/main/DownloadManager.tsx b/src/components/main/DownloadManager.tsx index 98b4bacc0..84ca6614b 100644 --- a/src/components/main/DownloadManager.tsx +++ b/src/components/main/DownloadManager.tsx @@ -23,7 +23,7 @@ type DispatchProps = Pick; const startedDownloads = new Set(); -const DownloadsManager: FC = ({ +const DownloadManager: FC = ({ activeDownloads, messages, cancelMessageMediaDownload, @@ -78,4 +78,4 @@ export default memo(withGlobal( }; }, (setGlobal, actions): DispatchProps => pick(actions, ['cancelMessageMediaDownload']), -)(DownloadsManager)); +)(DownloadManager)); diff --git a/src/modules/helpers/chats.ts b/src/modules/helpers/chats.ts index 9e4379aa4..de126723d 100644 --- a/src/modules/helpers/chats.ts +++ b/src/modules/helpers/chats.ts @@ -223,11 +223,7 @@ export function getChatSlowModeOptions(chat?: ApiChat) { } export function getChatOrder(chat: ApiChat) { - return Math.max( - chat.joinDate || 0, - chat.draftDate || 0, - chat.lastMessage ? chat.lastMessage.date : 0, - ); + return Math.max(chat.joinDate || 0, chat.draftDate || 0, chat.lastMessage?.date || 0); } export function isChatArchived(chat: ApiChat) { @@ -370,52 +366,66 @@ export function prepareChatList( orderedPinnedIds?: string[], folderType: 'all' | 'archived' | 'folder' = 'all', ) { - function chatFilter(chat?: ApiChat) { - if (!chat || !chat.lastMessage || chat.migratedTo) { - return false; - } - - switch (folderType) { - case 'all': - if (isChatArchived(chat)) { - return false; - } - break; - case 'archived': - if (!isChatArchived(chat)) { - return false; - } - break; - } - - return !chat.isRestricted && !chat.isNotJoined; - } - - const listedChats = listIds - .map((id) => chatsById[id]) - .filter(chatFilter); - const listIdsSet = new Set(listIds); - const pinnedChats = orderedPinnedIds - ? ( - orderedPinnedIds - .map((id) => chatsById[id]) - .filter(chatFilter) - .filter((chat) => listIdsSet.has(chat.id)) - ) - : []; + const orderedPinnedIdsSet = orderedPinnedIds ? new Set(orderedPinnedIds) : undefined; - const otherChats = orderBy( - orderedPinnedIds - ? listedChats.filter((chat) => !orderedPinnedIds.includes(chat.id)) - : listedChats, - getChatOrder, - 'desc', - ); + const pinnedChats = orderedPinnedIds?.reduce((acc, id) => { + const chat = chatsById[id]; + + if (chat && listIdsSet.has(chat.id) && chatFilter(chat, folderType)) { + acc.push(chat); + } + + return acc; + }, [] as ApiChat[]) || []; + + const otherChats = listIds.reduce((acc, id) => { + const chat = chatsById[id]; + + if (chat && (!orderedPinnedIdsSet || !orderedPinnedIdsSet.has(chat.id)) && chatFilter(chat, folderType)) { + acc.push(chat); + } + + return acc; + }, [] as ApiChat[]); + const otherChatsOrdered = orderBy(otherChats, getChatOrder, 'desc'); return { pinnedChats, - otherChats, + otherChats: otherChatsOrdered, + }; +} + +function chatFilter(chat: ApiChat, folderType: 'all' | 'archived' | 'folder') { + if (!chat.lastMessage || chat.migratedTo) { + return false; + } + + switch (folderType) { + case 'all': + if (isChatArchived(chat)) { + return false; + } + break; + case 'archived': + if (!isChatArchived(chat)) { + return false; + } + break; + } + + return !chat.isRestricted && !chat.isNotJoined; +} + +export function reduceChatList( + chatArrays: { pinnedChats: ApiChat[]; otherChats: ApiChat[] }, + filteredIds: string[], +) { + const filteredIdsSet = new Set(filteredIds); + + return { + pinnedChats: chatArrays.pinnedChats.filter(({ id }) => filteredIdsSet.has(id)), + otherChats: chatArrays.otherChats.filter(({ id }) => filteredIdsSet.has(id)), }; }