[Perf] Chat List: Some optimizations

This commit is contained in:
Alexander Zinchuk 2021-11-27 17:40:52 +01:00
parent f09de4ca28
commit 1a6f0bb28f
4 changed files with 78 additions and 60 deletions

View File

@ -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 { IS_ANDROID, IS_MAC_OS, IS_PWA } from '../../../util/environment';
import usePrevious from '../../../hooks/usePrevious'; import usePrevious from '../../../hooks/usePrevious';
import { mapValues, pick } from '../../../util/iteratees'; import { mapValues, pick } from '../../../util/iteratees';
import { getChatOrder, prepareChatList, prepareFolderListIds } from '../../../modules/helpers'; import {
getChatOrder, prepareChatList, prepareFolderListIds, reduceChatList,
} from '../../../modules/helpers';
import { import {
selectChatFolder, selectNotifyExceptions, selectNotifySettings, selectChatFolder, selectNotifyExceptions, selectNotifySettings,
} from '../../../modules/selectors'; } from '../../../modules/selectors';
@ -80,19 +82,20 @@ const ChatList: FC<OwnProps & StateProps & DispatchProps> = ({
: [listIds, orderedPinnedIds]; : [listIds, orderedPinnedIds];
}, [folderType, chatFolder, chatsById, usersById, notifySettings, notifyExceptions, listIds, orderedPinnedIds]); }, [folderType, chatFolder, chatsById, usersById, notifySettings, notifyExceptions, listIds, orderedPinnedIds]);
const [orderById, orderedIds] = useMemo(() => { const [orderById, orderedIds, chatArrays] = useMemo(() => {
if (!currentListIds || (folderType === 'folder' && !chatFolder)) { if (!currentListIds || (folderType === 'folder' && !chatFolder)) {
return []; return [];
} }
const newChatArrays = prepareChatList(chatsById, currentListIds, currentPinnedIds, folderType); 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 newOrderedIds = singleList.map(({ id }) => id);
const newOrderById = singleList.reduce((acc, chat, i) => { const newOrderById = singleList.reduce((acc, chat, i) => {
acc[chat.id] = i; acc[chat.id] = i;
return acc; return acc;
}, {} as Record<string, number>); }, {} as Record<string, number>);
return [newOrderById, newOrderedIds]; return [newOrderById, newOrderedIds, newChatArrays];
}, [currentListIds, currentPinnedIds, folderType, chatFolder, chatsById]); }, [currentListIds, currentPinnedIds, folderType, chatFolder, chatsById]);
const prevOrderById = usePrevious(orderById); const prevOrderById = usePrevious(orderById);
@ -119,8 +122,13 @@ const ChatList: FC<OwnProps & StateProps & DispatchProps> = ({
folderType === 'all' && !ALL_CHATS_PRELOAD_DISABLED, folderType === 'all' && !ALL_CHATS_PRELOAD_DISABLED,
); );
// TODO Refactor to not call `prepareChatList` twice const viewportChatArrays = useMemo(() => {
const chatArrays = viewportIds && prepareChatList(chatsById, viewportIds, currentPinnedIds, folderType); if (!viewportIds || !chatArrays) {
return undefined;
}
return reduceChatList(chatArrays, viewportIds);
}, [chatArrays, viewportIds]);
useEffect(() => { useEffect(() => {
if (lastSyncTime && folderType === 'all') { if (lastSyncTime && folderType === 'all') {
@ -133,7 +141,7 @@ const ChatList: FC<OwnProps & StateProps & DispatchProps> = ({
function renderChats() { function renderChats() {
const viewportOffset = orderedIds!.indexOf(viewportIds![0]); const viewportOffset = orderedIds!.indexOf(viewportIds![0]);
const pinnedOffset = viewportOffset + chatArrays!.pinnedChats.length; const pinnedOffset = viewportOffset + viewportChatArrays!.pinnedChats.length;
return ( return (
<div <div
@ -142,7 +150,7 @@ const ChatList: FC<OwnProps & StateProps & DispatchProps> = ({
style={IS_ANDROID ? `height: ${orderedIds!.length * CHAT_HEIGHT_PX}px` : undefined} style={IS_ANDROID ? `height: ${orderedIds!.length * CHAT_HEIGHT_PX}px` : undefined}
teactFastList teactFastList
> >
{chatArrays!.pinnedChats.map(({ id }, i) => ( {viewportChatArrays!.pinnedChats.map(({ id }, i) => (
<Chat <Chat
key={id} key={id}
teactOrderKey={i} teactOrderKey={i}
@ -155,7 +163,7 @@ const ChatList: FC<OwnProps & StateProps & DispatchProps> = ({
style={`top: ${(viewportOffset + i) * CHAT_HEIGHT_PX}px;`} style={`top: ${(viewportOffset + i) * CHAT_HEIGHT_PX}px;`}
/> />
))} ))}
{chatArrays!.otherChats.map((chat, i) => ( {viewportChatArrays!.otherChats.map((chat, i) => (
<Chat <Chat
key={chat.id} key={chat.id}
teactOrderKey={getChatOrder(chat)} teactOrderKey={getChatOrder(chat)}
@ -210,7 +218,7 @@ const ChatList: FC<OwnProps & StateProps & DispatchProps> = ({
noFastList noFastList
noScrollRestore noScrollRestore
> >
{viewportIds?.length && chatArrays ? ( {viewportIds?.length && viewportChatArrays ? (
renderChats() renderChats()
) : viewportIds && !viewportIds.length ? ( ) : viewportIds && !viewportIds.length ? (
( (

View File

@ -24,8 +24,8 @@ export function useChatAnimationType(orderDiffById: Record<string, number>) {
if ( if (
orderDiff === Infinity orderDiff === Infinity
|| orderDiff === -Infinity || orderDiff === -Infinity
|| (movesUp(chatId) && numberOfUp <= numberOfDown) || (numberOfUp <= numberOfDown && movesUp(chatId))
|| (movesDown(chatId) && numberOfDown < numberOfUp) || (numberOfDown < numberOfUp && movesDown(chatId))
) { ) {
return ChatAnimationTypes.Opacity; return ChatAnimationTypes.Opacity;
} }

View File

@ -23,7 +23,7 @@ type DispatchProps = Pick<GlobalActions, 'cancelMessageMediaDownload'>;
const startedDownloads = new Set<string>(); const startedDownloads = new Set<string>();
const DownloadsManager: FC<StateProps & DispatchProps> = ({ const DownloadManager: FC<StateProps & DispatchProps> = ({
activeDownloads, activeDownloads,
messages, messages,
cancelMessageMediaDownload, cancelMessageMediaDownload,
@ -78,4 +78,4 @@ export default memo(withGlobal(
}; };
}, },
(setGlobal, actions): DispatchProps => pick(actions, ['cancelMessageMediaDownload']), (setGlobal, actions): DispatchProps => pick(actions, ['cancelMessageMediaDownload']),
)(DownloadsManager)); )(DownloadManager));

View File

@ -223,11 +223,7 @@ export function getChatSlowModeOptions(chat?: ApiChat) {
} }
export function getChatOrder(chat: ApiChat) { export function getChatOrder(chat: ApiChat) {
return Math.max( return Math.max(chat.joinDate || 0, chat.draftDate || 0, chat.lastMessage?.date || 0);
chat.joinDate || 0,
chat.draftDate || 0,
chat.lastMessage ? chat.lastMessage.date : 0,
);
} }
export function isChatArchived(chat: ApiChat) { export function isChatArchived(chat: ApiChat) {
@ -370,52 +366,66 @@ export function prepareChatList(
orderedPinnedIds?: string[], orderedPinnedIds?: string[],
folderType: 'all' | 'archived' | 'folder' = 'all', 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 listIdsSet = new Set(listIds);
const pinnedChats = orderedPinnedIds const orderedPinnedIdsSet = orderedPinnedIds ? new Set(orderedPinnedIds) : undefined;
? (
orderedPinnedIds
.map((id) => chatsById[id])
.filter(chatFilter)
.filter((chat) => listIdsSet.has(chat.id))
)
: [];
const otherChats = orderBy( const pinnedChats = orderedPinnedIds?.reduce((acc, id) => {
orderedPinnedIds const chat = chatsById[id];
? listedChats.filter((chat) => !orderedPinnedIds.includes(chat.id))
: listedChats, if (chat && listIdsSet.has(chat.id) && chatFilter(chat, folderType)) {
getChatOrder, acc.push(chat);
'desc', }
);
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 { return {
pinnedChats, 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)),
}; };
} }