[Perf] Chat List: Some optimizations
This commit is contained in:
parent
f09de4ca28
commit
1a6f0bb28f
@ -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 ? (
|
||||||
(
|
(
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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));
|
||||||
|
|||||||
@ -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)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user