From 962d3e321fe78465a47362f33be2f168efc9124f Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Tue, 28 Feb 2023 18:43:11 +0100 Subject: [PATCH] Introduce Message Threads (#2546) --- src/api/gramjs/apiBuilders/messages.ts | 12 +- src/api/gramjs/methods/messages.ts | 14 +- src/api/gramjs/updater.ts | 8 + src/api/types/messages.ts | 1 + src/assets/fonts/icomoon.woff | Bin 53568 -> 54732 bytes src/assets/fonts/icomoon.woff2 | Bin 24896 -> 25496 bytes .../common/ChatForumLastMessage.module.scss | 1 + src/components/common/StickerSetModal.tsx | 6 +- src/components/middle/MessageList.tsx | 8 +- src/components/middle/MessageListContent.tsx | 17 +- src/components/middle/MiddleColumn.tsx | 6 +- src/components/middle/MiddleHeader.tsx | 21 +- .../middle/message/CommentButton.tsx | 8 +- .../middle/message/ContextMenuContainer.tsx | 18 +- src/components/middle/message/Message.tsx | 12 +- .../middle/message/MessageContextMenu.tsx | 20 +- .../middle/message/MessageMeta.scss | 9 +- src/components/middle/message/MessageMeta.tsx | 24 +- .../middle/message/hooks/useInnerHandlers.ts | 8 + src/components/right/GifSearch.tsx | 6 +- .../right/management/ManageUser.tsx | 4 +- src/global/actions/api/chats.ts | 32 +- src/global/actions/api/messages.ts | 16 +- src/global/actions/api/sync.ts | 24 +- src/global/actions/apiUpdaters/chats.ts | 17 +- src/global/actions/apiUpdaters/messages.ts | 20 +- src/global/cache.ts | 19 +- src/global/helpers/chats.ts | 6 +- src/global/init.ts | 33 +- src/global/reducers/messages.ts | 3 +- src/global/selectors/messages.ts | 15 +- src/global/types.ts | 5 + src/styles/Telegram T.json | 391 ++++++++++-------- src/styles/icons.scss | 3 + 34 files changed, 511 insertions(+), 276 deletions(-) diff --git a/src/api/gramjs/apiBuilders/messages.ts b/src/api/gramjs/apiBuilders/messages.ts index 7792ab860..4cee6f120 100644 --- a/src/api/gramjs/apiBuilders/messages.ts +++ b/src/api/gramjs/apiBuilders/messages.ts @@ -235,7 +235,7 @@ export function buildApiMessageWithChatId( }), ...(shouldHideKeyboardButtons && { shouldHideKeyboardButtons, isHideKeyboardSelective }), ...(mtpMessage.viaBotId && { viaBotId: buildApiPeerId(mtpMessage.viaBotId, 'user') }), - ...(replies?.comments && { repliesThreadInfo: buildThreadInfo(replies, mtpMessage.id, chatId) }), + ...(replies && { repliesThreadInfo: buildThreadInfo(replies, mtpMessage.id, chatId) }), ...(postAuthor && { postAuthorTitle: postAuthor }), isProtected, isForwardingAllowed, @@ -1591,20 +1591,18 @@ function buildThreadInfo( messageReplies: GramJs.TypeMessageReplies, messageId: number, chatId: string, ): ApiThreadInfo | undefined { const { - channelId, replies, maxId, readMaxId, recentRepliers, + channelId, replies, maxId, readMaxId, recentRepliers, comments, } = messageReplies; - if (!channelId) { - return undefined; - } - const apiChannelId = buildApiPeerId(channelId, 'channel'); + const apiChannelId = channelId ? buildApiPeerId(channelId, 'channel') : undefined; if (apiChannelId === DELETED_COMMENTS_CHANNEL_ID) { return undefined; } - const isPostThread = chatId !== apiChannelId; + const isPostThread = apiChannelId && chatId !== apiChannelId; return { + isComments: comments, threadId: messageId, ...(isPostThread ? { chatId: apiChannelId, diff --git a/src/api/gramjs/methods/messages.ts b/src/api/gramjs/methods/messages.ts index af95379c6..d4d270db0 100644 --- a/src/api/gramjs/methods/messages.ts +++ b/src/api/gramjs/methods/messages.ts @@ -850,9 +850,9 @@ export async function markMessagesRead({ } export async function requestThreadInfoUpdate({ - chat, threadId, + chat, threadId, originChannelId, }: { - chat: ApiChat; threadId: number; + chat: ApiChat; threadId: number; originChannelId?: string; }) { if (threadId === MAIN_THREAD_ID) { return undefined; @@ -881,15 +881,18 @@ export async function requestThreadInfoUpdate({ return undefined; } + const topMessageId = topMessageResult.messages[topMessageResult.messages.length - 1].id; + onUpdate({ '@type': 'updateThreadInfo', chatId: discussionChatId, - threadId, + threadId: topMessageId, threadInfo: { - threadId, - topMessageId: topMessageResult.messages[topMessageResult.messages.length - 1].id, + threadId: topMessageId, + topMessageId, lastReadInboxMessageId: topMessageResult.readInboxMaxId, messagesCount: (repliesResult instanceof GramJs.messages.ChannelMessages) ? repliesResult.count : undefined, + ...(originChannelId ? { originChannelId } : undefined), }, firstMessageId: repliesResult && 'messages' in repliesResult && repliesResult.messages.length ? repliesResult.messages[0].id @@ -920,6 +923,7 @@ export async function requestThreadInfoUpdate({ const users = topMessageResult.users.map(buildApiUser).filter(Boolean); return { + topMessageId, discussionChatId, users, }; diff --git a/src/api/gramjs/updater.ts b/src/api/gramjs/updater.ts index a21fa3419..f0f1d98a7 100644 --- a/src/api/gramjs/updater.ts +++ b/src/api/gramjs/updater.ts @@ -603,6 +603,14 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) { lastReadInboxMessageId: update.readMaxId, }, }); + } else if (update instanceof GramJs.UpdateReadChannelDiscussionOutbox) { + onUpdate({ + '@type': 'updateChat', + id: buildApiPeerId(update.channelId, 'channel'), + chat: { + lastReadOutboxMessageId: update.readMaxId, + }, + }); } else if ( update instanceof GramJs.UpdateDialogPinned && update.peer instanceof GramJs.DialogPeer diff --git a/src/api/types/messages.ts b/src/api/types/messages.ts index 6c02515aa..f9aa08ed3 100644 --- a/src/api/types/messages.ts +++ b/src/api/types/messages.ts @@ -491,6 +491,7 @@ export type ApiReactionCustomEmoji = { export type ApiReaction = ApiReactionEmoji | ApiReactionCustomEmoji; export interface ApiThreadInfo { + isComments?: boolean; threadId: number; chatId: string; topMessageId?: number; diff --git a/src/assets/fonts/icomoon.woff b/src/assets/fonts/icomoon.woff index a4f7b575c7d21cc83b08329aba9834dadf4f6503..257f0970f1c617004952aae6602588d304cb869b 100644 GIT binary patch delta 1477 zcmX|BOK2Tc7@mI~=giD~O_G^A(fdd{liW#@ax-(EX{|Rkg)U67Akr=rYp8jEi7m9y z3c;I&xN=h~qFX^xbQdWEq;%0uYfHoj3U2CNH)=t!TEBlLc<%Y{Ip=?T=l{L~bL%7R z-shTo<@Cvu3Mm*r+*5Mtx_!-^9X>d}xqMMkXdSK>*!(l!9ba3%#J;a^Huue0+jwIY zzT1j&e}nCpmj>^zuPnc+D0?w-dTe3q&WUw6;bXi>HhEj$THm~U6~1>AMg5EIx1Ub^ zym8??@&@0&1<*IyI!Q_i; zk51F>^;5J&yE`9{cV}oQXCMEleWq>GQ}iO8qhINF)Wks;2MH`}QiK_Fl4zd7s1tPI zkAfJMy1G##PMX9Mu_$hYokq7wm{F&J%5JCEA5eEdeQY{CY?6K_?#DgM1aru1YV(xz zTHXFUb^E<|fm)q5iZG8-(vDlnQEIo!ew}=`<@vGO#tt)f6E7|&UX>Ew&J)Mra%+?& z)M-xZGS>SIrwYyykel>0dD1tMdCHDaW7ta>gCN%S0)cRvpCN!E z&^80R_IWHfM z*Pm}$mLr7xQYG`>_sZj9tYYYfOC(7cD6JfJJa1$~29A?A3kAn1%~XU%YH0|j$dSk~ zZS$~)PHodN$Lx`C^u*TO_+;KZh#Kk4)#^dGG=1pM$N>lRY_i?l!DmVxdJUzdYs{HM zjy^`$*~q9u8`e!V@7X3Oz$I-C{^a-u~bJx(!iLuhmIFTg<5dB^~I@6qfZVIwMoa(WOaIsd6JtJ?5 zsBBmUi*6wioON_0r6q)_>2tGQQpsw*hC%S`uoYbi3JP{j2S@j5D*p* zyKJAHSPT-|1LSi+u|RrGWg1ZI2m`~37a+{nG)X-pH8F*O;nW(S8Z!`9X7sVl015)d zo&fnOAk2P*r8*Brp+yvY8W}dfwPzK)@Gh72N(fY CCR{84 diff --git a/src/assets/fonts/icomoon.woff2 b/src/assets/fonts/icomoon.woff2 index ec40bd44dfae06756d21f4e417ba163c97ad5664..f2b777ee836b8d89b6caa434b01fec18f420378f 100644 GIT binary patch literal 25496 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 const { chatId, threadId } = currentMessageList || {}; const chat = chatId && selectChat(global, chatId); const sendOptions = chat ? getAllowedAttachmentOptions(chat) : undefined; + const threadInfo = chatId && threadId ? selectThreadInfo(global, chatId, threadId) : undefined; + const isComments = Boolean(threadInfo?.originChannelId); const canSendStickers = Boolean( - chat && threadId && getCanPostInChat(chat, threadId) && sendOptions?.canSendStickers, + chat && threadId && getCanPostInChat(chat, threadId, isComments) && sendOptions?.canSendStickers, ); const isSavedMessages = Boolean(chatId) && selectIsChatWithSelf(global, chatId); diff --git a/src/components/middle/MessageList.tsx b/src/components/middle/MessageList.tsx index 041b7f247..cf9d3f1f5 100644 --- a/src/components/middle/MessageList.tsx +++ b/src/components/middle/MessageList.tsx @@ -28,7 +28,7 @@ import { selectFirstMessageId, selectChatScheduledMessages, selectCurrentMessageIds, - selectIsCurrentUserPremium, selectLastScrollOffset, + selectIsCurrentUserPremium, selectLastScrollOffset, selectThreadInfo, } from '../../global/selectors'; import { isChatChannel, @@ -96,6 +96,7 @@ type StateProps = { messageIds?: number[]; messagesById?: Record; firstUnreadId?: number; + isComments?: boolean; isViewportNewest?: boolean; isRestricted?: boolean; restrictionReason?: ApiRestrictionReason; @@ -144,6 +145,7 @@ const MessageList: FC = ({ messageIds, messagesById, firstUnreadId, + isComments, isViewportNewest, threadFirstMessageId, isRestricted, @@ -604,6 +606,8 @@ const MessageList: FC = ({ ( ? selectChatScheduledMessages(global, chatId) : selectChatMessages(global, chatId); const threadTopMessageId = selectThreadTopMessageId(global, chatId, threadId); + const threadInfo = selectThreadInfo(global, chatId, threadId); if ( threadId !== MAIN_THREAD_ID && !chat?.isForum @@ -687,6 +692,7 @@ export default memo(withGlobal( isBot: Boolean(chatBot), messageIds, messagesById, + isComments: Boolean(threadInfo?.originChannelId), firstUnreadId: selectFirstUnreadId(global, chatId, threadId), isViewportNewest: type !== 'thread' || selectIsViewportNewest(global, chatId, threadId), threadFirstMessageId: selectFirstMessageId(global, chatId, threadId), diff --git a/src/components/middle/MessageListContent.tsx b/src/components/middle/MessageListContent.tsx index d11e40039..2c9622656 100644 --- a/src/components/middle/MessageListContent.tsx +++ b/src/components/middle/MessageListContent.tsx @@ -1,10 +1,12 @@ import type { RefObject } from 'react'; import type { FC } from '../../lib/teact/teact'; import React, { memo } from '../../lib/teact/teact'; +import { getActions } from '../../global'; import type { MessageListType } from '../../global/types'; import { SCHEDULED_WHEN_ONLINE } from '../../config'; +import { MAIN_THREAD_ID } from '../../api/types'; import buildClassName from '../../util/buildClassName'; import { compact } from '../../util/iteratees'; import { formatHumanDate } from '../../util/dateFormat'; @@ -21,8 +23,6 @@ import useMessageObservers from './hooks/useMessageObservers'; import Message from './message/Message'; import SponsoredMessage from './message/SponsoredMessage'; import ActionMessage from './ActionMessage'; -import { getActions } from '../../global'; -import { MAIN_THREAD_ID } from '../../api/types'; interface OwnProps { isCurrentUserPremium?: boolean; @@ -33,6 +33,8 @@ interface OwnProps { isViewportNewest: boolean; isUnread: boolean; withUsers: boolean; + isChannelChat: boolean | undefined; + isComments?: boolean; noAvatars: boolean; containerRef: RefObject; anchorIdRef: { current: string | undefined }; @@ -60,7 +62,9 @@ const MessageListContent: FC = ({ messageGroups, isViewportNewest, isUnread, + isComments, withUsers, + isChannelChat, noAvatars, containerRef, anchorIdRef, @@ -190,6 +194,10 @@ const MessageListContent: FC = ({ // Service notifications saved in cache in previous versions may share the same `previousLocalId` const key = isServiceNotificationMessage(message) ? `${message.date}_${originalId}` : originalId; + const noComments = hasLinkedChat === false || !isChannelChat; + + const isTopicTopMessage = message.id === threadTopMessageId; + return compact([ message.id === memoUnreadDividerBeforeIdRef.current && unreadDivider, = ({ observeIntersectionForPlaying={observeIntersectionForPlaying} album={album} noAvatars={noAvatars} - withAvatar={position.isLastInGroup && withUsers && !isOwn && !(message.id === threadTopMessageId)} + withAvatar={position.isLastInGroup && withUsers && !isOwn && (!isTopicTopMessage || !isComments)} withSenderName={position.isFirstInGroup && withUsers && !isOwn} threadId={threadId} messageListType={type} - noComments={hasLinkedChat === false} + noComments={noComments} + noReplies={!noComments || threadId !== MAIN_THREAD_ID} appearanceOrder={messageCountToAnimate - ++appearanceIndex} isFirstInGroup={position.isFirstInGroup} isLastInGroup={position.isLastInGroup} diff --git a/src/components/middle/MiddleColumn.tsx b/src/components/middle/MiddleColumn.tsx index 0d94c859a..5b0d8ec0a 100644 --- a/src/components/middle/MiddleColumn.tsx +++ b/src/components/middle/MiddleColumn.tsx @@ -40,7 +40,7 @@ import { selectIsUserBlocked, selectPinnedIds, selectReplyingToId, - selectTheme, + selectTheme, selectThreadInfo, } from '../../global/selectors'; import { getCanPostInChat, @@ -630,7 +630,9 @@ export default memo(withGlobal( const pinnedIds = selectPinnedIds(global, chatId, threadId); const { chatId: audioChatId, messageId: audioMessageId } = audioPlayer; - const canPost = chat && getCanPostInChat(chat, threadId); + const threadInfo = selectThreadInfo(global, chatId, threadId); + const isComments = Boolean(threadInfo?.originChannelId); + const canPost = chat && getCanPostInChat(chat, threadId, isComments); const isBotNotStarted = selectIsChatBotNotStarted(global, chatId); const isPinnedMessageList = messageListType === 'pinned'; const isScheduledMessageList = messageListType === 'scheduled'; diff --git a/src/components/middle/MiddleHeader.tsx b/src/components/middle/MiddleHeader.tsx index 784f4b65b..4f124f379 100644 --- a/src/components/middle/MiddleHeader.tsx +++ b/src/components/middle/MiddleHeader.tsx @@ -89,6 +89,7 @@ type StateProps = { isRightColumnShown?: boolean; audioMessage?: ApiMessage; messagesCount?: number; + isComments?: boolean; isChatWithSelf?: boolean; lastSyncTime?: number; hasButtonInHeader?: boolean; @@ -116,6 +117,7 @@ const MiddleHeader: FC = ({ audioMessage, chat, messagesCount, + isComments, isChatWithSelf, lastSyncTime, hasButtonInHeader, @@ -340,7 +342,8 @@ const MiddleHeader: FC = ({ {renderBackButton()}

{messagesCount !== undefined ? ( - messageListType === 'thread' ? (lang('CommentsCount', messagesCount, 'i')) + messageListType === 'thread' ? ( + lang(isComments ? 'CommentsCount' : 'Replies', messagesCount, 'i')) : messageListType === 'pinned' ? (lang('PinnedMessagesCount', messagesCount, 'i')) : messageListType === 'scheduled' ? ( isChatWithSelf ? lang('Reminders') : lang('messages', messagesCount, 'i') @@ -422,13 +425,15 @@ const MiddleHeader: FC = ({ {renderInfo()} - + } + chatId={chatId} + /> + )} {shouldRenderPinnedMessage && renderingPinnedMessage && ( ( const pinnedMessageId = selectThreadTopMessageId(global, chatId, threadId); const message = pinnedMessageId ? selectChatMessage(global, chatId, pinnedMessageId) : undefined; const topMessageSender = message ? selectForwardedSender(global, message) : undefined; + const threadInfo = selectThreadInfo(global, chatId, threadId); return { ...state, pinnedMessageIds: pinnedMessageId, canUnpin: false, topMessageSender, + isComments: Boolean(threadInfo?.originChannelId), }; } diff --git a/src/components/middle/message/CommentButton.tsx b/src/components/middle/message/CommentButton.tsx index 5b395557c..e465c8ef8 100644 --- a/src/components/middle/message/CommentButton.tsx +++ b/src/components/middle/message/CommentButton.tsx @@ -25,16 +25,16 @@ const CommentButton: FC = ({ threadInfo, disabled, }) => { - const { openChat } = getActions(); + const { openComments } = getActions(); const lang = useLang(); const { - threadId, chatId, messagesCount, lastMessageId, lastReadInboxMessageId, recentReplierIds, + threadId, chatId, messagesCount, lastMessageId, lastReadInboxMessageId, recentReplierIds, originChannelId, } = threadInfo; const handleClick = useCallback(() => { - openChat({ id: chatId, threadId }); - }, [openChat, chatId, threadId]); + openComments({ id: chatId, threadId, originChannelId }); + }, [openComments, chatId, threadId, originChannelId]); const recentRepliers = useMemo(() => { if (!recentReplierIds?.length) { diff --git a/src/components/middle/message/ContextMenuContainer.tsx b/src/components/middle/message/ContextMenuContainer.tsx index b9ba2718e..3c69f9c94 100644 --- a/src/components/middle/message/ContextMenuContainer.tsx +++ b/src/components/middle/message/ContextMenuContainer.tsx @@ -6,7 +6,7 @@ import { getActions, getGlobal, withGlobal } from '../../../global'; import type { MessageListType } from '../../../global/types'; import type { - ApiAvailableReaction, ApiStickerSetInfo, ApiMessage, ApiStickerSet, ApiChatReactions, ApiReaction, + ApiAvailableReaction, ApiStickerSetInfo, ApiMessage, ApiStickerSet, ApiChatReactions, ApiReaction, ApiThreadInfo, } from '../../../api/types'; import type { IAlbum, IAnchorPosition } from '../../../types'; @@ -48,8 +48,10 @@ export type OwnProps = { album?: IAlbum; anchor: IAnchorPosition; messageListType: MessageListType; + noReplies?: boolean; onClose: () => void; onCloseAnimationEnd: () => void; + repliesThreadInfo?: ApiThreadInfo; }; type StateProps = { @@ -107,6 +109,7 @@ const ContextMenuContainer: FC = ({ canReschedule, canReply, canPin, + repliesThreadInfo, canUnpin, canDelete, canReport, @@ -129,11 +132,13 @@ const ContextMenuContainer: FC = ({ canRevote, canClosePoll, activeDownloads, + noReplies, canShowSeenBy, canScheduleUntilOnline, threadId, }) => { const { + openChat, setReplyingToId, setEditingId, pinMessage, @@ -254,6 +259,14 @@ const ContextMenuContainer: FC = ({ closeMenu(); }, [setReplyingToId, message.id, closeMenu]); + const handleOpenThread = useCallback(() => { + openChat({ + id: message.chatId, + threadId: message.id, + }); + closeMenu(); + }, [closeMenu, message.chatId, message.id, openChat]); + const handleEdit = useCallback(() => { setEditingId({ messageId: message.id }); closeMenu(); @@ -411,6 +424,7 @@ const ContextMenuContainer: FC = ({ canDelete={canDelete} canReport={canReport} canPin={canPin} + repliesThreadInfo={repliesThreadInfo} canUnpin={canUnpin} canEdit={canEdit} canForward={canForward} @@ -428,6 +442,8 @@ const ContextMenuContainer: FC = ({ customEmojiSets={customEmojiSets} isDownloading={isDownloading} seenByRecentUsers={seenByRecentUsers} + noReplies={noReplies} + onOpenThread={handleOpenThread} onReply={handleReply} onEdit={handleEdit} onPin={handlePin} diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx index aa404d5a9..c2b6540d5 100644 --- a/src/components/middle/message/Message.tsx +++ b/src/components/middle/message/Message.tsx @@ -170,6 +170,7 @@ type OwnProps = threadId: number; messageListType: MessageListType; noComments: boolean; + noReplies: boolean; appearanceOrder: number; memoFirstUnreadIdRef: { current: number | undefined }; } @@ -265,6 +266,7 @@ const Message: FC = ({ withAvatar, withSenderName, noComments, + noReplies, appearanceOrder, isFirstInGroup, isPremium, @@ -466,6 +468,7 @@ const Message: FC = ({ handleAudioPlay, handleAlbumMediaClick, handleMetaClick, + handleOpenThread, handleReadMedia, handleCancelUpload, handleVoteSend, @@ -523,7 +526,7 @@ const Message: FC = ({ message.hasUnreadMention && 'has-unread-mention', isSelected && 'is-selected', isInSelectMode && 'is-in-selection-mode', - isThreadTop && 'is-thread-top', + isThreadTop && !withAvatar && 'is-thread-top', Boolean(message.inlineButtons) && 'has-inline-buttons', isSwiped && 'is-swiped', transitionClassNames, @@ -546,7 +549,7 @@ const Message: FC = ({ isCustomShape, isLastInGroup, asForwarded, - hasThread, + hasThread: hasThread && !noComments, forceSenderName, hasComments: repliesThreadInfo && repliesThreadInfo.messagesCount > 0, hasActionButton: canForward || canFocus, @@ -708,11 +711,14 @@ const Message: FC = ({ const meta = ( ); @@ -1200,6 +1206,8 @@ const Message: FC = ({ messageListType={messageListType} onClose={handleContextMenuClose} onCloseAnimationEnd={handleContextMenuHide} + repliesThreadInfo={repliesThreadInfo} + noReplies={noReplies} /> )} diff --git a/src/components/middle/message/MessageContextMenu.tsx b/src/components/middle/message/MessageContextMenu.tsx index fe47f91a2..3c190ac36 100644 --- a/src/components/middle/message/MessageContextMenu.tsx +++ b/src/components/middle/message/MessageContextMenu.tsx @@ -5,7 +5,14 @@ import { getActions } from '../../../global'; import type { FC } from '../../../lib/teact/teact'; import type { - ApiAvailableReaction, ApiChatReactions, ApiMessage, ApiReaction, ApiSponsoredMessage, ApiStickerSet, ApiUser, + ApiAvailableReaction, + ApiChatReactions, + ApiMessage, + ApiReaction, + ApiSponsoredMessage, + ApiStickerSet, + ApiThreadInfo, + ApiUser, } from '../../../api/types'; import type { IAnchorPosition } from '../../../types'; @@ -39,6 +46,7 @@ type OwnProps = { maxUniqueReactions?: number; canReschedule?: boolean; canReply?: boolean; + repliesThreadInfo?: ApiThreadInfo; canPin?: boolean; canUnpin?: boolean; canDelete?: boolean; @@ -62,9 +70,11 @@ type OwnProps = { isDownloading?: boolean; canShowSeenBy?: boolean; seenByRecentUsers?: ApiUser[]; + noReplies?: boolean; hasCustomEmoji?: boolean; customEmojiSets?: ApiStickerSet[]; onReply?: () => void; + onOpenThread?: VoidFunction; onEdit?: () => void; onPin?: () => void; onUnpin?: () => void; @@ -110,6 +120,7 @@ const MessageContextMenu: FC = ({ canBuyPremium, canReply, canEdit, + noReplies, canPin, canUnpin, canDelete, @@ -125,6 +136,7 @@ const MessageContextMenu: FC = ({ canRevote, canClosePoll, isDownloading, + repliesThreadInfo, canShowSeenBy, canShowReactionsCount, canShowReactionList, @@ -132,6 +144,7 @@ const MessageContextMenu: FC = ({ hasCustomEmoji, customEmojiSets, onReply, + onOpenThread, onEdit, onPin, onUnpin, @@ -294,6 +307,11 @@ const MessageContextMenu: FC = ({ {lang('MessageScheduleEditTime')} )} {canReply && {lang('Reply')}} + {!noReplies && Boolean(repliesThreadInfo?.messagesCount) && ( + + {lang('Conversation.ContextViewReplies', repliesThreadInfo!.messagesCount, 'i')} + + )} {canEdit && {lang('Edit')}} {canFaveSticker && ( {lang('AddToFavorites')} diff --git a/src/components/middle/message/MessageMeta.scss b/src/components/middle/message/MessageMeta.scss index 53727a123..7259f3536 100644 --- a/src/components/middle/message/MessageMeta.scss +++ b/src/components/middle/message/MessageMeta.scss @@ -16,7 +16,8 @@ .message-time, .message-imported, .message-signature, - .message-views { + .message-views, + .message-replies { font-size: 0.75rem; white-space: nowrap; } @@ -55,6 +56,12 @@ top: -0.0625rem; } + .icon-reply-filled { + margin-left: 0.125rem; + margin-right: 0.375rem; + font-size: 0.75rem; + } + .has-solid-background & { color: rgba(var(--color-text-meta-rgb), 0.75); background: none; diff --git a/src/components/middle/message/MessageMeta.tsx b/src/components/middle/message/MessageMeta.tsx index dead41eae..c8cc003d3 100644 --- a/src/components/middle/message/MessageMeta.tsx +++ b/src/components/middle/message/MessageMeta.tsx @@ -2,7 +2,9 @@ import type { FC } from '../../../lib/teact/teact'; import React, { memo, useMemo } from '../../../lib/teact/teact'; import { getActions } from '../../../global'; -import type { ApiAvailableReaction, ApiMessage, ApiMessageOutgoingStatus } from '../../../api/types'; +import type { + ApiAvailableReaction, ApiMessage, ApiMessageOutgoingStatus, ApiThreadInfo, +} from '../../../api/types'; import { formatDateTimeToString, formatTime } from '../../../util/dateFormat'; import { formatIntegerCompact } from '../../../util/textFormat'; @@ -13,6 +15,7 @@ import useFlag from '../../../hooks/useFlag'; import buildClassName from '../../../util/buildClassName'; import MessageOutgoingStatus from '../../common/MessageOutgoingStatus'; +import AnimatedCounter from '../../common/AnimatedCounter'; import './MessageMeta.scss'; @@ -22,7 +25,10 @@ type OwnProps = { outgoingStatus?: ApiMessageOutgoingStatus; signature?: string; availableReactions?: ApiAvailableReaction[]; + noReplies?: boolean; + repliesThreadInfo?: ApiThreadInfo; onClick: (e: React.MouseEvent) => void; + onOpenThread: () => void; }; const MessageMeta: FC = ({ @@ -30,7 +36,10 @@ const MessageMeta: FC = ({ outgoingStatus, signature, withReactionOffset, + repliesThreadInfo, + noReplies, onClick, + onOpenThread, }) => { const { showNotification } = getActions(); const lang = useLang(); @@ -44,6 +53,11 @@ const MessageMeta: FC = ({ }); }; + function handleOpenThread(e: React.MouseEvent) { + e.stopPropagation(); + onOpenThread(); + } + const title = useMemo(() => { if (!isActivated) return undefined; const createDateTime = formatDateTimeToString(message.date * 1000, lang.code); @@ -84,6 +98,14 @@ const MessageMeta: FC = ({ )} + {!noReplies && Boolean(repliesThreadInfo?.messagesCount) && ( + + + + + + + )} {signature && ( {renderText(signature)} )} diff --git a/src/components/middle/message/hooks/useInnerHandlers.ts b/src/components/middle/message/hooks/useInnerHandlers.ts index 79c8185dd..a7f904050 100644 --- a/src/components/middle/message/hooks/useInnerHandlers.ts +++ b/src/components/middle/message/hooks/useInnerHandlers.ts @@ -160,6 +160,13 @@ export default function useInnerHandlers( selectMessage(e, groupedId); }, [selectMessage, groupedId]); + const handleOpenThread = useCallback(() => { + openChat({ + id: message.chatId, + threadId: message.id, + }); + }, [message.chatId, message.id, openChat]); + const handleTopicChipClick = useCallback(() => { if (!messageTopic) return; focusMessage({ @@ -178,6 +185,7 @@ export default function useInnerHandlers( handleAudioPlay, handleAlbumMediaClick, handleMetaClick: selectWithGroupedId, + handleOpenThread, handleReadMedia, handleCancelUpload, handleVoteSend, diff --git a/src/components/right/GifSearch.tsx b/src/components/right/GifSearch.tsx index 0efedb079..6d470485d 100644 --- a/src/components/right/GifSearch.tsx +++ b/src/components/right/GifSearch.tsx @@ -11,7 +11,7 @@ import { selectIsChatWithBot, selectCurrentMessageList, selectCanScheduleUntilOnline, - selectIsChatWithSelf, + selectIsChatWithSelf, selectThreadInfo, } from '../../global/selectors'; import { getAllowedAttachmentOptions, getCanPostInChat } from '../../global/helpers'; import buildClassName from '../../util/buildClassName'; @@ -155,7 +155,9 @@ export default memo(withGlobal( const chat = chatId ? selectChat(global, chatId) : undefined; const isChatWithBot = chat ? selectIsChatWithBot(global, chat) : undefined; const isSavedMessages = Boolean(chatId) && selectIsChatWithSelf(global, chatId); - const canPostInChat = Boolean(chat) && Boolean(threadId) && getCanPostInChat(chat, threadId); + const threadInfo = chatId && threadId ? selectThreadInfo(global, chatId, threadId) : undefined; + const isComments = Boolean(threadInfo?.originChannelId); + const canPostInChat = Boolean(chat) && Boolean(threadId) && getCanPostInChat(chat, threadId, isComments); return { query, diff --git a/src/components/right/management/ManageUser.tsx b/src/components/right/management/ManageUser.tsx index a5a76db72..d3bb561a4 100644 --- a/src/components/right/management/ManageUser.tsx +++ b/src/components/right/management/ManageUser.tsx @@ -276,9 +276,9 @@ const ManageUser: FC = ({ export default memo(withGlobal( (global, { userId }): StateProps => { const user = selectUser(global, userId); - const chat = selectChat(global, userId)!; + const chat = selectChat(global, userId); const { progress } = selectTabState(global).management; - const isMuted = selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global)); + const isMuted = chat && selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global)); return { user, progress, isMuted, diff --git a/src/global/actions/api/chats.ts b/src/global/actions/api/chats.ts index 51775d6b1..36c5a69b8 100644 --- a/src/global/actions/api/chats.ts +++ b/src/global/actions/api/chats.ts @@ -55,7 +55,7 @@ import { selectChatFolder, selectSupportChat, selectChatByUsername, selectCurrentMessageList, selectThreadInfo, selectCurrentChat, selectLastServiceNotification, selectVisibleUsers, selectUserByPhoneNumber, selectDraft, selectThreadTopMessageId, - selectTabState, selectThread, + selectTabState, selectThread, selectThreadOriginChat, } from '../../selectors'; import { buildCollectionByKey, omit } from '../../../util/iteratees'; import { debounce, pause, throttle } from '../../../util/schedulers'; @@ -142,10 +142,38 @@ addActionHandler('openChat', (global, actions, payload): ActionReturnType => { actions.requestChatUpdate({ chatId: id }); } + if (threadId !== MAIN_THREAD_ID) { + actions.requestThreadInfoUpdate({ chatId: id, threadId }); + } +}); + +addActionHandler('openComments', async (global, actions, payload): Promise => { + const { + id, threadId, originChannelId, tabId = getCurrentTabId(), + } = payload; + if (threadId !== MAIN_THREAD_ID) { const topMessageId = selectThreadTopMessageId(global, id, threadId); if (!topMessageId) { - actions.requestThreadInfoUpdate({ chatId: id, threadId }); + const chat = selectThreadOriginChat(global, id, threadId); + if (!chat) { + return; + } + + actions.openChat({ id: TMP_CHAT_ID, tabId }); + + const result = await callApi('requestThreadInfoUpdate', { chat, threadId, originChannelId }); + if (!result) { + actions.openPreviousChat({ tabId }); + return; + } + global = getGlobal(); + global = addUsers(global, buildCollectionByKey(result.users, 'id')); + setGlobal(global); + + actions.openChat({ id, threadId: result.topMessageId, tabId }); + } else { + actions.openChat({ id, threadId: topMessageId, tabId }); } } }); diff --git a/src/global/actions/api/messages.ts b/src/global/actions/api/messages.ts index 24b67c8d6..4165c3635 100644 --- a/src/global/actions/api/messages.ts +++ b/src/global/actions/api/messages.ts @@ -68,7 +68,6 @@ import { selectReplyingToId, selectEditingId, selectDraft, - selectThreadOriginChat, selectThreadTopMessageId, selectEditingScheduledId, selectEditingMessage, @@ -239,12 +238,17 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType => } const chat = selectChat(global, chatId)!; - const replyingToTopId = chat.isForum ? selectThreadTopMessageId(global, chatId, threadId) : undefined; + const replyingToId = selectReplyingToId(global, chatId, threadId); + const replyingToMessage = replyingToId ? selectChatMessage(global, chatId, replyingToId) : undefined; + + const replyingToTopId = chat.isForum + ? selectThreadTopMessageId(global, chatId, threadId) + : replyingToMessage?.replyToTopMessageId || replyingToMessage?.replyToMessageId; const params = { ...payload, chat, - replyingTo: selectReplyingToId(global, chatId, threadId), + replyingTo: replyingToId, replyingToTopId, noWebPage: selectNoWebPage(global, chatId, threadId), sendAs: selectSendAs(global, chatId), @@ -549,7 +553,7 @@ addActionHandler('markMessageListRead', (global, actions, payload): ActionReturn } const { chatId, threadId } = currentMessageList; - const chat = selectThreadOriginChat(global, chatId, threadId); + const chat = selectChat(global, chatId); if (!chat) { return undefined; } @@ -853,7 +857,7 @@ addActionHandler('rescheduleMessage', (global, actions, payload): ActionReturnTy addActionHandler('requestThreadInfoUpdate', async (global, actions, payload): Promise => { const { chatId, threadId } = payload; - const chat = selectThreadOriginChat(global, chatId, threadId); + const chat = selectChat(global, chatId); if (!chat) { return; } @@ -939,7 +943,7 @@ async function loadViewportMessages( global = getGlobal(); const result = await callApi('fetchMessages', { - chat: selectThreadOriginChat(global, chatId, threadId)!, + chat: selectChat(global, chatId)!, offsetId, addOffset, limit: MESSAGE_LIST_SLICE, diff --git a/src/global/actions/api/sync.ts b/src/global/actions/api/sync.ts index 2eee3ff48..752da5b48 100644 --- a/src/global/actions/api/sync.ts +++ b/src/global/actions/api/sync.ts @@ -105,8 +105,6 @@ async function loadAndReplaceMessages(global: T, actions: const activeThreadId = currentThreadId || MAIN_THREAD_ID; const threadInfo = currentThreadId && currentChatId ? selectThreadInfo(global, currentChatId, currentThreadId) : undefined; - // TODO Fix comments chat id, or refetch chat thread here - const activeCurrentChatId = threadInfo?.originChannelId || currentChatId; // Memoize drafts const draftChatIds = Object.keys(global.messages.byChatId); // eslint-disable-next-line @typescript-eslint/no-loop-func @@ -119,14 +117,14 @@ async function loadAndReplaceMessages(global: T, actions: return acc; }, {}); - const currentChat = activeCurrentChatId ? global.chats.byId[activeCurrentChatId] : undefined; - if (activeCurrentChatId && currentChat) { + const currentChat = currentChatId ? global.chats.byId[currentChatId] : undefined; + if (currentChatId && currentChat) { const result = await loadTopMessages(currentChat, activeThreadId, threadInfo?.lastReadInboxMessageId); global = getGlobal(); const { chatId: newCurrentChatId } = selectCurrentMessageList(global, tabId) || {}; if (result && newCurrentChatId === currentChatId) { - const currentChatMessages = selectChatMessages(global, activeCurrentChatId); + const currentChatMessages = selectChatMessages(global, currentChatId); const localMessages = currentChatId === SERVICE_NOTIFICATIONS_USER_ID ? global.serviceNotifications.filter(({ isDeleted }) => !isDeleted).map(({ message }) => message) : []; @@ -158,18 +156,20 @@ async function loadAndReplaceMessages(global: T, actions: wasReset = true; } - global = addChatMessagesById(global, activeCurrentChatId, byId); - global = updateListedIds(global, activeCurrentChatId, activeThreadId, listedIds); + global = addChatMessagesById(global, currentChatId, byId); + global = updateListedIds(global, currentChatId, activeThreadId, listedIds); // eslint-disable-next-line @typescript-eslint/no-loop-func Object.values(global.byTabId).forEach(({ id: otherTabId }) => { const { chatId: otherChatId, threadId: otherThreadId } = selectCurrentMessageList(global, otherTabId) || {}; - if (otherChatId === activeCurrentChatId && otherThreadId === activeThreadId) { - global = safeReplaceViewportIds(global, activeCurrentChatId, activeThreadId, listedIds, otherTabId); + if (otherChatId === currentChatId && otherThreadId === activeThreadId) { + global = safeReplaceViewportIds(global, currentChatId, activeThreadId, listedIds, otherTabId); } }); global = updateChats(global, buildCollectionByKey(result.chats, 'id')); global = updateUsers(global, buildCollectionByKey(result.users, 'id')); - global = updateThreadInfos(global, activeCurrentChatId, result.repliesThreadInfos); + if (result.repliesThreadInfos.length) { + global = updateThreadInfos(global, currentChatId, result.repliesThreadInfos); + } areMessagesLoaded = true; } @@ -184,10 +184,10 @@ async function loadAndReplaceMessages(global: T, actions: setGlobal(global); if (currentChat?.isForum) { - actions.loadTopics({ chatId: activeCurrentChatId!, force: true }); + actions.loadTopics({ chatId: currentChatId!, force: true }); if (currentThreadId && currentThreadId !== MAIN_THREAD_ID) { actions.loadTopicById({ - chatId: activeCurrentChatId!, topicId: currentThreadId, shouldCloseChatOnError: true, + chatId: currentChatId!, topicId: currentThreadId, shouldCloseChatOnError: true, }); } } diff --git a/src/global/actions/apiUpdaters/chats.ts b/src/global/actions/apiUpdaters/chats.ts index c16abdde4..d44a5b943 100644 --- a/src/global/actions/apiUpdaters/chats.ts +++ b/src/global/actions/apiUpdaters/chats.ts @@ -1,9 +1,10 @@ import { addActionHandler, getGlobal, setGlobal } from '../../index'; +import type { ApiUpdateChat } from '../../../api/types'; import { MAIN_THREAD_ID } from '../../../api/types'; import { ARCHIVED_FOLDER_ID, MAX_ACTIVE_PINNED_CHATS } from '../../../config'; -import { buildCollectionByKey, pick } from '../../../util/iteratees'; +import { buildCollectionByKey, omit, pick } from '../../../util/iteratees'; import { closeMessageNotifications, notifyAboutMessage } from '../../../util/notifications'; import { updateChat, @@ -28,7 +29,15 @@ const TYPING_STATUS_CLEAR_DELAY = 6000; // 6 seconds addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { switch (update['@type']) { case 'updateChat': { - const { isForum: prevIsForum } = selectChat(global, update.id) || {}; + const { isForum: prevIsForum, lastReadOutboxMessageId } = selectChat(global, update.id) || {}; + + if (update.chat.lastReadOutboxMessageId && lastReadOutboxMessageId + && update.chat.lastReadOutboxMessageId < lastReadOutboxMessageId) { + update = { + ...update, + chat: omit(update.chat, ['lastReadInboxMessageId']), + }; + } global = updateChat(global, update.id, update.chat, update.newProfilePhoto); setGlobal(global); @@ -47,8 +56,10 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { Object.values(global.byTabId).forEach(({ id: tabId }) => { const { chatId: currentChatId } = selectCurrentMessageList(global, tabId) || {}; + const chatUpdate = update as ApiUpdateChat; // The property `isForum` was changed in another client - if (currentChatId === update.id && 'isForum' in update.chat && prevIsForum !== update.chat.isForum) { + if (currentChatId === chatUpdate.id + && 'isForum' in chatUpdate.chat && prevIsForum !== chatUpdate.chat.isForum) { if (prevIsForum) { actions.closeForumPanel({ tabId }); } diff --git a/src/global/actions/apiUpdaters/messages.ts b/src/global/actions/apiUpdaters/messages.ts index b940dc36a..eecff8ce2 100644 --- a/src/global/actions/apiUpdaters/messages.ts +++ b/src/global/actions/apiUpdaters/messages.ts @@ -108,7 +108,7 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { } const { threadInfo } = selectThreadByMessage(global, message as ApiMessage) || {}; - if (threadInfo) { + if (threadInfo && !isLocal) { actions.requestThreadInfoUpdate({ chatId, threadId: threadInfo.threadId }); } @@ -362,6 +362,17 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { actions.loadTopicById({ chatId, topicId: threadId }); } + // Update reply thread last read message id if already read in main thread + if (threadInfo.topMessageId === threadId && !chat?.isForum) { + const lastReadInboxMessageId = chat?.lastReadInboxMessageId; + const lastReadInboxMessageIdInThread = newThreadInfo.lastReadInboxMessageId || lastReadInboxMessageId; + if (lastReadInboxMessageId && lastReadInboxMessageIdInThread) { + global = updateThreadInfo(global, chatId, threadId, { + lastReadInboxMessageId: Math.max(lastReadInboxMessageIdInThread, lastReadInboxMessageId), + }); + } + } + setGlobal(global); break; @@ -758,8 +769,13 @@ function updateListedAndViewportIds( global = replaceThreadParam(global, chatId, threadInfo.threadId, 'threadInfo', { ...threadInfo, lastMessageId: message.id, - messagesCount: (threadInfo.messagesCount || 0) + 1, }); + + if (!isMessageLocal(message)) { + global = updateThreadInfo(global, chatId, threadInfo.threadId, { + messagesCount: (threadInfo.messagesCount || 0) + 1, + }); + } } if (isUnreadChatNotLoaded) { diff --git a/src/global/cache.ts b/src/global/cache.ts index 7025cf7b9..db29b384c 100644 --- a/src/global/cache.ts +++ b/src/global/cache.ts @@ -3,7 +3,7 @@ import { addCallback, removeCallback } from '../lib/teact/teactn'; import { addActionHandler, getGlobal } from './index'; -import type { ActionReturnType, GlobalState } from './types'; +import type { ActionReturnType, GlobalState, MessageList } from './types'; import type { ApiChat, ApiUser } from '../api/types'; import { MAIN_THREAD_ID } from '../api/types'; @@ -27,7 +27,7 @@ import { } from '../util/iteratees'; import { selectChat, - selectCurrentMessageList, + selectCurrentMessageList, selectThreadOriginChat, selectVisibleUsers, } from './selectors'; import { hasStoredSession } from '../util/sessions'; @@ -459,8 +459,19 @@ function reduceChats(global: T): GlobalState['chats'] { const { chats: { byId }, currentUserId } = global; const currentChatIds = compact( Object.values(global.byTabId) - .map(({ id: tabId }) => selectCurrentMessageList(global, tabId)), - ).map(({ chatId }) => chatId).filter((chatId) => isUserId(chatId)); + .flatMap(({ id: tabId }): MessageList[] | undefined => { + const messageList = selectCurrentMessageList(global, tabId); + if (!messageList) return undefined; + + const { chatId, threadId } = messageList; + const origin = selectThreadOriginChat(global, chatId, threadId); + return origin ? [{ + chatId: origin.id, + threadId: MAIN_THREAD_ID, + type: 'thread', + }, messageList] : [messageList]; + }), + ).map(({ chatId }) => chatId); const idsToSave = unique([ ...currentUserId ? [currentUserId] : [], diff --git a/src/global/helpers/chats.ts b/src/global/helpers/chats.ts index ca5982b63..dc55a7b8f 100644 --- a/src/global/helpers/chats.ts +++ b/src/global/helpers/chats.ts @@ -177,7 +177,7 @@ export function isUserRightBanned(chat: ApiChat, key: keyof ApiChatBannedRights) ); } -export function getCanPostInChat(chat: ApiChat, threadId: number) { +export function getCanPostInChat(chat: ApiChat, threadId: number, isComments?: boolean) { if (threadId !== MAIN_THREAD_ID) { if (chat.isForum) { if (chat.isNotJoined) { @@ -189,10 +189,10 @@ export function getCanPostInChat(chat: ApiChat, threadId: number) { return false; } } - return true; // TODO[forums] legacy value, check that again } - if (chat.isRestricted || chat.isForbidden || chat.migratedTo || chat.isNotJoined || isChatWithRepliesBot(chat.id)) { + if (chat.isRestricted || chat.isForbidden || chat.migratedTo + || (!isComments && chat.isNotJoined) || isChatWithRepliesBot(chat.id)) { return false; } diff --git a/src/global/init.ts b/src/global/init.ts index 94621cef1..4c3b2e33a 100644 --- a/src/global/init.ts +++ b/src/global/init.ts @@ -10,7 +10,6 @@ import { cloneDeep } from '../util/iteratees'; import { replaceTabThreadParam, replaceThreadParam, updatePasscodeSettings } from './reducers'; import { clearStoredSession } from '../util/sessions'; import { parseLocationHash } from '../util/routing'; -import { MAIN_THREAD_ID } from '../api/types'; import { selectTabState, selectThreadParam } from './selectors'; import { Bundles, loadBundle } from '../util/moduleLoader'; import { getCurrentTabId, reestablishMasterToSelf } from '../util/establishMultitabRole'; @@ -68,20 +67,24 @@ addActionHandler('init', (global, actions, payload): ActionReturnType => { } Object.keys(global.messages.byChatId).forEach((chatId) => { - const lastViewportIds = selectThreadParam(global, chatId, MAIN_THREAD_ID, 'lastViewportIds'); - // Check if migration from previous version is faulty - if (!lastViewportIds?.every((id) => isLocalMessageId(id) || global.messages.byChatId[chatId]?.byId[id])) { - global = replaceThreadParam(global, chatId, MAIN_THREAD_ID, 'lastViewportIds', undefined); - return; - } - global = replaceTabThreadParam( - global, - chatId, - MAIN_THREAD_ID, - 'viewportIds', - lastViewportIds, - tabId, - ); + const threadsById = global.messages.byChatId[chatId].threadsById; + Object.keys(threadsById).forEach((thread) => { + const threadId = Number(thread); + const lastViewportIds = selectThreadParam(global, chatId, threadId, 'lastViewportIds'); + // Check if migration from previous version is faulty + if (!lastViewportIds?.every((id) => isLocalMessageId(id) || global.messages.byChatId[chatId]?.byId[id])) { + global = replaceThreadParam(global, chatId, threadId, 'lastViewportIds', undefined); + return; + } + global = replaceTabThreadParam( + global, + chatId, + threadId, + 'viewportIds', + lastViewportIds, + tabId, + ); + }); }); // Temporary state fix diff --git a/src/global/reducers/messages.ts b/src/global/reducers/messages.ts index ff39697ff..b9be302be 100644 --- a/src/global/reducers/messages.ts +++ b/src/global/reducers/messages.ts @@ -30,6 +30,7 @@ import { } from '../../util/iteratees'; import { updateTabState } from './tabs'; import { getCurrentTabId } from '../../util/establishMultitabRole'; +import { isLocalMessageId } from '../helpers'; type MessageStoreSections = { byId: Record; @@ -254,7 +255,7 @@ export function deleteChatMessages( messageIds.forEach((messageId) => { if (listedIds?.includes(messageId)) { listedIds = listedIds.filter((id) => id !== messageId); - if (newMessageCount !== undefined) newMessageCount -= 1; + if (newMessageCount !== undefined && !isLocalMessageId(messageId)) newMessageCount -= 1; } if (pinnedIds?.includes(messageId)) { diff --git a/src/global/selectors/messages.ts b/src/global/selectors/messages.ts index a6b66a41d..55a9497f5 100644 --- a/src/global/selectors/messages.ts +++ b/src/global/selectors/messages.ts @@ -482,8 +482,14 @@ export function selectThreadIdFromMessage(global: T, mess return message.id; } - // TODO ignore only basic group if reply threads are added - if (!chat?.isForum) return MAIN_THREAD_ID; + if (!chat?.isForum) { + if (chat && isChatBasicGroup(chat)) return MAIN_THREAD_ID; + + if (chat && isChatSuperGroup(chat)) { + return replyToTopMessageId || replyToMessageId || MAIN_THREAD_ID; + } + return MAIN_THREAD_ID; + } if (!isTopicReply) return GENERAL_TOPIC_ID; return replyToTopMessageId || replyToMessageId || GENERAL_TOPIC_ID; } @@ -531,7 +537,10 @@ export function selectAllowedMessageActions(global: T, me && !chat.isForbidden ); - const canReply = !isLocal && !isServiceNotification && !chat.isForbidden && getCanPostInChat(chat, threadId) + const threadInfo = selectThreadInfo(global, message.chatId, threadId); + const isComments = Boolean(threadInfo?.originChannelId); + const canReply = !isLocal && !isServiceNotification && !chat.isForbidden + && getCanPostInChat(chat, threadId, isComments) && (!messageTopic || !messageTopic.isClosed || messageTopic.isOwner || getHasAdminRight(chat, 'manageTopics')); const hasPinPermission = isPrivate || ( diff --git a/src/global/types.ts b/src/global/types.ts index cbb45f05b..6b3e8a428 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -1572,6 +1572,11 @@ export interface ActionPayloads { shouldReplaceHistory?: boolean; noForumTopicPanel?: boolean; } & WithTabId; + openComments: { + id: string; + threadId: number; + originChannelId?: string; + } & WithTabId; loadFullChat: { chatId: string; force?: boolean; diff --git a/src/styles/Telegram T.json b/src/styles/Telegram T.json index 54afe7f3a..96bd27303 100644 --- a/src/styles/Telegram T.json +++ b/src/styles/Telegram T.json @@ -2,7 +2,7 @@ "metadata": { "name": "Telegram T", "lastOpened": 0, - "created": 1674067291539 + "created": 1675856733901 }, "iconSets": [ { @@ -157,13 +157,21 @@ }, { "selection": [ + { + "order": 754, + "id": 85, + "name": "replies", + "prevSize": 32, + "code": 59833, + "tempChar": "" + }, { "order": 746, "id": 84, "name": "forums", "prevSize": 32, "code": 59828, - "tempChar": "" + "tempChar": "" }, { "order": 743, @@ -171,7 +179,7 @@ "name": "hashtag", "prevSize": 32, "code": 59825, - "tempChar": "" + "tempChar": "" }, { "order": 744, @@ -179,7 +187,7 @@ "name": "reopen-topic", "prevSize": 32, "code": 59826, - "tempChar": "" + "tempChar": "" }, { "order": 745, @@ -187,7 +195,7 @@ "name": "close-topic", "prevSize": 32, "code": 59827, - "tempChar": "" + "tempChar": "" }, { "order": 739, @@ -195,7 +203,7 @@ "name": "open-in-new-tab", "prevSize": 32, "code": 59823, - "tempChar": "" + "tempChar": "" }, { "order": 738, @@ -203,7 +211,7 @@ "name": "pip", "prevSize": 32, "code": 59822, - "tempChar": "" + "tempChar": "" }, { "order": 737, @@ -211,7 +219,7 @@ "name": "gift", "prevSize": 32, "code": 59821, - "tempChar": "" + "tempChar": "" }, { "order": 734, @@ -219,7 +227,7 @@ "name": "sort", "prevSize": 32, "code": 59820, - "tempChar": "" + "tempChar": "" }, { "order": 732, @@ -227,7 +235,7 @@ "name": "web", "prevSize": 32, "code": 59819, - "tempChar": "" + "tempChar": "" }, { "order": 731, @@ -235,7 +243,7 @@ "name": "transcribe", "prevSize": 32, "code": 59818, - "tempChar": "" + "tempChar": "" }, { "order": 719, @@ -243,7 +251,7 @@ "name": "add-one-badge", "prevSize": 32, "code": 59803, - "tempChar": "" + "tempChar": "" }, { "order": 720, @@ -251,7 +259,7 @@ "name": "chat-badge", "prevSize": 32, "code": 59808, - "tempChar": "" + "tempChar": "" }, { "order": 721, @@ -259,7 +267,7 @@ "name": "chats-badge", "prevSize": 32, "code": 59809, - "tempChar": "" + "tempChar": "" }, { "order": 722, @@ -267,7 +275,7 @@ "name": "double-badge", "prevSize": 32, "code": 59810, - "tempChar": "" + "tempChar": "" }, { "order": 723, @@ -275,7 +283,7 @@ "name": "file-badge", "prevSize": 32, "code": 59811, - "tempChar": "" + "tempChar": "" }, { "order": 724, @@ -283,7 +291,7 @@ "name": "folder-badge", "prevSize": 32, "code": 59812, - "tempChar": "" + "tempChar": "" }, { "order": 726, @@ -291,7 +299,7 @@ "name": "link-badge", "prevSize": 32, "code": 59813, - "tempChar": "" + "tempChar": "" }, { "order": 725, @@ -299,7 +307,7 @@ "name": "pin-badge", "prevSize": 32, "code": 59814, - "tempChar": "" + "tempChar": "" }, { "order": 727, @@ -307,7 +315,7 @@ "name": "premium", "prevSize": 32, "code": 59815, - "tempChar": "" + "tempChar": "" }, { "order": 728, @@ -315,7 +323,7 @@ "name": "unlock-badge", "prevSize": 32, "code": 59816, - "tempChar": "" + "tempChar": "" }, { "order": 729, @@ -323,7 +331,7 @@ "name": "lock-badge", "prevSize": 32, "code": 59817, - "tempChar": "" + "tempChar": "" }, { "order": 715, @@ -331,7 +339,7 @@ "name": "key", "prevSize": 32, "code": 59802, - "tempChar": "" + "tempChar": "" }, { "order": 714, @@ -339,7 +347,7 @@ "name": "heart-outline", "prevSize": 32, "code": 59806, - "tempChar": "" + "tempChar": "" }, { "order": 713, @@ -347,7 +355,7 @@ "name": "heart", "prevSize": 32, "code": 59807, - "tempChar": "" + "tempChar": "" }, { "order": 712, @@ -355,7 +363,7 @@ "name": "word-wrap", "prevSize": 32, "code": 59805, - "tempChar": "" + "tempChar": "" }, { "order": 708, @@ -363,7 +371,7 @@ "name": "webapp", "prevSize": 32, "code": 59795, - "tempChar": "" + "tempChar": "" }, { "order": 707, @@ -371,7 +379,7 @@ "name": "reload", "prevSize": 32, "code": 59796, - "tempChar": "" + "tempChar": "" }, { "order": 706, @@ -379,7 +387,7 @@ "name": "install", "prevSize": 32, "code": 59801, - "tempChar": "" + "tempChar": "" }, { "order": 705, @@ -387,7 +395,7 @@ "name": "favorite-filled", "prevSize": 32, "code": 59800, - "tempChar": "" + "tempChar": "" }, { "order": 702, @@ -395,7 +403,7 @@ "name": "share-screen", "prevSize": 32, "code": 59770, - "tempChar": "" + "tempChar": "" }, { "order": 701, @@ -403,7 +411,7 @@ "name": "video-outlined", "prevSize": 32, "code": 59799, - "tempChar": "" + "tempChar": "" }, { "order": 700, @@ -411,7 +419,7 @@ "name": "stats", "prevSize": 32, "code": 59798, - "tempChar": "" + "tempChar": "" }, { "order": 699, @@ -419,7 +427,7 @@ "name": "copy-media", "prevSize": 32, "code": 59797, - "tempChar": "" + "tempChar": "" }, { "order": 704, @@ -427,7 +435,7 @@ "name": "sidebar", "prevSize": 32, "code": 59794, - "tempChar": "" + "tempChar": "" }, { "order": 690, @@ -435,7 +443,7 @@ "name": "video-stop", "prevSize": 32, "code": 59787, - "tempChar": "" + "tempChar": "" }, { "order": 678, @@ -443,7 +451,7 @@ "name": "speaker", "prevSize": 32, "code": 59777, - "tempChar": "" + "tempChar": "" }, { "order": 679, @@ -451,7 +459,7 @@ "name": "speaker-outline", "prevSize": 32, "code": 59778, - "tempChar": "" + "tempChar": "" }, { "order": 680, @@ -459,7 +467,7 @@ "name": "phone-discard-outline", "prevSize": 32, "code": 59779, - "tempChar": "" + "tempChar": "" }, { "order": 681, @@ -467,7 +475,7 @@ "name": "allow-speak", "prevSize": 32, "code": 59780, - "tempChar": "" + "tempChar": "" }, { "order": 682, @@ -475,7 +483,7 @@ "name": "stop-raising-hand", "prevSize": 32, "code": 59781, - "tempChar": "" + "tempChar": "" }, { "order": 683, @@ -483,7 +491,7 @@ "name": "share-screen-outlined", "prevSize": 32, "code": 59782, - "tempChar": "" + "tempChar": "" }, { "order": 684, @@ -491,7 +499,7 @@ "name": "voice-chat", "prevSize": 32, "code": 59783, - "tempChar": "" + "tempChar": "" }, { "order": 689, @@ -499,7 +507,7 @@ "name": "video", "prevSize": 32, "code": 59784, - "tempChar": "" + "tempChar": "" }, { "order": 686, @@ -507,7 +515,7 @@ "name": "noise-suppression", "prevSize": 32, "code": 59785, - "tempChar": "" + "tempChar": "" }, { "order": 703, @@ -515,7 +523,7 @@ "name": "phone-discard", "prevSize": 32, "code": 59786, - "tempChar": "" + "tempChar": "" }, { "order": 667, @@ -523,7 +531,7 @@ "name": "bot-commands-filled", "prevSize": 32, "code": 59775, - "tempChar": "" + "tempChar": "" }, { "order": 664, @@ -531,7 +539,7 @@ "name": "reply-filled", "prevSize": 32, "code": 59776, - "tempChar": "" + "tempChar": "" }, { "order": 656, @@ -539,7 +547,7 @@ "name": "bug", "prevSize": 32, "code": 59774, - "tempChar": "" + "tempChar": "" }, { "order": 619, @@ -547,7 +555,7 @@ "name": "data", "prevSize": 32, "code": 59773, - "tempChar": "" + "tempChar": "" }, { "order": 622, @@ -555,7 +563,7 @@ "name": "darkmode", "prevSize": 32, "code": 59769, - "tempChar": "" + "tempChar": "" }, { "order": 711, @@ -563,7 +571,7 @@ "name": "animations", "prevSize": 32, "code": 59804, - "tempChar": "" + "tempChar": "" }, { "order": 626, @@ -571,7 +579,7 @@ "name": "enter", "prevSize": 32, "code": 59771, - "tempChar": "" + "tempChar": "" }, { "order": 627, @@ -579,7 +587,7 @@ "name": "fontsize", "prevSize": 32, "code": 59772, - "tempChar": "" + "tempChar": "" }, { "order": 630, @@ -587,7 +595,7 @@ "name": "permissions", "prevSize": 32, "code": 59766, - "tempChar": "" + "tempChar": "" }, { "order": 631, @@ -595,7 +603,7 @@ "name": "card", "prevSize": 32, "code": 59767, - "tempChar": "" + "tempChar": "" }, { "order": 634, @@ -603,7 +611,7 @@ "name": "truck", "prevSize": 32, "code": 59768, - "tempChar": "" + "tempChar": "" }, { "order": 663, @@ -611,7 +619,7 @@ "name": "share-filled", "prevSize": 32, "code": 59738, - "tempChar": "" + "tempChar": "" }, { "order": 638, @@ -619,7 +627,7 @@ "name": "bold", "prevSize": 32, "code": 59745, - "tempChar": "" + "tempChar": "" }, { "order": 639, @@ -627,7 +635,7 @@ "name": "bot-command", "prevSize": 32, "code": 59746, - "tempChar": "" + "tempChar": "" }, { "order": 642, @@ -635,7 +643,7 @@ "name": "calendar-filter", "prevSize": 32, "code": 59747, - "tempChar": "" + "tempChar": "" }, { "order": 643, @@ -643,7 +651,7 @@ "name": "comments", "prevSize": 32, "code": 59748, - "tempChar": "" + "tempChar": "" }, { "order": 645, @@ -651,7 +659,7 @@ "name": "comments-sticker", "prevSize": 32, "code": 59749, - "tempChar": "" + "tempChar": "" }, { "order": 646, @@ -659,7 +667,7 @@ "name": "arrow-down", "prevSize": 32, "code": 59750, - "tempChar": "" + "tempChar": "" }, { "order": 668, @@ -667,7 +675,7 @@ "name": "email", "prevSize": 32, "code": 59751, - "tempChar": "" + "tempChar": "" }, { "order": 648, @@ -675,7 +683,7 @@ "name": "italic", "prevSize": 32, "code": 59752, - "tempChar": "" + "tempChar": "" }, { "order": 620, @@ -683,7 +691,7 @@ "name": "link", "prevSize": 32, "code": 59753, - "tempChar": "" + "tempChar": "" }, { "order": 742, @@ -691,7 +699,7 @@ "name": "link-broken", "prevSize": 32, "code": 59824, - "tempChar": "" + "tempChar": "" }, { "order": 621, @@ -699,7 +707,7 @@ "name": "mention", "prevSize": 32, "code": 59754, - "tempChar": "" + "tempChar": "" }, { "order": 624, @@ -707,7 +715,7 @@ "name": "monospace", "prevSize": 32, "code": 59755, - "tempChar": "" + "tempChar": "" }, { "order": 625, @@ -715,7 +723,7 @@ "name": "next", "prevSize": 32, "code": 59756, - "tempChar": "" + "tempChar": "" }, { "order": 628, @@ -723,7 +731,7 @@ "name": "password-off", "prevSize": 32, "code": 59757, - "tempChar": "" + "tempChar": "" }, { "order": 629, @@ -731,7 +739,7 @@ "name": "pin-list", "prevSize": 32, "code": 59758, - "tempChar": "" + "tempChar": "" }, { "order": 632, @@ -739,7 +747,7 @@ "name": "previous", "prevSize": 32, "code": 59759, - "tempChar": "" + "tempChar": "" }, { "order": 633, @@ -747,7 +755,7 @@ "name": "replace", "prevSize": 32, "code": 59760, - "tempChar": "" + "tempChar": "" }, { "order": 636, @@ -755,7 +763,7 @@ "name": "schedule", "prevSize": 32, "code": 59761, - "tempChar": "" + "tempChar": "" }, { "order": 691, @@ -763,7 +771,7 @@ "name": "strikethrough", "prevSize": 32, "code": 59762, - "tempChar": "" + "tempChar": "" }, { "order": 692, @@ -771,7 +779,7 @@ "name": "underlined", "prevSize": 32, "code": 59763, - "tempChar": "" + "tempChar": "" }, { "order": 641, @@ -779,7 +787,7 @@ "name": "zoom-in", "prevSize": 32, "code": 59764, - "tempChar": "" + "tempChar": "" }, { "order": 649, @@ -787,20 +795,37 @@ "name": "zoom-out", "prevSize": 32, "code": 59765, - "tempChar": "" + "tempChar": "" } ], "id": 2, "metadata": { "name": "Untitled Set", "importSize": { - "width": 24, - "height": 24 + "width": 768, + "height": 768 } }, "height": 1024, "prevSize": 32, "icons": [ + { + "id": 85, + "paths": [ + "M563.2 576c-0.933 0-2 0-2.933 0-0.533 0-1.067 0-1.6-0.133l-4.267-0.4c-0.667 0-1.333-0.133-2-0.133-5.2-0.667-9.733-1.733-14.267-3.6-4.667-1.867-9.2-4.4-14.4-8-0.4-0.267-0.8-0.533-1.2-0.933l-5.067-3.733c-0.4-0.267-0.667-0.533-1.067-0.8l-1.2-0.933c-0.4-0.4-0.933-0.8-1.467-1.2-0.133-0.133-0.4-0.267-0.533-0.533l-6.533-5.6c-0.267-0.267-0.533-0.533-0.8-0.667l-12.267-10.933c-0.133-0.133-0.267-0.267-0.4-0.4l-178.8-165.333c-0.133-0.133-0.4-0.267-0.533-0.533l-9.2-8.8c-0.267-0.267-0.533-0.4-0.667-0.667-0.8-0.8-1.6-1.6-2.4-2.4-0.667-0.667-1.2-1.333-1.733-1.867-0.4-0.533-0.933-1.067-1.333-1.6l-2.533-3.067c-0.533-0.667-1.2-1.467-1.733-2.133-0.933-1.333-1.733-2.533-2.667-3.867-1.067-1.6-2-3.333-3.067-5.2-0.8-1.467-1.6-3.067-2.267-4.667s-1.333-3.2-1.867-4.8c-2.667-7.733-4-15.733-4-23.867s1.333-16.133 4-23.867c0.533-1.6 1.2-3.2 1.867-4.8s1.467-3.2 2.267-4.667c1.067-2 2-3.733 3.067-5.2 0.933-1.467 1.733-2.667 2.667-4 0.533-0.8 1.067-1.467 1.733-2.267l2.533-3.067c0.4-0.533 0.8-0.933 1.333-1.467 0.533-0.667 1.2-1.333 1.867-2 0.8-0.8 1.6-1.6 2.4-2.4 0.133-0.133 0.4-0.4 0.533-0.533l5.6-5.333c0.267-0.267 0.4-0.4 0.667-0.667l182.267-168.533c0.133-0.133 0.267-0.267 0.4-0.4l12.4-11.067c0.133-0.133 0.267-0.267 0.4-0.4 0.533-0.533 1.2-1.067 1.733-1.6l1.6-1.333c0.133-0.133 0.4-0.267 0.533-0.533l5.867-4.8c0.533-0.4 0.933-0.8 1.467-1.2l4.8-3.6c0.4-0.267 0.8-0.667 1.2-0.933 5.2-3.6 9.733-6.133 14.4-8 4.533-1.733 9.067-2.933 14.267-3.6 0.667-0.133 1.333-0.133 2-0.267l4.267-0.267c0.533 0 1.067-0.133 1.6-0.133 0.933 0 2 0 2.933 0 10.267 0 20.133 2.133 29.467 6.133 10.133 4.4 19.333 11.2 26.533 19.733 0.4 0.4 0.667 0.8 0.933 1.2l2.533 3.2c0.4 0.533 0.8 0.933 1.067 1.467 2.933 4.133 5.2 8 6.933 12.267 1.733 4.4 3.067 9.2 4 14.933 0.133 0.533 0.133 1.067 0.267 1.6l0.667 5.6c0 0.4 0.133 0.8 0.133 1.333l0.133 1.333c0 0.667 0.133 1.2 0.133 1.733 0 0.267 0 0.533 0 0.8l0.4 7.6c0 0.4 0 0.8 0 1.2l0.267 14.533c0 0.267 0 0.533 0 0.8v11.6c0 0.667 0 1.2 0 1.867v13.333c76.133 7.467 139.333 35.467 188.667 83.467 49.467 48.133 83.467 115.867 100.933 201.333 1.067 2.667 1.867 5.333 2.4 8.267l5.067 28.267c0 0.267 0.133 0.533 0.133 0.933l1.733 11.067c0.133 0.533 0.133 0.933 0.267 1.467l0.667 4.933c0.133 0.667 0.133 1.333 0.267 2.133l0.267 3.733c0 0.667 0.133 1.467 0.133 2.133 0 0.533 0 1.067 0 1.467 0 6.933-1.733 13.733-4.8 19.733l-0.133 0.267c-0.4 0.8-0.8 1.6-1.333 2.267l-2.4 3.867c-0.133 0.133-0.267 0.4-0.4 0.533-5.2 8.4-12.133 15.067-20.267 19.733-8.267 4.8-17.6 7.467-27.6 7.733l-3.067 0.133c-0.4 0-0.8 0-1.333 0h-1.467c-0.133 0-0.133 0-0.267 0-7.333 0-14.667-1.867-21.067-5.6l-0.4-0.267c-0.933-0.533-1.733-1.067-2.533-1.6l-2.4-1.6c-0.533-0.4-1.2-0.8-1.733-1.2l-3.6-2.667c-0.4-0.267-0.8-0.667-1.2-0.933l-7.867-6.267c-0.267-0.267-0.533-0.533-0.933-0.667l-30.667-25.6c-0.933-0.667-1.733-1.467-2.533-2.267-24.4-19.467-52.267-34.667-82.933-45.2-24.4-8.4-51.067-14-79.333-16.8v10.533c0 0.133 0 0.267 0 0.4v9.867c0 0.133 0 0.133 0 0.267v0.533c0 0.533 0 0.933 0 1.467v7.6c0 0.4 0 0.667 0 1.067l-0.4 10.267c0 0.267 0 0.533 0 0.8-0.533 11.333-1.733 19.2-3.733 26.133-2.133 7.2-5.333 13.6-10.133 19.867-0.267 0.267-0.4 0.533-0.667 0.933l-1.467 1.867c-0.267 0.267-0.533 0.667-0.8 0.933-7.2 8.533-16.4 15.333-26.667 19.867-9.6 4.133-19.467 6.267-29.733 6.267zM550.933 474.933l0.4 0.4v-61.467c0-11.6 4.533-22.667 12.8-30.8 8.133-8.133 19.067-12.8 30.8-12.8 53.733 0 103.867 7.867 148.933 23.333 33.6 11.6 64.667 27.467 92.667 47.467-14.8-56.133-38.667-100.533-70.933-131.867-39.867-38.8-94.267-59.333-161.6-60.933l-9.467-0.133c-11.467-0.133-22.267-4.667-30.4-12.667-8.267-8.267-12.8-19.2-12.8-30.933v-61.467l-0.4 0.4-179.467 165.733 1.067 1.067 178.4 164.667zM636.8 413.867v0c0 0 0 0 0 0z", + "M272.933 980c-28.667 0-43.467-4.8-52.133-8.4-25.733-10.8-44.267-32-50.8-58.4-4.8-19.333-5.067-49.067 20.667-80.4 0.133-0.267 0.4-0.4 0.533-0.667l0.933-1.2c0.133-0.267 0.4-0.4 0.533-0.667l7.467-8.4c0.133-0.133 0.267-0.267 0.4-0.4l6-6.533 5.733-6.533c1.333-1.6 2.667-3.2 3.733-4.667-36.4-31.333-65.733-67.333-87.2-107.333-28-52.133-42.267-109.6-42.267-170.8 0-53.733 11.6-105.867 34.4-154.8 10-21.333 35.333-30.533 56.667-20.667 21.333 10 30.533 35.333 20.667 56.667-17.6 37.6-26.4 77.6-26.4 118.8 0 47.067 10.8 90.933 32 130.4 19.6 36.533 48 69.067 84.4 96.667 8.133 6.133 13.733 14.933 15.867 24.8 1.867 8.4 6.4 38-12.667 67.067-4.133 6.4-8.933 12.667-14.4 19.2-0.133 0.267-0.4 0.4-0.533 0.667l-6.4 7.2c-0.267 0.267-0.4 0.533-0.667 0.8l-6.133 6.667-7.467 8.4c-0.933 1.2-2.8 3.467-3.333 5.067 0.133 0.133 0.4 0.267 0.667 0.4l0.4 0.133c0 0 5.067 1.733 18.8 1.733 10 0 44.533-1.2 72.133-16.667 10.933-6.133 20.933-12.667 30.533-19.867l0.267-0.267 9.6-7.6 8-6.8 9.333-8.533c9.867-9.067 23.467-12.933 36.667-10.4 22.8 4.4 46.267 6.533 69.733 6.533 90.133 0 174.933-31.867 238.667-89.6 30.933-28 55.2-60.667 72.133-97.067 10-21.333 35.333-30.533 56.667-20.667 21.333 10 30.533 35.333 20.667 56.667-21.733 46.667-52.8 88.533-92.133 124.267-79.467 72.133-184.533 111.733-296 111.733-21.6 0-43.2-1.467-64.533-4.533l-4 3.333c-0.267 0.133-0.4 0.4-0.667 0.533l-1.2 0.933c-0.267 0.133-0.4 0.4-0.667 0.533l-9.6 7.6c-0.133 0.133-0.267 0.133-0.4 0.267l-0.667 0.533c-0.133 0.133-0.267 0.267-0.4 0.267-12.8 9.733-26.133 18.267-40.533 26.4-43.6 24.933-93.6 27.6-113.067 27.6zM220.933 794.933c0 0 0 0 0 0s0 0 0 0z" + ], + "attrs": [ + {}, + {} + ], + "isMulticolor": false, + "isMulticolor2": false, + "grid": 24, + "tags": [ + "replies" + ] + }, { "id": 84, "paths": [ @@ -3680,7 +3705,7 @@ "name": "spoiler-disable", "prevSize": 32, "code": 59829, - "tempChar": "" + "tempChar": "" }, { "order": 752, @@ -3688,7 +3713,7 @@ "name": "grouped", "prevSize": 32, "code": 59830, - "tempChar": "" + "tempChar": "" }, { "order": 751, @@ -3696,7 +3721,7 @@ "name": "grouped-disable", "prevSize": 32, "code": 59831, - "tempChar": "" + "tempChar": "" }, { "order": 749, @@ -3704,7 +3729,7 @@ "name": "spoiler", "prevSize": 32, "code": 59832, - "tempChar": "" + "tempChar": "" }, { "order": 576, @@ -3712,7 +3737,7 @@ "name": "select", "prevSize": 32, "code": 59744, - "tempChar": "" + "tempChar": "" }, { "order": 480, @@ -3720,7 +3745,7 @@ "name": "folder", "prevSize": 32, "code": 59667, - "tempChar": "" + "tempChar": "" }, { "order": 481, @@ -3728,7 +3753,7 @@ "name": "bots", "prevSize": 32, "code": 59669, - "tempChar": "" + "tempChar": "" }, { "order": 482, @@ -3736,7 +3761,7 @@ "name": "calendar", "prevSize": 32, "code": 59670, - "tempChar": "" + "tempChar": "" }, { "order": 483, @@ -3744,7 +3769,7 @@ "name": "cloud-download", "prevSize": 32, "code": 59671, - "tempChar": "" + "tempChar": "" }, { "order": 484, @@ -3752,7 +3777,7 @@ "name": "colorize", "prevSize": 32, "code": 59672, - "tempChar": "" + "tempChar": "" }, { "order": 651, @@ -3760,7 +3785,7 @@ "name": "forward", "prevSize": 32, "code": 59687, - "tempChar": "" + "tempChar": "" }, { "order": 650, @@ -3768,7 +3793,7 @@ "name": "reply", "prevSize": 32, "code": 59719, - "tempChar": "" + "tempChar": "" }, { "order": 487, @@ -3776,7 +3801,7 @@ "name": "help", "prevSize": 32, "code": 59690, - "tempChar": "" + "tempChar": "" }, { "order": 488, @@ -3784,7 +3809,7 @@ "name": "info", "prevSize": 32, "code": 59691, - "tempChar": "" + "tempChar": "" }, { "order": 489, @@ -3792,7 +3817,7 @@ "name": "info-filled", "prevSize": 32, "code": 59675, - "tempChar": "" + "tempChar": "" }, { "order": 490, @@ -3800,7 +3825,7 @@ "name": "delete-filled", "prevSize": 32, "code": 59676, - "tempChar": "" + "tempChar": "" }, { "order": 491, @@ -3808,7 +3833,7 @@ "name": "delete", "prevSize": 32, "code": 59677, - "tempChar": "" + "tempChar": "" }, { "order": 492, @@ -3816,7 +3841,7 @@ "name": "edit", "prevSize": 32, "code": 59683, - "tempChar": "" + "tempChar": "" }, { "order": 493, @@ -3824,7 +3849,7 @@ "name": "new-chat-filled", "prevSize": 32, "code": 59705, - "tempChar": "" + "tempChar": "" }, { "order": 494, @@ -3832,7 +3857,7 @@ "name": "send", "prevSize": 32, "code": 59722, - "tempChar": "" + "tempChar": "" }, { "order": 495, @@ -3840,7 +3865,7 @@ "name": "send-outline", "prevSize": 32, "code": 59723, - "tempChar": "" + "tempChar": "" }, { "order": 496, @@ -3848,7 +3873,7 @@ "name": "add-user-filled", "prevSize": 32, "code": 59652, - "tempChar": "" + "tempChar": "" }, { "order": 497, @@ -3856,7 +3881,7 @@ "name": "add-user", "prevSize": 32, "code": 59653, - "tempChar": "" + "tempChar": "" }, { "order": 498, @@ -3864,7 +3889,7 @@ "name": "delete-user", "prevSize": 32, "code": 59678, - "tempChar": "" + "tempChar": "" }, { "order": 499, @@ -3872,7 +3897,7 @@ "name": "microphone", "prevSize": 32, "code": 59701, - "tempChar": "" + "tempChar": "" }, { "order": 500, @@ -3880,7 +3905,7 @@ "name": "microphone-alt", "prevSize": 32, "code": 59707, - "tempChar": "" + "tempChar": "" }, { "order": 501, @@ -3888,7 +3913,7 @@ "name": "poll", "prevSize": 32, "code": 59704, - "tempChar": "" + "tempChar": "" }, { "order": 502, @@ -3896,7 +3921,7 @@ "name": "revote", "prevSize": 32, "code": 59706, - "tempChar": "" + "tempChar": "" }, { "order": 503, @@ -3904,7 +3929,7 @@ "name": "photo", "prevSize": 32, "code": 59712, - "tempChar": "" + "tempChar": "" }, { "order": 748, @@ -3912,7 +3937,7 @@ "name": "document", "prevSize": 32, "code": 59679, - "tempChar": "" + "tempChar": "" }, { "order": 505, @@ -3920,7 +3945,7 @@ "name": "camera", "prevSize": 32, "code": 59662, - "tempChar": "" + "tempChar": "" }, { "order": 506, @@ -3928,7 +3953,7 @@ "name": "camera-add", "prevSize": 32, "code": 59663, - "tempChar": "" + "tempChar": "" }, { "order": 507, @@ -3936,7 +3961,7 @@ "name": "logout", "prevSize": 32, "code": 59698, - "tempChar": "" + "tempChar": "" }, { "order": 508, @@ -3944,7 +3969,7 @@ "name": "saved-messages", "prevSize": 32, "code": 59720, - "tempChar": "" + "tempChar": "" }, { "order": 509, @@ -3952,7 +3977,7 @@ "name": "settings", "prevSize": 32, "code": 59726, - "tempChar": "" + "tempChar": "" }, { "order": 652, @@ -3960,7 +3985,7 @@ "name": "phone", "prevSize": 32, "code": 59711, - "tempChar": "" + "tempChar": "" }, { "order": 653, @@ -3968,7 +3993,7 @@ "name": "attach", "prevSize": 32, "code": 59657, - "tempChar": "" + "tempChar": "" }, { "order": 512, @@ -3976,7 +4001,7 @@ "name": "copy", "prevSize": 32, "code": 59674, - "tempChar": "" + "tempChar": "" }, { "order": 513, @@ -3984,7 +4009,7 @@ "name": "channel", "prevSize": 32, "code": 59665, - "tempChar": "" + "tempChar": "" }, { "order": 514, @@ -3992,7 +4017,7 @@ "name": "group", "prevSize": 32, "code": 59689, - "tempChar": "" + "tempChar": "" }, { "order": 515, @@ -4000,7 +4025,7 @@ "name": "user", "prevSize": 32, "code": 59737, - "tempChar": "" + "tempChar": "" }, { "order": 516, @@ -4008,7 +4033,7 @@ "name": "non-contacts", "prevSize": 32, "code": 59688, - "tempChar": "" + "tempChar": "" }, { "order": 517, @@ -4016,7 +4041,7 @@ "name": "active-sessions", "prevSize": 32, "code": 59650, - "tempChar": "" + "tempChar": "" }, { "order": 518, @@ -4024,7 +4049,7 @@ "name": "admin", "prevSize": 32, "code": 59654, - "tempChar": "" + "tempChar": "" }, { "order": 519, @@ -4032,7 +4057,7 @@ "name": "download", "prevSize": 32, "code": 59681, - "tempChar": "" + "tempChar": "" }, { "order": 520, @@ -4040,7 +4065,7 @@ "name": "location", "prevSize": 32, "code": 59696, - "tempChar": "" + "tempChar": "" }, { "order": 521, @@ -4048,7 +4073,7 @@ "name": "stop", "prevSize": 32, "code": 59730, - "tempChar": "" + "tempChar": "" }, { "order": 523, @@ -4056,7 +4081,7 @@ "name": "archive", "prevSize": 32, "code": 59656, - "tempChar": "" + "tempChar": "" }, { "order": 524, @@ -4064,7 +4089,7 @@ "name": "unarchive", "prevSize": 32, "code": 59731, - "tempChar": "" + "tempChar": "" }, { "order": 525, @@ -4072,7 +4097,7 @@ "name": "readchats", "prevSize": 32, "code": 59699, - "tempChar": "" + "tempChar": "" }, { "order": 526, @@ -4080,7 +4105,7 @@ "name": "unread", "prevSize": 32, "code": 59735, - "tempChar": "" + "tempChar": "" }, { "order": 654, @@ -4088,7 +4113,7 @@ "name": "message", "prevSize": 32, "code": 59700, - "tempChar": "" + "tempChar": "" }, { "order": 659, @@ -4096,7 +4121,7 @@ "name": "lock", "prevSize": 32, "code": 59697, - "tempChar": "" + "tempChar": "" }, { "order": 529, @@ -4104,7 +4129,7 @@ "name": "unlock", "prevSize": 32, "code": 59732, - "tempChar": "" + "tempChar": "" }, { "order": 530, @@ -4112,7 +4137,7 @@ "name": "mute", "prevSize": 32, "code": 59703, - "tempChar": "" + "tempChar": "" }, { "order": 531, @@ -4120,7 +4145,7 @@ "name": "unmute", "prevSize": 32, "code": 59733, - "tempChar": "" + "tempChar": "" }, { "order": 532, @@ -4128,7 +4153,7 @@ "name": "pin", "prevSize": 32, "code": 59713, - "tempChar": "" + "tempChar": "" }, { "order": 533, @@ -4136,7 +4161,7 @@ "name": "unpin", "prevSize": 32, "code": 59734, - "tempChar": "" + "tempChar": "" }, { "order": 534, @@ -4144,7 +4169,7 @@ "name": "smallscreen", "prevSize": 32, "code": 59742, - "tempChar": "" + "tempChar": "" }, { "order": 535, @@ -4152,7 +4177,7 @@ "name": "fullscreen", "prevSize": 32, "code": 59743, - "tempChar": "" + "tempChar": "" }, { "order": 536, @@ -4160,7 +4185,7 @@ "name": "large-pause", "prevSize": 32, "code": 59694, - "tempChar": "" + "tempChar": "" }, { "order": 537, @@ -4168,7 +4193,7 @@ "name": "large-play", "prevSize": 32, "code": 59695, - "tempChar": "" + "tempChar": "" }, { "order": 538, @@ -4176,7 +4201,7 @@ "name": "pause", "prevSize": 32, "code": 59709, - "tempChar": "" + "tempChar": "" }, { "order": 539, @@ -4184,7 +4209,7 @@ "name": "play", "prevSize": 32, "code": 59715, - "tempChar": "" + "tempChar": "" }, { "order": 540, @@ -4192,7 +4217,7 @@ "name": "channelviews", "prevSize": 32, "code": 59666, - "tempChar": "" + "tempChar": "" }, { "order": 541, @@ -4200,7 +4225,7 @@ "name": "message-succeeded", "prevSize": 32, "code": 59648, - "tempChar": "" + "tempChar": "" }, { "order": 657, @@ -4208,7 +4233,7 @@ "name": "message-read", "prevSize": 32, "code": 59649, - "tempChar": "" + "tempChar": "" }, { "order": 543, @@ -4216,7 +4241,7 @@ "name": "message-pending", "prevSize": 32, "code": 59724, - "tempChar": "" + "tempChar": "" }, { "order": 544, @@ -4224,7 +4249,7 @@ "name": "message-failed", "prevSize": 32, "code": 59725, - "tempChar": "" + "tempChar": "" }, { "order": 545, @@ -4232,7 +4257,7 @@ "name": "favorite", "prevSize": 32, "code": 59710, - "tempChar": "" + "tempChar": "" }, { "order": 546, @@ -4240,7 +4265,7 @@ "name": "keyboard", "prevSize": 32, "code": 59716, - "tempChar": "" + "tempChar": "" }, { "order": 547, @@ -4248,7 +4273,7 @@ "name": "delete-left", "prevSize": 32, "code": 59717, - "tempChar": "" + "tempChar": "" }, { "order": 548, @@ -4256,7 +4281,7 @@ "name": "recent", "prevSize": 32, "code": 59718, - "tempChar": "" + "tempChar": "" }, { "order": 549, @@ -4264,7 +4289,7 @@ "name": "gifs", "prevSize": 32, "code": 59727, - "tempChar": "" + "tempChar": "" }, { "order": 550, @@ -4272,7 +4297,7 @@ "name": "stickers", "prevSize": 32, "code": 59739, - "tempChar": "" + "tempChar": "" }, { "order": 551, @@ -4280,7 +4305,7 @@ "name": "smile", "prevSize": 32, "code": 59728, - "tempChar": "" + "tempChar": "" }, { "order": 552, @@ -4288,7 +4313,7 @@ "name": "animals", "prevSize": 32, "code": 59655, - "tempChar": "" + "tempChar": "" }, { "order": 553, @@ -4296,7 +4321,7 @@ "name": "eats", "prevSize": 32, "code": 59682, - "tempChar": "" + "tempChar": "" }, { "order": 554, @@ -4304,7 +4329,7 @@ "name": "sport", "prevSize": 32, "code": 59729, - "tempChar": "" + "tempChar": "" }, { "order": 555, @@ -4312,7 +4337,7 @@ "name": "car", "prevSize": 32, "code": 59664, - "tempChar": "" + "tempChar": "" }, { "order": 556, @@ -4320,7 +4345,7 @@ "name": "lamp", "prevSize": 32, "code": 59692, - "tempChar": "" + "tempChar": "" }, { "order": 557, @@ -4328,7 +4353,7 @@ "name": "language", "prevSize": 32, "code": 59693, - "tempChar": "" + "tempChar": "" }, { "order": 558, @@ -4336,7 +4361,7 @@ "name": "flag", "prevSize": 32, "code": 59686, - "tempChar": "" + "tempChar": "" }, { "order": 559, @@ -4344,7 +4369,7 @@ "name": "more", "prevSize": 32, "code": 59702, - "tempChar": "" + "tempChar": "" }, { "order": 560, @@ -4352,7 +4377,7 @@ "name": "search", "prevSize": 32, "code": 59721, - "tempChar": "" + "tempChar": "" }, { "order": 561, @@ -4360,7 +4385,7 @@ "name": "remove", "prevSize": 32, "code": 59740, - "tempChar": "" + "tempChar": "" }, { "order": 562, @@ -4368,7 +4393,7 @@ "name": "add", "prevSize": 32, "code": 59651, - "tempChar": "" + "tempChar": "" }, { "order": 563, @@ -4376,7 +4401,7 @@ "name": "check", "prevSize": 32, "code": 59668, - "tempChar": "" + "tempChar": "" }, { "order": 564, @@ -4384,7 +4409,7 @@ "name": "close", "prevSize": 32, "code": 59673, - "tempChar": "" + "tempChar": "" }, { "order": 610, @@ -4392,7 +4417,7 @@ "name": "arrow-left", "prevSize": 32, "code": 59661, - "tempChar": "" + "tempChar": "" }, { "order": 566, @@ -4400,7 +4425,7 @@ "name": "arrow-right", "prevSize": 32, "code": 59708, - "tempChar": "" + "tempChar": "" }, { "order": 730, @@ -4408,7 +4433,7 @@ "name": "down", "prevSize": 32, "code": 59680, - "tempChar": "" + "tempChar": "" }, { "order": 568, @@ -4416,7 +4441,7 @@ "name": "up", "prevSize": 32, "code": 59736, - "tempChar": "" + "tempChar": "" }, { "order": 569, @@ -4424,7 +4449,7 @@ "name": "eye-closed", "prevSize": 32, "code": 59685, - "tempChar": "" + "tempChar": "" }, { "order": 570, @@ -4432,7 +4457,7 @@ "name": "eye", "prevSize": 32, "code": 59684, - "tempChar": "" + "tempChar": "" }, { "order": 571, @@ -4440,7 +4465,7 @@ "name": "muted", "prevSize": 32, "code": 59741, - "tempChar": "" + "tempChar": "" }, { "order": 572, @@ -4448,7 +4473,7 @@ "name": "avatar-archived-chats", "prevSize": 32, "code": 59658, - "tempChar": "" + "tempChar": "" }, { "order": 573, @@ -4456,7 +4481,7 @@ "name": "avatar-deleted-account", "prevSize": 32, "code": 59659, - "tempChar": "" + "tempChar": "" }, { "order": 747, @@ -4464,7 +4489,7 @@ "name": "avatar-saved-messages", "prevSize": 32, "code": 59660, - "tempChar": "" + "tempChar": "" }, { "order": 575, @@ -4472,7 +4497,7 @@ "name": "pinned-chat", "prevSize": 32, "code": 59714, - "tempChar": "" + "tempChar": "" } ], "prevSize": 32, @@ -4524,4 +4549,4 @@ "showLiga": false }, "uid": -1 -} +} \ No newline at end of file diff --git a/src/styles/icons.scss b/src/styles/icons.scss index 44cdb0978..f9560f509 100644 --- a/src/styles/icons.scss +++ b/src/styles/icons.scss @@ -51,6 +51,9 @@ .icon-volume-3:before { content: "\e991"; } +.icon-replies:before { + content: "\e9b9"; +} .icon-forums:before { content: "\e9b4"; }