Chat Folders: Add "Read all messages" and "Edit" for the "All chats" folder (#5793)

Co-authored-by: zubiden <19638254+zubiden@users.noreply.github.com>
This commit is contained in:
Alexander Zinchuk 2025-04-23 18:59:04 +02:00
parent 7d84defd09
commit 3167a36d84
9 changed files with 164 additions and 48 deletions

View File

@ -11,7 +11,7 @@
"Month11" = "November"; "Month11" = "November";
"Month12" = "December"; "Month12" = "December";
"GroupLeaveGroup" = "Leave Group"; "GroupLeaveGroup" = "Leave Group";
"DeleteChatUser" = "Delete chat"; "DeleteChat" = "Delete Chat";
"AccDescrGroup" = "Group"; "AccDescrGroup" = "Group";
"AccDescrChannel" = "Channel"; "AccDescrChannel" = "Channel";
"Nothing" = "Nothing"; "Nothing" = "Nothing";
@ -85,6 +85,9 @@
"ProfileCopyPhone" = "Copy Phone Number"; "ProfileCopyPhone" = "Copy Phone Number";
"ContextCopySelected" = "Copy Selected Text"; "ContextCopySelected" = "Copy Selected Text";
"ContextCopyText" = "Copy Text"; "ContextCopyText" = "Copy Text";
"ContextArchiveCollapse" = "Collapse";
"ContextArchiveExpand" = "Collapse";
"ContextArchiveToMenu" = "Move to Main Menu";
"CallMessageVideoIncomingDeclined" = "Declined Video Call"; "CallMessageVideoIncomingDeclined" = "Declined Video Call";
"CallMessageVideoOutgoingMissed" = "Canceled Video Call"; "CallMessageVideoOutgoingMissed" = "Canceled Video Call";
"CallMessageVideoIncomingMissed" = "Missed Video Call"; "CallMessageVideoIncomingMissed" = "Missed Video Call";
@ -478,8 +481,9 @@
"PasscodeControllerDisableTitle" = "Disable Passcode"; "PasscodeControllerDisableTitle" = "Disable Passcode";
"PasscodeControllerChangeTitle" = "Change Passcode"; "PasscodeControllerChangeTitle" = "Change Passcode";
"FilterNew" = "New Folder"; "FilterNew" = "New Folder";
"FilterEdit" = "Edit folder"; "EditFolder" = "Edit Folder";
"FilterDelete" = "Delete folder"; "FilterEditFolders" = "Edit Folders";
"FilterMenuDelete" = "Delete Folder";
"FilterShare" = "Share"; "FilterShare" = "Share";
"AutoDeleteConfirm" = "Confirm"; "AutoDeleteConfirm" = "Confirm";
"LogOutTitle" = "Log Out"; "LogOutTitle" = "Log Out";
@ -827,7 +831,7 @@
"EmptyGroupInfoLine3" = "• Public links such as t.me/title"; "EmptyGroupInfoLine3" = "• Public links such as t.me/title";
"EmptyGroupInfoLine4" = "• Admins with different rights"; "EmptyGroupInfoLine4" = "• Admins with different rights";
"Reactions" = "Reactions"; "Reactions" = "Reactions";
"MarkAllAsRead" = "Mark all as read"; "ChatListMarkAllAsRead" = "Mark All as Read";
"PaymentCardNumber" = "Card Number"; "PaymentCardNumber" = "Card Number";
"PaymentCheckoutAcceptRecurrent" = "I accept the *Terms of Service* of {bot}."; "PaymentCheckoutAcceptRecurrent" = "I accept the *Terms of Service* of {bot}.";
"CheckoutTotalAmount" = "Total"; "CheckoutTotalAmount" = "Total";
@ -881,7 +885,7 @@
"ChannelSubscribers" = "Subscribers"; "ChannelSubscribers" = "Subscribers";
"ChannelBlockedUsers" = "Removed users"; "ChannelBlockedUsers" = "Removed users";
"ChannelDelete" = "Delete Channel"; "ChannelDelete" = "Delete Channel";
"LeaveChannel" = "Leave channel"; "ChannelLeave" = "Leave Channel";
"ChannelDeleteAlert" = "Deleting this channel will remove all subscribers and all posts will be lost. Delete the channel anyway?"; "ChannelDeleteAlert" = "Deleting this channel will remove all subscribers and all posts will be lost. Delete the channel anyway?";
"ChannelLeaveAlert" = "Are you sure you want to leave this channel?"; "ChannelLeaveAlert" = "Are you sure you want to leave this channel?";
"ChannelCreator" = "Owner"; "ChannelCreator" = "Owner";
@ -1178,10 +1182,13 @@
"ConversationViewChannel" = "VIEW CHANNEL"; "ConversationViewChannel" = "VIEW CHANNEL";
"Telegram" = "Telegram"; "Telegram" = "Telegram";
"ChatListFilterAddToFolder" = "Add to folder..."; "ChatListFilterAddToFolder" = "Add to folder...";
"UnpinFromTop" = "Unpin from top"; "ChatListUnpinFromTop" = "Unpin from Top";
"PinToTop" = "Pin to top"; "ChatListPinToTop" = "Pin to Top";
"MarkAsRead" = "Mark as read"; "ChatListOpenInNewWindow" = "Open in New Window";
"MarkAsUnread" = "Mark as unread"; "ChatListOpenInNewTab" = "Open in New Tab";
"ChatListContextMaskAsRead" = "Mark as Read";
"ChatListContextMaskAsUnread" = "Mark as Unread";
"ChatListContextAddToFolder" = "Add to Folder";
"Unarchive" = "Unarchive"; "Unarchive" = "Unarchive";
"Archive" = "Archive"; "Archive" = "Archive";
"WaitingForNetwork" = "Waiting for network..."; "WaitingForNetwork" = "Waiting for network...";

View File

@ -109,7 +109,7 @@ const GiftMenuItems = ({
<> <>
{hasPinOptions && ( {hasPinOptions && (
<MenuItem icon={savedGift.isPinned ? 'unpin' : 'pin'} onClick={handleTogglePin}> <MenuItem icon={savedGift.isPinned ? 'unpin' : 'pin'} onClick={handleTogglePin}>
{lang(savedGift.isPinned ? 'UnpinFromTop' : 'PinToTop')} {lang(savedGift.isPinned ? 'ChatListUnpinFromTop' : 'ChatListPinToTop')}
</MenuItem> </MenuItem>
)} )}
<MenuItem icon="link-badge" onClick={handleCopyLink}> <MenuItem icon="link-badge" onClick={handleCopyLink}>

View File

@ -13,7 +13,7 @@ import { formatIntegerCompact } from '../../../util/textFormat';
import renderText from '../../common/helpers/renderText'; import renderText from '../../common/helpers/renderText';
import { useFolderManagerForOrderedIds, useFolderManagerForUnreadCounters } from '../../../hooks/useFolderManager'; import { useFolderManagerForOrderedIds, useFolderManagerForUnreadCounters } from '../../../hooks/useFolderManager';
import useOldLang from '../../../hooks/useOldLang'; import useLang from '../../../hooks/useLang';
import Avatar from '../../common/Avatar'; import Avatar from '../../common/Avatar';
import Icon from '../../common/icons/Icon'; import Icon from '../../common/icons/Icon';
@ -42,7 +42,7 @@ const Archive: FC<OwnProps> = ({
onClick, onClick,
}) => { }) => {
const { updateArchiveSettings } = getActions(); const { updateArchiveSettings } = getActions();
const lang = useOldLang(); const lang = useLang();
const orderedChatIds = useFolderManagerForOrderedIds(ARCHIVED_FOLDER_ID); const orderedChatIds = useFolderManagerForOrderedIds(ARCHIVED_FOLDER_ID);
const unreadCounters = useFolderManagerForUnreadCounters(); const unreadCounters = useFolderManagerForUnreadCounters();
@ -75,7 +75,7 @@ const Archive: FC<OwnProps> = ({
const contextActions = useMemo(() => { const contextActions = useMemo(() => {
const actionMinimize = !archiveSettings.isMinimized && { const actionMinimize = !archiveSettings.isMinimized && {
title: lang('lng_context_archive_collapse'), title: lang('ContextArchiveCollapse'),
icon: 'collapse', icon: 'collapse',
handler: () => { handler: () => {
updateArchiveSettings({ isMinimized: true }); updateArchiveSettings({ isMinimized: true });
@ -83,7 +83,7 @@ const Archive: FC<OwnProps> = ({
} satisfies MenuItemContextAction; } satisfies MenuItemContextAction;
const actionExpand = archiveSettings.isMinimized && { const actionExpand = archiveSettings.isMinimized && {
title: lang('lng_context_archive_expand'), title: lang('ContextArchiveExpand'),
icon: 'expand', icon: 'expand',
handler: () => { handler: () => {
updateArchiveSettings({ isMinimized: false }); updateArchiveSettings({ isMinimized: false });
@ -91,7 +91,7 @@ const Archive: FC<OwnProps> = ({
} satisfies MenuItemContextAction; } satisfies MenuItemContextAction;
const actionHide = { const actionHide = {
title: lang('lng_context_archive_to_menu'), title: lang('ContextArchiveToMenu'),
icon: 'archive-to-main', icon: 'archive-to-main',
handler: () => { handler: () => {
updateArchiveSettings({ isHidden: true }); updateArchiveSettings({ isHidden: true });

View File

@ -7,9 +7,10 @@ import { getActions, getGlobal, withGlobal } from '../../../global';
import type { ApiChatFolder, ApiChatlistExportedInvite, ApiSession } from '../../../api/types'; import type { ApiChatFolder, ApiChatlistExportedInvite, ApiSession } from '../../../api/types';
import type { GlobalState } from '../../../global/types'; import type { GlobalState } from '../../../global/types';
import type { FolderEditDispatch } from '../../../hooks/reducers/useFoldersReducer'; import type { FolderEditDispatch } from '../../../hooks/reducers/useFoldersReducer';
import type { LeftColumnContent, SettingsScreens } from '../../../types'; import type { LeftColumnContent } from '../../../types';
import type { MenuItemContextAction } from '../../ui/ListItem'; import type { MenuItemContextAction } from '../../ui/ListItem';
import type { TabWithProperties } from '../../ui/TabList'; import type { TabWithProperties } from '../../ui/TabList';
import { SettingsScreens } from '../../../types';
import { ALL_FOLDER_ID } from '../../../config'; import { ALL_FOLDER_ID } from '../../../config';
import { selectCanShareFolder, selectTabState } from '../../../global/selectors'; import { selectCanShareFolder, selectTabState } from '../../../global/selectors';
@ -22,7 +23,10 @@ import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities'; import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
import useDerivedState from '../../../hooks/useDerivedState'; import useDerivedState from '../../../hooks/useDerivedState';
import { useFolderManagerForUnreadCounters } from '../../../hooks/useFolderManager'; import {
useFolderManagerForUnreadChatsByFolder,
useFolderManagerForUnreadCounters,
} from '../../../hooks/useFolderManager';
import useHistoryBack from '../../../hooks/useHistoryBack'; import useHistoryBack from '../../../hooks/useHistoryBack';
import useLang from '../../../hooks/useLang'; import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback'; import useLastCallback from '../../../hooks/useLastCallback';
@ -90,6 +94,7 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
openDeleteChatFolderModal, openDeleteChatFolderModal,
openEditChatFolder, openEditChatFolder,
openLimitReachedModal, openLimitReachedModal,
markChatMessagesRead,
} = getActions(); } = getActions();
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
@ -112,6 +117,14 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
}); });
const isStoryRibbonClosing = useDerivedState(getIsStoryRibbonClosing); const isStoryRibbonClosing = useDerivedState(getIsStoryRibbonClosing);
const scrollToTop = useLastCallback(() => {
const activeList = ref.current?.querySelector<HTMLElement>('.chat-list.Transition_slide-active');
activeList?.scrollTo({
top: 0,
behavior: 'smooth',
});
});
const allChatsFolder: ApiChatFolder = useMemo(() => { const allChatsFolder: ApiChatFolder = useMemo(() => {
return { return {
id: ALL_FOLDER_ID, id: ALL_FOLDER_ID,
@ -137,6 +150,16 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
const isInAllChatsFolder = allChatsFolderIndex === activeChatFolder; const isInAllChatsFolder = allChatsFolderIndex === activeChatFolder;
const isInFirstFolder = FIRST_FOLDER_INDEX === activeChatFolder; const isInFirstFolder = FIRST_FOLDER_INDEX === activeChatFolder;
const folderUnreadChatsCountersById = useFolderManagerForUnreadChatsByFolder();
const handleReadAllChats = useLastCallback((folderId: number) => {
const unreadChatIds = folderUnreadChatsCountersById[folderId];
if (!unreadChatIds?.length) return;
unreadChatIds.forEach((chatId) => {
markChatMessagesRead({ id: chatId });
});
});
const folderCountersById = useFolderManagerForUnreadCounters(); const folderCountersById = useFolderManagerForUnreadCounters();
const folderTabs = useMemo(() => { const folderTabs = useMemo(() => {
if (!displayedFolders || !displayedFolders.length) { if (!displayedFolders || !displayedFolders.length) {
@ -177,17 +200,41 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
}); });
} }
if (id !== ALL_FOLDER_ID) { if (id === ALL_FOLDER_ID) {
contextActions.push({ contextActions.push({
title: lang('FilterEdit'), title: lang('FilterEditFolders'),
icon: 'edit',
handler: () => {
onSettingsScreenSelect(SettingsScreens.Folders);
},
});
if (folderUnreadChatsCountersById[id]?.length) {
contextActions.push({
title: lang('ChatListMarkAllAsRead'),
icon: 'readchats',
handler: () => handleReadAllChats(folder.id),
});
}
} else {
contextActions.push({
title: lang('EditFolder'),
icon: 'edit', icon: 'edit',
handler: () => { handler: () => {
openEditChatFolder({ folderId: id }); openEditChatFolder({ folderId: id });
}, },
}); });
if (folderUnreadChatsCountersById[id]?.length) {
contextActions.push({
title: lang('ChatListMarkAllAsRead'),
icon: 'readchats',
handler: () => handleReadAllChats(folder.id),
});
}
contextActions.push({ contextActions.push({
title: lang('FilterDelete'), title: lang('FilterMenuDelete'),
icon: 'delete', icon: 'delete',
destructive: true, destructive: true,
handler: () => { handler: () => {
@ -211,11 +258,14 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
}); });
}, [ }, [
displayedFolders, maxFolders, folderCountersById, lang, chatFoldersById, maxChatLists, folderInvitesById, displayedFolders, maxFolders, folderCountersById, lang, chatFoldersById, maxChatLists, folderInvitesById,
maxFolderInvites, maxFolderInvites, folderUnreadChatsCountersById, onSettingsScreenSelect,
]); ]);
const handleSwitchTab = useLastCallback((index: number) => { const handleSwitchTab = useLastCallback((index: number) => {
setActiveChatFolder({ activeChatFolder: index }, { forceOnHeavyAnimation: true }); setActiveChatFolder({ activeChatFolder: index }, { forceOnHeavyAnimation: true });
if (activeChatFolder === index) {
scrollToTop();
}
}); });
// Prevent `activeTab` pointing at non-existing folder after update // Prevent `activeTab` pointing at non-existing folder after update

View File

@ -55,7 +55,7 @@
.badge { .badge {
min-width: 1.25rem; min-width: 1.25rem;
height: 1.25rem; height: 1.25rem;
margin-inline-start: 0.5rem; margin-inline-start: 0.3125rem;
background: var(--color-gray); background: var(--color-gray);
border-radius: 0.75rem; border-radius: 0.75rem;
padding: 0 0.3125rem; padding: 0 0.3125rem;

View File

@ -11,7 +11,7 @@ import {
} from '../global/helpers'; } from '../global/helpers';
import { IS_ELECTRON, IS_OPEN_IN_NEW_TAB_SUPPORTED } from '../util/browser/windowEnvironment'; import { IS_ELECTRON, IS_OPEN_IN_NEW_TAB_SUPPORTED } from '../util/browser/windowEnvironment';
import { compact } from '../util/iteratees'; import { compact } from '../util/iteratees';
import useOldLang from './useOldLang'; import useLang from './useLang';
const useChatContextActions = ({ const useChatContextActions = ({
chat, chat,
@ -44,7 +44,7 @@ const useChatContextActions = ({
handleChatFolderChange: NoneToVoidFunction; handleChatFolderChange: NoneToVoidFunction;
handleReport?: NoneToVoidFunction; handleReport?: NoneToVoidFunction;
}, isInSearch = false) => { }, isInSearch = false) => {
const lang = useOldLang(); const lang = useLang();
const { isSelf } = user || {}; const { isSelf } = user || {};
const isServiceNotifications = user?.id === SERVICE_NOTIFICATIONS_USER_ID; const isServiceNotifications = user?.id === SERVICE_NOTIFICATIONS_USER_ID;
@ -57,7 +57,7 @@ const useChatContextActions = ({
} }
if (isUserId(chat.id)) { if (isUserId(chat.id)) {
return lang('DeleteChatUser'); return lang('DeleteChat');
} }
if (getCanDeleteChat(chat)) { if (getCanDeleteChat(chat)) {
@ -65,10 +65,10 @@ const useChatContextActions = ({
} }
if (isChatChannel(chat)) { if (isChatChannel(chat)) {
return lang('LeaveChannel'); return lang('ChannelLeave');
} }
return lang('Group.LeaveGroup'); return lang('GroupLeaveGroup');
}, [chat, isSavedDialog, lang]); }, [chat, isSavedDialog, lang]);
return useMemo(() => { return useMemo(() => {
@ -87,7 +87,7 @@ const useChatContextActions = ({
} = getActions(); } = getActions();
const actionOpenInNewTab = IS_OPEN_IN_NEW_TAB_SUPPORTED && { const actionOpenInNewTab = IS_OPEN_IN_NEW_TAB_SUPPORTED && {
title: IS_ELECTRON ? 'Open in new window' : 'Open in new tab', title: IS_ELECTRON ? lang('ChatListOpenInNewWindow') : lang('ChatListOpenInNewTab'),
icon: 'open-in-new-tab', icon: 'open-in-new-tab',
handler: () => { handler: () => {
if (isSavedDialog) { if (isSavedDialog) {
@ -108,12 +108,12 @@ const useChatContextActions = ({
const actionPin = isPinned const actionPin = isPinned
? { ? {
title: lang('UnpinFromTop'), title: lang('ChatListUnpinFromTop'),
icon: 'unpin', icon: 'unpin',
handler: togglePinned, handler: togglePinned,
} }
: { : {
title: lang('PinToTop'), title: lang('ChatListPinToTop'),
icon: 'pin', icon: 'pin',
handler: togglePinned, handler: togglePinned,
}; };
@ -130,19 +130,19 @@ const useChatContextActions = ({
} }
const actionAddToFolder = canChangeFolder ? { const actionAddToFolder = canChangeFolder ? {
title: lang('ChatList.Filter.AddToFolder'), title: lang('ChatListContextAddToFolder'),
icon: 'folder', icon: 'folder',
handler: handleChatFolderChange, handler: handleChatFolderChange,
} : undefined; } : undefined;
const actionMute = isMuted const actionMute = isMuted
? { ? {
title: lang('ChatList.Unmute'), title: lang('ChatsUnmute'),
icon: 'unmute', icon: 'unmute',
handler: () => updateChatMutedState({ chatId: chat.id, isMuted: false }), handler: () => updateChatMutedState({ chatId: chat.id, isMuted: false }),
} }
: { : {
title: `${lang('ChatList.Mute')}...`, title: `${lang('ChatsMute')}...`,
icon: 'mute', icon: 'mute',
handler: handleMute, handler: handleMute,
}; };
@ -153,10 +153,13 @@ const useChatContextActions = ({
const actionMaskAsRead = ( const actionMaskAsRead = (
chat.unreadCount || chat.hasUnreadMark || Object.values(topics || {}).some(({ unreadCount }) => unreadCount) chat.unreadCount || chat.hasUnreadMark || Object.values(topics || {}).some(({ unreadCount }) => unreadCount)
) ? { title: lang('MarkAsRead'), icon: 'readchats', handler: () => markChatMessagesRead({ id: chat.id }) } ) ? {
: undefined; title: lang('ChatListContextMaskAsRead'),
icon: 'readchats',
handler: () => markChatMessagesRead({ id: chat.id }),
} : undefined;
const actionMarkAsUnread = !(chat.unreadCount || chat.hasUnreadMark) && !chat.isForum const actionMarkAsUnread = !(chat.unreadCount || chat.hasUnreadMark) && !chat.isForum
? { title: lang('MarkAsUnread'), icon: 'unread', handler: () => markChatUnread({ id: chat.id }) } ? { title: lang('ChatListContextMaskAsUnread'), icon: 'unread', handler: () => markChatUnread({ id: chat.id }) }
: undefined; : undefined;
const actionArchive = isChatArchived(chat) const actionArchive = isChatArchived(chat)
@ -165,7 +168,7 @@ const useChatContextActions = ({
const canReport = handleReport && !user && (isChatChannel(chat) || isChatGroup(chat)); const canReport = handleReport && !user && (isChatChannel(chat) || isChatGroup(chat));
const actionReport = canReport const actionReport = canReport
? { title: lang('ReportPeer.Report'), icon: 'flag', handler: handleReport } ? { title: lang('ReportPeerReport'), icon: 'flag', handler: handleReport }
: undefined; : undefined;
const isInFolder = folderId !== undefined; const isInFolder = folderId !== undefined;

View File

@ -2,10 +2,10 @@ import { useEffect } from '../lib/teact/teact';
import { import {
addChatsCountCallback, addChatsCountCallback,
addOrderedIdsCallback, addOrderedIdsCallback, addUnreadChatsByFolderIdCallback,
addUnreadCountersCallback, addUnreadCountersCallback,
getChatsCount, getChatsCount,
getOrderedIds, getOrderedIds, getUnreadChatsByFolderId,
getUnreadCounters, getUnreadCounters,
} from '../util/folderManager'; } from '../util/folderManager';
import useForceUpdate from './useForceUpdate'; import useForceUpdate from './useForceUpdate';
@ -33,3 +33,11 @@ export function useFolderManagerForChatsCount() {
return getChatsCount(); return getChatsCount();
} }
export function useFolderManagerForUnreadChatsByFolder() {
const forceUpdate = useForceUpdate();
useEffect(() => addUnreadChatsByFolderIdCallback(forceUpdate), [forceUpdate]);
return getUnreadChatsByFolderId();
}

View File

@ -14,7 +14,7 @@ export interface LangPair {
'Month11': undefined; 'Month11': undefined;
'Month12': undefined; 'Month12': undefined;
'GroupLeaveGroup': undefined; 'GroupLeaveGroup': undefined;
'DeleteChatUser': undefined; 'DeleteChat': undefined;
'AccDescrGroup': undefined; 'AccDescrGroup': undefined;
'AccDescrChannel': undefined; 'AccDescrChannel': undefined;
'Nothing': undefined; 'Nothing': undefined;
@ -67,6 +67,9 @@ export interface LangPair {
'ProfileCopyPhone': undefined; 'ProfileCopyPhone': undefined;
'ContextCopySelected': undefined; 'ContextCopySelected': undefined;
'ContextCopyText': undefined; 'ContextCopyText': undefined;
'ContextArchiveCollapse': undefined;
'ContextArchiveExpand': undefined;
'ContextArchiveToMenu': undefined;
'CallMessageVideoIncomingDeclined': undefined; 'CallMessageVideoIncomingDeclined': undefined;
'CallMessageVideoOutgoingMissed': undefined; 'CallMessageVideoOutgoingMissed': undefined;
'CallMessageVideoIncomingMissed': undefined; 'CallMessageVideoIncomingMissed': undefined;
@ -428,8 +431,9 @@ export interface LangPair {
'PasscodeControllerDisableTitle': undefined; 'PasscodeControllerDisableTitle': undefined;
'PasscodeControllerChangeTitle': undefined; 'PasscodeControllerChangeTitle': undefined;
'FilterNew': undefined; 'FilterNew': undefined;
'FilterEdit': undefined; 'EditFolder': undefined;
'FilterDelete': undefined; 'FilterEditFolders': undefined;
'FilterMenuDelete': undefined;
'FilterShare': undefined; 'FilterShare': undefined;
'AutoDeleteConfirm': undefined; 'AutoDeleteConfirm': undefined;
'LogOutTitle': undefined; 'LogOutTitle': undefined;
@ -710,7 +714,7 @@ export interface LangPair {
'EmptyGroupInfoLine3': undefined; 'EmptyGroupInfoLine3': undefined;
'EmptyGroupInfoLine4': undefined; 'EmptyGroupInfoLine4': undefined;
'Reactions': undefined; 'Reactions': undefined;
'MarkAllAsRead': undefined; 'ChatListMarkAllAsRead': undefined;
'PaymentCardNumber': undefined; 'PaymentCardNumber': undefined;
'CheckoutTotalAmount': undefined; 'CheckoutTotalAmount': undefined;
'PaymentCheckoutMethod': undefined; 'PaymentCheckoutMethod': undefined;
@ -761,7 +765,7 @@ export interface LangPair {
'ChannelSubscribers': undefined; 'ChannelSubscribers': undefined;
'ChannelBlockedUsers': undefined; 'ChannelBlockedUsers': undefined;
'ChannelDelete': undefined; 'ChannelDelete': undefined;
'LeaveChannel': undefined; 'ChannelLeave': undefined;
'ChannelDeleteAlert': undefined; 'ChannelDeleteAlert': undefined;
'ChannelLeaveAlert': undefined; 'ChannelLeaveAlert': undefined;
'ChannelCreator': undefined; 'ChannelCreator': undefined;
@ -976,6 +980,7 @@ export interface LangPair {
'ChatListFilterErrorEmpty': undefined; 'ChatListFilterErrorEmpty': undefined;
'ChatListFilterErrorTitleEmpty': undefined; 'ChatListFilterErrorTitleEmpty': undefined;
'FilterMuted': undefined; 'FilterMuted': undefined;
'ReadFolder': undefined;
'FilterRead': undefined; 'FilterRead': undefined;
'FilterArchived': undefined; 'FilterArchived': undefined;
'GroupsAndChannelsLimitTitle': undefined; 'GroupsAndChannelsLimitTitle': undefined;
@ -1008,10 +1013,13 @@ export interface LangPair {
'ConversationViewChannel': undefined; 'ConversationViewChannel': undefined;
'Telegram': undefined; 'Telegram': undefined;
'ChatListFilterAddToFolder': undefined; 'ChatListFilterAddToFolder': undefined;
'UnpinFromTop': undefined; 'ChatListUnpinFromTop': undefined;
'PinToTop': undefined; 'ChatListPinToTop': undefined;
'MarkAsRead': undefined; 'ChatListOpenInNewWindow': undefined;
'MarkAsUnread': undefined; 'ChatListOpenInNewTab': undefined;
'ChatListContextMaskAsRead': undefined;
'ChatListContextMaskAsUnread': undefined;
'ChatListContextAddToFolder': undefined;
'Unarchive': undefined; 'Unarchive': undefined;
'Archive': undefined; 'Archive': undefined;
'WaitingForNetwork': undefined; 'WaitingForNetwork': undefined;

View File

@ -97,12 +97,14 @@ let results: {
chatsCount: number; chatsCount: number;
notificationsCount: number; notificationsCount: number;
} | undefined>; } | undefined>;
unreadChatIdsByFolderId: Record<string, string[] | undefined>;
} = initials.results; } = initials.results;
let callbacks: { let callbacks: {
orderedIdsByFolderId: Record<number, CallbackManager>; orderedIdsByFolderId: Record<number, CallbackManager>;
chatsCountByFolderId: CallbackManager; chatsCountByFolderId: CallbackManager;
unreadCountersByFolderId: CallbackManager; unreadCountersByFolderId: CallbackManager;
unreadChatIdsByFolderId: CallbackManager;
} = initials.callbacks; } = initials.callbacks;
if (DEBUG) { if (DEBUG) {
@ -159,6 +161,12 @@ export function getUnreadCounters() {
return results.unreadCountersByFolderId; return results.unreadCountersByFolderId;
} }
export function getUnreadChatsByFolderId() {
if (!inited) init();
return results.unreadChatIdsByFolderId;
}
export function getAllNotificationsCount() { export function getAllNotificationsCount() {
return getUnreadCounters()[ALL_FOLDER_ID]?.notificationsCount || 0; return getUnreadCounters()[ALL_FOLDER_ID]?.notificationsCount || 0;
} }
@ -182,6 +190,12 @@ export function addChatsCountCallback(callback: (chatsCount: typeof results.chat
return callbacks.chatsCountByFolderId.addCallback(callback); return callbacks.chatsCountByFolderId.addCallback(callback);
} }
export function addUnreadChatsByFolderIdCallback(
callback: (unreadChats: typeof results.unreadChatIdsByFolderId) => void,
) {
return callbacks.unreadChatIdsByFolderId.addCallback(callback);
}
export function addUnreadCountersCallback(callback: (unreadCounters: typeof results.unreadCountersByFolderId) => void) { export function addUnreadCountersCallback(callback: (unreadCounters: typeof results.unreadCountersByFolderId) => void) {
return callbacks.unreadCountersByFolderId.addCallback(callback); return callbacks.unreadCountersByFolderId.addCallback(callback);
} }
@ -677,6 +691,7 @@ function updateListsForChat(chatId: string, currentFolderIds: number[], newFolde
function updateResults(affectedFolderIds: number[]) { function updateResults(affectedFolderIds: number[]) {
let wasUnreadCountersChanged = false; let wasUnreadCountersChanged = false;
let wasChatsCountChanged = false; let wasChatsCountChanged = false;
let wasUnreadChatsChanged = false;
Array.from(affectedFolderIds).forEach((folderId) => { Array.from(affectedFolderIds).forEach((folderId) => {
const { pinnedCount: newPinnedCount, orderedIds: newOrderedIds } = buildFolderOrderedIds(folderId); const { pinnedCount: newPinnedCount, orderedIds: newOrderedIds } = buildFolderOrderedIds(folderId);
@ -715,6 +730,15 @@ function updateResults(affectedFolderIds: number[]) {
); );
} }
results.unreadCountersByFolderId[folderId] = newUnreadCounters; results.unreadCountersByFolderId[folderId] = newUnreadCounters;
const currentUnreadChats = results.unreadChatIdsByFolderId[folderId];
const newUnreadChats = buildFolderByUnreadChats(folderId);
if (!wasUnreadChatsChanged) {
wasUnreadChatsChanged = (
!currentUnreadChats || !areSortedArraysEqual(newUnreadChats, currentUnreadChats)
);
}
results.unreadChatIdsByFolderId[folderId] = newUnreadChats;
}); });
if (wasChatsCountChanged) { if (wasChatsCountChanged) {
@ -730,6 +754,10 @@ function updateResults(affectedFolderIds: number[]) {
results.unreadCountersByFolderId = newValue; results.unreadCountersByFolderId = newValue;
callbacks.unreadCountersByFolderId.runCallbacks(newValue); callbacks.unreadCountersByFolderId.runCallbacks(newValue);
} }
if (wasUnreadChatsChanged) {
callbacks.unreadChatIdsByFolderId.runCallbacks(results.unreadChatIdsByFolderId);
}
} }
function buildFolderOrderedIds(folderId: number) { function buildFolderOrderedIds(folderId: number) {
@ -801,6 +829,16 @@ function buildFolderUnreadCounters(folderId: number) {
}); });
} }
function buildFolderByUnreadChats(folderId: number) {
const { chatSummariesById } = prepared;
const { orderedIdsByFolderId: { [folderId]: orderedIds } } = results;
return orderedIds!.filter((chatId) => {
const chatSummary = chatSummariesById.get(chatId);
return chatSummary?.isUnread;
});
}
function buildInitials() { function buildInitials() {
return { return {
prevGlobal: { prevGlobal: {
@ -823,12 +861,14 @@ function buildInitials() {
pinnedCountByFolderId: {}, pinnedCountByFolderId: {},
chatsCountByFolderId: {}, chatsCountByFolderId: {},
unreadCountersByFolderId: {}, unreadCountersByFolderId: {},
unreadChatIdsByFolderId: {},
}, },
callbacks: { callbacks: {
orderedIdsByFolderId: {}, orderedIdsByFolderId: {},
chatsCountByFolderId: createCallbackManager(), chatsCountByFolderId: createCallbackManager(),
unreadCountersByFolderId: createCallbackManager(), unreadCountersByFolderId: createCallbackManager(),
unreadChatIdsByFolderId: createCallbackManager(),
}, },
}; };
} }