[Perf] Various optimizations for calculating chat lists
This commit is contained in:
parent
865ed08d82
commit
8015a7360e
@ -4,7 +4,7 @@ import React, {
|
|||||||
import { withGlobal } from '../../../lib/teact/teactn';
|
import { withGlobal } from '../../../lib/teact/teactn';
|
||||||
|
|
||||||
import { ApiChat, ApiChatFolder, ApiUser } from '../../../api/types';
|
import { ApiChat, ApiChatFolder, ApiUser } from '../../../api/types';
|
||||||
import { GlobalActions } from '../../../global/types';
|
import { GlobalActions, GlobalState } from '../../../global/types';
|
||||||
import { NotifyException, NotifySettings, SettingsScreens } from '../../../types';
|
import { NotifyException, NotifySettings, SettingsScreens } from '../../../types';
|
||||||
import { FolderEditDispatch } from '../../../hooks/reducers/useFoldersReducer';
|
import { FolderEditDispatch } from '../../../hooks/reducers/useFoldersReducer';
|
||||||
|
|
||||||
@ -30,6 +30,7 @@ type OwnProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
|
allListIds: GlobalState['chats']['listIds'];
|
||||||
chatsById: Record<string, ApiChat>;
|
chatsById: Record<string, ApiChat>;
|
||||||
usersById: Record<string, ApiUser>;
|
usersById: Record<string, ApiUser>;
|
||||||
chatFoldersById: Record<number, ApiChatFolder>;
|
chatFoldersById: Record<number, ApiChatFolder>;
|
||||||
@ -48,6 +49,7 @@ const INFO_THROTTLE = 3000;
|
|||||||
const SAVED_MESSAGES_HOTKEY = '0';
|
const SAVED_MESSAGES_HOTKEY = '0';
|
||||||
|
|
||||||
const ChatFolders: FC<OwnProps & StateProps & DispatchProps> = ({
|
const ChatFolders: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
allListIds,
|
||||||
chatsById,
|
chatsById,
|
||||||
usersById,
|
usersById,
|
||||||
chatFoldersById,
|
chatFoldersById,
|
||||||
@ -86,11 +88,10 @@ const ChatFolders: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const chatIds = Object.keys(chatsById);
|
|
||||||
const counters = displayedFolders.map((folder) => {
|
const counters = displayedFolders.map((folder) => {
|
||||||
const {
|
const {
|
||||||
unreadDialogsCount, hasActiveDialogs,
|
unreadDialogsCount, hasActiveDialogs,
|
||||||
} = getFolderUnreadDialogs(chatsById, usersById, folder, chatIds, notifySettings, notifyExceptions) || {};
|
} = getFolderUnreadDialogs(allListIds, chatsById, usersById, folder, notifySettings, notifyExceptions) || {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: folder.id,
|
id: folder.id,
|
||||||
@ -100,7 +101,7 @@ const ChatFolders: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return buildCollectionByKey(counters, 'id');
|
return buildCollectionByKey(counters, 'id');
|
||||||
}, INFO_THROTTLE, [displayedFolders, chatsById, usersById, notifySettings, notifyExceptions]);
|
}, INFO_THROTTLE, [displayedFolders, allListIds, chatsById, usersById, notifySettings, notifyExceptions]);
|
||||||
|
|
||||||
const folderTabs = useMemo(() => {
|
const folderTabs = useMemo(() => {
|
||||||
if (!displayedFolders || !displayedFolders.length) {
|
if (!displayedFolders || !displayedFolders.length) {
|
||||||
@ -240,7 +241,7 @@ const ChatFolders: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global): StateProps => {
|
(global): StateProps => {
|
||||||
const {
|
const {
|
||||||
chats: { byId: chatsById },
|
chats: { listIds: allListIds, byId: chatsById },
|
||||||
users: { byId: usersById },
|
users: { byId: usersById },
|
||||||
chatFolders: {
|
chatFolders: {
|
||||||
byId: chatFoldersById,
|
byId: chatFoldersById,
|
||||||
@ -253,6 +254,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
} = global;
|
} = global;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
allListIds,
|
||||||
chatsById,
|
chatsById,
|
||||||
usersById,
|
usersById,
|
||||||
chatFoldersById,
|
chatFoldersById,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import React, {
|
|||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { withGlobal } from '../../../lib/teact/teactn';
|
import { withGlobal } from '../../../lib/teact/teactn';
|
||||||
|
|
||||||
import { GlobalActions } from '../../../global/types';
|
import { GlobalActions, GlobalState } from '../../../global/types';
|
||||||
import {
|
import {
|
||||||
ApiChat, ApiChatFolder, ApiUser,
|
ApiChat, ApiChatFolder, ApiUser,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
@ -37,11 +37,12 @@ type OwnProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
|
allListIds: GlobalState['chats']['listIds'];
|
||||||
chatsById: Record<string, ApiChat>;
|
chatsById: Record<string, ApiChat>;
|
||||||
usersById: Record<string, ApiUser>;
|
usersById: Record<string, ApiUser>;
|
||||||
chatFolder?: ApiChatFolder;
|
|
||||||
listIds?: string[];
|
listIds?: string[];
|
||||||
orderedPinnedIds?: string[];
|
orderedPinnedIds?: string[];
|
||||||
|
chatFolder?: ApiChatFolder;
|
||||||
lastSyncTime?: number;
|
lastSyncTime?: number;
|
||||||
notifySettings: NotifySettings;
|
notifySettings: NotifySettings;
|
||||||
notifyExceptions?: Record<number, NotifyException>;
|
notifyExceptions?: Record<number, NotifyException>;
|
||||||
@ -60,15 +61,16 @@ const ChatList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
folderType,
|
folderType,
|
||||||
folderId,
|
folderId,
|
||||||
isActive,
|
isActive,
|
||||||
chatFolder,
|
allListIds,
|
||||||
chatsById,
|
chatsById,
|
||||||
usersById,
|
usersById,
|
||||||
listIds,
|
listIds,
|
||||||
orderedPinnedIds,
|
orderedPinnedIds,
|
||||||
|
chatFolder,
|
||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
foldersDispatch,
|
|
||||||
notifySettings,
|
notifySettings,
|
||||||
notifyExceptions,
|
notifyExceptions,
|
||||||
|
foldersDispatch,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
loadMoreChats,
|
loadMoreChats,
|
||||||
preloadTopChatMessages,
|
preloadTopChatMessages,
|
||||||
@ -78,9 +80,12 @@ const ChatList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [currentListIds, currentPinnedIds] = useMemo(() => {
|
const [currentListIds, currentPinnedIds] = useMemo(() => {
|
||||||
return folderType === 'folder' && chatFolder
|
return folderType === 'folder' && chatFolder
|
||||||
? prepareFolderListIds(chatsById, usersById, chatFolder, notifySettings, notifyExceptions)
|
? prepareFolderListIds(allListIds, chatsById, usersById, chatFolder, notifySettings, notifyExceptions)
|
||||||
: [listIds, orderedPinnedIds];
|
: [listIds, orderedPinnedIds];
|
||||||
}, [folderType, chatFolder, chatsById, usersById, notifySettings, notifyExceptions, listIds, orderedPinnedIds]);
|
}, [
|
||||||
|
folderType, chatFolder, allListIds, chatsById, usersById,
|
||||||
|
notifySettings, notifyExceptions, listIds, orderedPinnedIds,
|
||||||
|
]);
|
||||||
|
|
||||||
const [orderById, orderedIds, chatArrays] = useMemo(() => {
|
const [orderById, orderedIds, chatArrays] = useMemo(() => {
|
||||||
if (!currentListIds || (folderType === 'folder' && !chatFolder)) {
|
if (!currentListIds || (folderType === 'folder' && !chatFolder)) {
|
||||||
@ -106,7 +111,7 @@ const ChatList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return mapValues(orderById, (order, id) => {
|
return mapValues(orderById, (order, id) => {
|
||||||
return order - (prevOrderById[id] !== undefined ? prevOrderById[id] : Infinity);
|
return prevOrderById[id] !== undefined ? order - prevOrderById[id] : -Infinity;
|
||||||
});
|
});
|
||||||
}, [orderById, prevOrderById]);
|
}, [orderById, prevOrderById]);
|
||||||
|
|
||||||
@ -137,6 +142,39 @@ const ChatList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
}, [lastSyncTime, folderType, preloadTopChatMessages, preloadArchivedChats]);
|
}, [lastSyncTime, folderType, preloadTopChatMessages, preloadArchivedChats]);
|
||||||
|
|
||||||
|
// Support <Cmd>+<Digit> and <Alt>+<Up/Down> to navigate between chats
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isActive || !orderedIds) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeyDown(e: KeyboardEvent) {
|
||||||
|
if (IS_PWA && ((IS_MAC_OS && e.metaKey) || (!IS_MAC_OS && e.ctrlKey)) && e.code.startsWith('Digit')) {
|
||||||
|
const [, digit] = e.code.match(/Digit(\d)/) || [];
|
||||||
|
if (!digit) return;
|
||||||
|
|
||||||
|
const position = Number(digit) - 1;
|
||||||
|
if (position > orderedIds!.length - 1) return;
|
||||||
|
|
||||||
|
openChat({ id: orderedIds![position], shouldReplaceHistory: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.altKey) {
|
||||||
|
const targetIndexDelta = e.key === 'ArrowDown' ? 1 : e.key === 'ArrowUp' ? -1 : undefined;
|
||||||
|
if (!targetIndexDelta) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
openNextChat({ targetIndexDelta, orderedIds });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
};
|
||||||
|
}, [isActive, openChat, openNextChat, orderedIds]);
|
||||||
|
|
||||||
const getAnimationType = useChatAnimationType(orderDiffById);
|
const getAnimationType = useChatAnimationType(orderDiffById);
|
||||||
|
|
||||||
function renderChats() {
|
function renderChats() {
|
||||||
@ -179,36 +217,6 @@ const ChatList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
|
||||||
if (isActive && orderedIds) {
|
|
||||||
if (IS_PWA && ((IS_MAC_OS && e.metaKey) || (!IS_MAC_OS && e.ctrlKey)) && e.code.startsWith('Digit')) {
|
|
||||||
const [, digit] = e.code.match(/Digit(\d)/) || [];
|
|
||||||
if (!digit) return;
|
|
||||||
|
|
||||||
const position = Number(digit) - 1;
|
|
||||||
if (position > orderedIds.length - 1) return;
|
|
||||||
|
|
||||||
openChat({ id: orderedIds[position], shouldReplaceHistory: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.altKey) {
|
|
||||||
const targetIndexDelta = e.key === 'ArrowDown' ? 1 : e.key === 'ArrowUp' ? -1 : undefined;
|
|
||||||
if (!targetIndexDelta) return;
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
openNextChat({ targetIndexDelta, orderedIds });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('keydown', handleKeyDown, false);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('keydown', handleKeyDown, false);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
className="chat-list custom-scroll"
|
className="chat-list custom-scroll"
|
||||||
@ -251,6 +259,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const chatFolder = folderId ? selectChatFolder(global, folderId) : undefined;
|
const chatFolder = folderId ? selectChatFolder(global, folderId) : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
allListIds: listIds,
|
||||||
chatsById,
|
chatsById,
|
||||||
usersById,
|
usersById,
|
||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from '../../../../lib/teact/teact';
|
import { useMemo } from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
export enum ChatAnimationTypes {
|
export enum ChatAnimationTypes {
|
||||||
Move,
|
Move,
|
||||||
@ -7,29 +7,27 @@ export enum ChatAnimationTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useChatAnimationType(orderDiffById: Record<string, number>) {
|
export function useChatAnimationType(orderDiffById: Record<string, number>) {
|
||||||
const movesUp = useCallback((id: string) => orderDiffById[id] < 0, [orderDiffById]);
|
return useMemo(() => {
|
||||||
const movesDown = useCallback((id: string) => orderDiffById[id] > 0, [orderDiffById]);
|
const orderDiffs = Object.values(orderDiffById);
|
||||||
|
const numberOfUp = orderDiffs.filter((diff) => diff < 0).length;
|
||||||
|
const numberOfDown = orderDiffs.filter((diff) => diff > 0).length;
|
||||||
|
|
||||||
const orderDiffIds = Object.keys(orderDiffById);
|
return (chatId: string): ChatAnimationTypes => {
|
||||||
const numberOfUp = orderDiffIds.filter(movesUp).length;
|
const orderDiff = orderDiffById[chatId];
|
||||||
const numberOfDown = orderDiffIds.filter(movesDown).length;
|
if (orderDiff === 0) {
|
||||||
|
return ChatAnimationTypes.None;
|
||||||
|
}
|
||||||
|
|
||||||
return useCallback((chatId: string): ChatAnimationTypes => {
|
if (
|
||||||
const orderDiff = orderDiffById[chatId];
|
orderDiff === Infinity
|
||||||
|
|| orderDiff === -Infinity
|
||||||
|
|| (numberOfUp <= numberOfDown && orderDiff < 0)
|
||||||
|
|| (numberOfDown < numberOfUp && orderDiff > 0)
|
||||||
|
) {
|
||||||
|
return ChatAnimationTypes.Opacity;
|
||||||
|
}
|
||||||
|
|
||||||
if (orderDiff === 0) {
|
return ChatAnimationTypes.Move;
|
||||||
return ChatAnimationTypes.None;
|
};
|
||||||
}
|
}, [orderDiffById]);
|
||||||
|
|
||||||
if (
|
|
||||||
orderDiff === Infinity
|
|
||||||
|| orderDiff === -Infinity
|
|
||||||
|| (numberOfUp <= numberOfDown && movesUp(chatId))
|
|
||||||
|| (numberOfDown < numberOfUp && movesDown(chatId))
|
|
||||||
) {
|
|
||||||
return ChatAnimationTypes.Opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChatAnimationTypes.Move;
|
|
||||||
}, [movesDown, movesUp, numberOfDown, numberOfUp, orderDiffById]);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import React, {
|
|||||||
} from '../../../../lib/teact/teact';
|
} from '../../../../lib/teact/teact';
|
||||||
import { withGlobal } from '../../../../lib/teact/teactn';
|
import { withGlobal } from '../../../../lib/teact/teactn';
|
||||||
|
|
||||||
import { GlobalActions } from '../../../../global/types';
|
import { GlobalActions, GlobalState } from '../../../../global/types';
|
||||||
import { ApiChatFolder, ApiChat, ApiUser } from '../../../../api/types';
|
import { ApiChatFolder, ApiChat, ApiUser } from '../../../../api/types';
|
||||||
import { NotifyException, NotifySettings, SettingsScreens } from '../../../../types';
|
import { NotifyException, NotifySettings, SettingsScreens } from '../../../../types';
|
||||||
|
|
||||||
@ -22,14 +22,15 @@ import Loading from '../../../ui/Loading';
|
|||||||
import AnimatedSticker from '../../../common/AnimatedSticker';
|
import AnimatedSticker from '../../../common/AnimatedSticker';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
|
isActive?: boolean;
|
||||||
onCreateFolder: () => void;
|
onCreateFolder: () => void;
|
||||||
onEditFolder: (folder: ApiChatFolder) => void;
|
onEditFolder: (folder: ApiChatFolder) => void;
|
||||||
isActive?: boolean;
|
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
|
allListIds: GlobalState['chats']['listIds'];
|
||||||
chatsById: Record<string, ApiChat>;
|
chatsById: Record<string, ApiChat>;
|
||||||
usersById: Record<string, ApiUser>;
|
usersById: Record<string, ApiUser>;
|
||||||
orderedFolderIds?: number[];
|
orderedFolderIds?: number[];
|
||||||
@ -46,11 +47,8 @@ const runThrottledForLoadRecommended = throttle((cb) => cb(), 60000, true);
|
|||||||
const MAX_ALLOWED_FOLDERS = 10;
|
const MAX_ALLOWED_FOLDERS = 10;
|
||||||
|
|
||||||
const SettingsFoldersMain: FC<OwnProps & StateProps & DispatchProps> = ({
|
const SettingsFoldersMain: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
onCreateFolder,
|
|
||||||
onEditFolder,
|
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
allListIds,
|
||||||
onReset,
|
|
||||||
chatsById,
|
chatsById,
|
||||||
usersById,
|
usersById,
|
||||||
orderedFolderIds,
|
orderedFolderIds,
|
||||||
@ -58,6 +56,10 @@ const SettingsFoldersMain: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
recommendedChatFolders,
|
recommendedChatFolders,
|
||||||
notifySettings,
|
notifySettings,
|
||||||
notifyExceptions,
|
notifyExceptions,
|
||||||
|
onCreateFolder,
|
||||||
|
onEditFolder,
|
||||||
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
loadRecommendedChatFolders,
|
loadRecommendedChatFolders,
|
||||||
addChatFolder,
|
addChatFolder,
|
||||||
showDialog,
|
showDialog,
|
||||||
@ -104,8 +106,6 @@ const SettingsFoldersMain: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const chatIds = Object.keys(chatsById);
|
|
||||||
|
|
||||||
return orderedFolderIds.map((id) => {
|
return orderedFolderIds.map((id) => {
|
||||||
const folder = foldersById[id];
|
const folder = foldersById[id];
|
||||||
|
|
||||||
@ -113,11 +113,11 @@ const SettingsFoldersMain: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
id: folder.id,
|
id: folder.id,
|
||||||
title: folder.title,
|
title: folder.title,
|
||||||
subtitle: getFolderDescriptionText(
|
subtitle: getFolderDescriptionText(
|
||||||
lang, chatsById, usersById, folder, chatIds, notifySettings, notifyExceptions,
|
lang, allListIds, chatsById, usersById, folder, notifySettings, notifyExceptions,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, [orderedFolderIds, chatsById, foldersById, usersById, notifySettings, notifyExceptions, lang]);
|
}, [lang, allListIds, foldersById, chatsById, usersById, orderedFolderIds, notifySettings, notifyExceptions]);
|
||||||
|
|
||||||
const handleCreateFolderFromRecommended = useCallback((folder: ApiChatFolder) => {
|
const handleCreateFolderFromRecommended = useCallback((folder: ApiChatFolder) => {
|
||||||
if (Object.keys(foldersById).length >= MAX_ALLOWED_FOLDERS) {
|
if (Object.keys(foldersById).length >= MAX_ALLOWED_FOLDERS) {
|
||||||
@ -229,7 +229,7 @@ const SettingsFoldersMain: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global): StateProps => {
|
(global): StateProps => {
|
||||||
const {
|
const {
|
||||||
chats: { byId: chatsById },
|
chats: { listIds: allListIds, byId: chatsById },
|
||||||
users: { byId: usersById },
|
users: { byId: usersById },
|
||||||
} = global;
|
} = global;
|
||||||
|
|
||||||
@ -240,6 +240,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
} = global.chatFolders;
|
} = global.chatFolders;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
allListIds,
|
||||||
chatsById,
|
chatsById,
|
||||||
usersById,
|
usersById,
|
||||||
orderedFolderIds,
|
orderedFolderIds,
|
||||||
|
|||||||
@ -76,7 +76,7 @@ addReducer('preloadTopChatMessages', (global, actions) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { chatId: currentChatId } = selectCurrentMessageList(global) || {};
|
const { chatId: currentChatId } = selectCurrentMessageList(global) || {};
|
||||||
const { pinnedChats, otherChats } = prepareChatList(byId, listIds, orderedPinnedIds);
|
const { pinnedChats, otherChats } = prepareChatList(byId, listIds, orderedPinnedIds, 'all', true);
|
||||||
const topChats = [...pinnedChats, ...otherChats];
|
const topChats = [...pinnedChats, ...otherChats];
|
||||||
const chatToPreload = topChats.find(({ id }) => id !== currentChatId && !preloadedChatIds.includes(id));
|
const chatToPreload = topChats.find(({ id }) => id !== currentChatId && !preloadedChatIds.includes(id));
|
||||||
if (!chatToPreload) {
|
if (!chatToPreload) {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
MAIN_THREAD_ID,
|
MAIN_THREAD_ID,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
|
|
||||||
|
import { GlobalState } from '../../global/types';
|
||||||
import { NotifyException, NotifySettings } from '../../types';
|
import { NotifyException, NotifySettings } from '../../types';
|
||||||
import { LangFn } from '../../hooks/useLang';
|
import { LangFn } from '../../hooks/useLang';
|
||||||
|
|
||||||
@ -269,17 +270,17 @@ export function getCanDeleteChat(chat: ApiChat) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function prepareFolderListIds(
|
export function prepareFolderListIds(
|
||||||
|
allListIds: GlobalState['chats']['listIds'],
|
||||||
chatsById: Record<string, ApiChat>,
|
chatsById: Record<string, ApiChat>,
|
||||||
usersById: Record<string, ApiUser>,
|
usersById: Record<string, ApiUser>,
|
||||||
folder: ApiChatFolder,
|
folder: ApiChatFolder,
|
||||||
notifySettings: NotifySettings,
|
notifySettings: NotifySettings,
|
||||||
notifyExceptions?: Record<number, NotifyException>,
|
notifyExceptions?: Record<number, NotifyException>,
|
||||||
chatIdsCache?: string[],
|
|
||||||
) {
|
) {
|
||||||
const excludedChatIds = folder.excludedChatIds ? new Set(folder.excludedChatIds) : undefined;
|
const excludedChatIds = folder.excludedChatIds ? new Set(folder.excludedChatIds) : undefined;
|
||||||
const includedChatIds = folder.excludedChatIds ? new Set(folder.includedChatIds) : undefined;
|
const includedChatIds = folder.excludedChatIds ? new Set(folder.includedChatIds) : undefined;
|
||||||
const pinnedChatIds = folder.excludedChatIds ? new Set(folder.pinnedChatIds) : undefined;
|
const pinnedChatIds = folder.excludedChatIds ? new Set(folder.pinnedChatIds) : undefined;
|
||||||
const listIds = (chatIdsCache || Object.keys(chatsById))
|
const listIds = ([] as string[]).concat(allListIds.active || [], allListIds.archived || [])
|
||||||
.filter((id) => {
|
.filter((id) => {
|
||||||
return filterChatFolder(
|
return filterChatFolder(
|
||||||
chatsById[id],
|
chatsById[id],
|
||||||
@ -296,6 +297,7 @@ export function prepareFolderListIds(
|
|||||||
return [listIds, folder.pinnedChatIds] as const;
|
return [listIds, folder.pinnedChatIds] as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function is the most expensive in the project, so any possible optimizations are welcome
|
||||||
function filterChatFolder(
|
function filterChatFolder(
|
||||||
chat: ApiChat,
|
chat: ApiChat,
|
||||||
folder: ApiChatFolder,
|
folder: ApiChatFolder,
|
||||||
@ -310,51 +312,55 @@ function filterChatFolder(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (excludedChatIds && excludedChatIds.has(chat.id)) {
|
const { id: chatId, type, unreadMentionsCount } = chat;
|
||||||
|
|
||||||
|
if (excludedChatIds?.has(chatId)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includedChatIds && includedChatIds.has(chat.id)) {
|
if (includedChatIds?.has(chatId)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pinnedChatIds && pinnedChatIds.has(chat.id)) {
|
if (pinnedChatIds?.has(chatId)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isChatArchived(chat) && folder.excludeArchived) {
|
if (folder.excludeArchived && chat.folderId === ARCHIVED_FOLDER_ID) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (folder.excludeMuted && !chat.unreadMentionsCount && selectIsChatMuted(chat, notifySettings, notifyExceptions)) {
|
if (folder.excludeRead && !chat.unreadCount && !unreadMentionsCount && !chat.hasUnreadMark) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!chat.unreadCount && !chat.unreadMentionsCount && !chat.hasUnreadMark && folder.excludeRead) {
|
if (folder.excludeMuted && !unreadMentionsCount && selectIsChatMuted(chat, notifySettings, notifyExceptions)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUserId(chat.id)) {
|
if (type === 'chatTypePrivate') {
|
||||||
const privateChatUser = usersById[chat.id];
|
const user = usersById[chatId];
|
||||||
|
if (user) {
|
||||||
|
const { type: userType, isContact } = user;
|
||||||
|
|
||||||
const isChatWithBot = privateChatUser && privateChatUser.type === 'userTypeBot';
|
if (userType === 'userTypeBot') {
|
||||||
if (isChatWithBot) {
|
if (folder.bots) {
|
||||||
if (folder.bots) {
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
if (folder.contacts && isContact) {
|
||||||
if (folder.contacts && privateChatUser && privateChatUser.isContact) {
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (folder.nonContacts && privateChatUser && !privateChatUser.isContact) {
|
if (folder.nonContacts && !isContact) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (isChatGroup(chat)) {
|
} else if (type === 'chatTypeChannel') {
|
||||||
return !!folder.groups;
|
|
||||||
} else if (isChatChannel(chat)) {
|
|
||||||
return !!folder.channels;
|
return !!folder.channels;
|
||||||
|
} else if (type === 'chatTypeBasicGroup' || type === 'chatTypeSuperGroup') {
|
||||||
|
return !!folder.groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -365,6 +371,7 @@ export function prepareChatList(
|
|||||||
listIds: string[],
|
listIds: string[],
|
||||||
orderedPinnedIds?: string[],
|
orderedPinnedIds?: string[],
|
||||||
folderType: 'all' | 'archived' | 'folder' = 'all',
|
folderType: 'all' | 'archived' | 'folder' = 'all',
|
||||||
|
noOrder = false,
|
||||||
) {
|
) {
|
||||||
const listIdsSet = new Set(listIds);
|
const listIdsSet = new Set(listIds);
|
||||||
const orderedPinnedIdsSet = orderedPinnedIds ? new Set(orderedPinnedIds) : undefined;
|
const orderedPinnedIdsSet = orderedPinnedIds ? new Set(orderedPinnedIds) : undefined;
|
||||||
@ -372,7 +379,7 @@ export function prepareChatList(
|
|||||||
const pinnedChats = orderedPinnedIds?.reduce((acc, id) => {
|
const pinnedChats = orderedPinnedIds?.reduce((acc, id) => {
|
||||||
const chat = chatsById[id];
|
const chat = chatsById[id];
|
||||||
|
|
||||||
if (chat && listIdsSet.has(chat.id) && chatFilter(chat, folderType)) {
|
if (chat && listIdsSet.has(chat.id) && checkChat(chat, folderType)) {
|
||||||
acc.push(chat);
|
acc.push(chat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,39 +389,25 @@ export function prepareChatList(
|
|||||||
const otherChats = listIds.reduce((acc, id) => {
|
const otherChats = listIds.reduce((acc, id) => {
|
||||||
const chat = chatsById[id];
|
const chat = chatsById[id];
|
||||||
|
|
||||||
if (chat && (!orderedPinnedIdsSet || !orderedPinnedIdsSet.has(chat.id)) && chatFilter(chat, folderType)) {
|
if (chat && (!orderedPinnedIdsSet || !orderedPinnedIdsSet.has(chat.id)) && checkChat(chat, folderType)) {
|
||||||
acc.push(chat);
|
acc.push(chat);
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, [] as ApiChat[]);
|
}, [] as ApiChat[]);
|
||||||
const otherChatsOrdered = orderBy(otherChats, getChatOrder, 'desc');
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pinnedChats,
|
pinnedChats,
|
||||||
otherChats: otherChatsOrdered,
|
otherChats: noOrder ? otherChats : orderBy(otherChats, getChatOrder, 'desc'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function chatFilter(chat: ApiChat, folderType: 'all' | 'archived' | 'folder') {
|
function checkChat(chat: ApiChat, folderType: 'all' | 'archived' | 'folder') {
|
||||||
if (!chat.lastMessage || chat.migratedTo) {
|
return (
|
||||||
return false;
|
chat.lastMessage && !chat.migratedTo && !chat.isRestricted && !chat.isNotJoined
|
||||||
}
|
&& !(folderType === 'all' && chat.folderId === ARCHIVED_FOLDER_ID)
|
||||||
|
&& !(folderType === 'archived' && chat.folderId !== ARCHIVED_FOLDER_ID)
|
||||||
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(
|
export function reduceChatList(
|
||||||
@ -430,26 +423,36 @@ export function reduceChatList(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getFolderUnreadDialogs(
|
export function getFolderUnreadDialogs(
|
||||||
|
allListIds: GlobalState['chats']['listIds'],
|
||||||
chatsById: Record<string, ApiChat>,
|
chatsById: Record<string, ApiChat>,
|
||||||
usersById: Record<string, ApiUser>,
|
usersById: Record<string, ApiUser>,
|
||||||
folder: ApiChatFolder,
|
folder: ApiChatFolder,
|
||||||
chatIdsCache: string[],
|
|
||||||
notifySettings: NotifySettings,
|
notifySettings: NotifySettings,
|
||||||
notifyExceptions?: Record<number, NotifyException>,
|
notifyExceptions?: Record<number, NotifyException>,
|
||||||
) {
|
) {
|
||||||
const [listIds] = prepareFolderListIds(chatsById, usersById, folder, notifySettings, notifyExceptions, chatIdsCache);
|
const [listIds] = prepareFolderListIds(allListIds, chatsById, usersById, folder, notifySettings, notifyExceptions);
|
||||||
|
|
||||||
const listedChats = listIds
|
let hasActiveDialogs = false;
|
||||||
.map((id) => chatsById[id])
|
const unreadDialogsCount = listIds.reduce((acc, id) => {
|
||||||
.filter((chat) => (chat?.lastMessage && !chat.isRestricted && !chat.isNotJoined));
|
const chat = chatsById[id];
|
||||||
|
if (!chat?.lastMessage || chat?.isRestricted || chat?.isNotJoined) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
const unreadDialogsCount = listedChats
|
const isUnread = chat.unreadCount || chat.hasUnreadMark;
|
||||||
.reduce((total, chat) => (chat.unreadCount || chat.hasUnreadMark ? total + 1 : total), 0);
|
|
||||||
|
|
||||||
const hasActiveDialogs = listedChats.some((chat) => (
|
if (isUnread) {
|
||||||
chat.unreadMentionsCount
|
acc++;
|
||||||
|| (!selectIsChatMuted(chat, notifySettings, notifyExceptions) && (chat.unreadCount || chat.hasUnreadMark))
|
}
|
||||||
));
|
|
||||||
|
if (!hasActiveDialogs && (
|
||||||
|
chat.unreadMentionsCount || (isUnread && !selectIsChatMuted(chat, notifySettings, notifyExceptions))
|
||||||
|
)) {
|
||||||
|
hasActiveDialogs = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
unreadDialogsCount,
|
unreadDialogsCount,
|
||||||
@ -459,10 +462,10 @@ export function getFolderUnreadDialogs(
|
|||||||
|
|
||||||
export function getFolderDescriptionText(
|
export function getFolderDescriptionText(
|
||||||
lang: LangFn,
|
lang: LangFn,
|
||||||
|
allListIds: GlobalState['chats']['listIds'],
|
||||||
chatsById: Record<string, ApiChat>,
|
chatsById: Record<string, ApiChat>,
|
||||||
usersById: Record<string, ApiUser>,
|
usersById: Record<string, ApiUser>,
|
||||||
folder: ApiChatFolder,
|
folder: ApiChatFolder,
|
||||||
chatIdsCache: string[],
|
|
||||||
notifySettings: NotifySettings,
|
notifySettings: NotifySettings,
|
||||||
notifyExceptions?: Record<number, NotifyException>,
|
notifyExceptions?: Record<number, NotifyException>,
|
||||||
) {
|
) {
|
||||||
@ -480,7 +483,7 @@ export function getFolderDescriptionText(
|
|||||||
|| (excludedChatIds?.length)
|
|| (excludedChatIds?.length)
|
||||||
|| (includedChatIds?.length)
|
|| (includedChatIds?.length)
|
||||||
) {
|
) {
|
||||||
const length = getFolderChatsCount(chatsById, usersById, folder, chatIdsCache, notifySettings, notifyExceptions);
|
const length = getFolderChatsCount(allListIds, chatsById, usersById, folder, notifySettings, notifyExceptions);
|
||||||
return lang('Chats', length);
|
return lang('Chats', length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,17 +504,17 @@ export function getFolderDescriptionText(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getFolderChatsCount(
|
function getFolderChatsCount(
|
||||||
|
allListIds: GlobalState['chats']['listIds'],
|
||||||
chatsById: Record<string, ApiChat>,
|
chatsById: Record<string, ApiChat>,
|
||||||
usersById: Record<string, ApiUser>,
|
usersById: Record<string, ApiUser>,
|
||||||
folder: ApiChatFolder,
|
folder: ApiChatFolder,
|
||||||
chatIdsCache: string[],
|
|
||||||
notifySettings: NotifySettings,
|
notifySettings: NotifySettings,
|
||||||
notifyExceptions?: Record<string, NotifyException>,
|
notifyExceptions?: Record<string, NotifyException>,
|
||||||
) {
|
) {
|
||||||
const [listIds, pinnedIds] = prepareFolderListIds(
|
const [listIds, pinnedIds] = prepareFolderListIds(
|
||||||
chatsById, usersById, folder, notifySettings, notifyExceptions, chatIdsCache,
|
allListIds, chatsById, usersById, folder, notifySettings, notifyExceptions,
|
||||||
);
|
);
|
||||||
const { pinnedChats, otherChats } = prepareChatList(chatsById, listIds, pinnedIds, 'folder');
|
const { pinnedChats, otherChats } = prepareChatList(chatsById, listIds, pinnedIds, 'folder', true);
|
||||||
return pinnedChats.length + otherChats.length;
|
return pinnedChats.length + otherChats.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user