diff --git a/src/api/gramjs/methods/messages.ts b/src/api/gramjs/methods/messages.ts index b40f82243..ccc68c499 100644 --- a/src/api/gramjs/methods/messages.ts +++ b/src/api/gramjs/methods/messages.ts @@ -875,14 +875,16 @@ export async function markMessagesRead({ } export async function fetchMessageViews({ - chat, ids, + chat, ids, shouldIncrement, }: { - chat: ApiChat; ids: number[]; + chat: ApiChat; + ids: number[]; + shouldIncrement?: boolean; }) { const result = await invokeRequest(new GramJs.messages.GetMessagesViews({ peer: buildInputPeer(chat.id, chat.accessHash), id: ids, - increment: false, + increment: shouldIncrement, })); if (!result) return undefined; diff --git a/src/components/middle/MessageList.tsx b/src/components/middle/MessageList.tsx index 77490902d..98a7ed407 100644 --- a/src/components/middle/MessageList.tsx +++ b/src/components/middle/MessageList.tsx @@ -302,7 +302,8 @@ const MessageList: FC = ({ if (!messageIds || !messagesById || threadId !== MAIN_THREAD_ID) { return; } - const ids = messageIds.filter((id) => messagesById[id]?.repliesThreadInfo?.isComments); + const ids = messageIds.filter((id) => messagesById[id]?.repliesThreadInfo?.isComments + || messagesById[id]?.views !== undefined); if (!ids.length) return; diff --git a/src/components/middle/MessageListContent.tsx b/src/components/middle/MessageListContent.tsx index 9b3f9d6dd..e67a07502 100644 --- a/src/components/middle/MessageListContent.tsx +++ b/src/components/middle/MessageListContent.tsx @@ -90,7 +90,7 @@ const MessageListContent: FC = ({ observeIntersectionForReading, observeIntersectionForLoading, observeIntersectionForPlaying, - } = useMessageObservers(type, containerRef, memoFirstUnreadIdRef, onPinnedIntersectionChange); + } = useMessageObservers(type, containerRef, memoFirstUnreadIdRef, onPinnedIntersectionChange, chatId); const { backwardsTriggerRef, diff --git a/src/components/middle/hooks/useMessageObservers.ts b/src/components/middle/hooks/useMessageObservers.ts index a97c1981f..a0f93563c 100644 --- a/src/components/middle/hooks/useMessageObservers.ts +++ b/src/components/middle/hooks/useMessageObservers.ts @@ -17,9 +17,11 @@ export default function useMessageObservers( containerRef: RefObject, memoFirstUnreadIdRef: { current: number | undefined }, onPinnedIntersectionChange: PinnedIntersectionChangedCallback, + chatId: string, ) { const { markMessageListRead, markMentionsRead, animateUnreadReaction, + scheduleForViewsIncrement, } = getActions(); const { isMobile } = useAppLayout(); @@ -40,6 +42,7 @@ export default function useMessageObservers( const reactionIds: number[] = []; const viewportPinnedIdsToAdd: number[] = []; const viewportPinnedIdsToRemove: number[] = []; + const scheduledToUpdateViews: number[] = []; let isReversed = false; entries.forEach((entry) => { @@ -49,6 +52,7 @@ export default function useMessageObservers( const { dataset } = target as HTMLDivElement; const messageId = Number(dataset.lastMessageId || dataset.messageId); + const shouldUpdateViews = dataset.shouldUpdateViews === 'true'; const albumMainId = dataset.albumMainId ? Number(dataset.albumMainId) : undefined; if (!isIntersecting) { @@ -76,6 +80,10 @@ export default function useMessageObservers( if (dataset.isPinned) { viewportPinnedIdsToAdd.push(albumMainId || messageId); } + + if (shouldUpdateViews) { + scheduledToUpdateViews.push(albumMainId || messageId); + } }); if (memoFirstUnreadIdRef.current && maxId >= memoFirstUnreadIdRef.current) { @@ -93,6 +101,10 @@ export default function useMessageObservers( if (viewportPinnedIdsToAdd.length || viewportPinnedIdsToRemove.length) { onPinnedIntersectionChange({ viewportPinnedIdsToAdd, viewportPinnedIdsToRemove, isReversed }); } + + if (scheduledToUpdateViews.length) { + scheduleForViewsIncrement({ chatId, ids: scheduledToUpdateViews }); + } }); useBackgroundMode(freezeForReading, unfreezeForReading); diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx index 338f7f650..8993b5d2e 100644 --- a/src/components/middle/message/Message.tsx +++ b/src/components/middle/message/Message.tsx @@ -1236,6 +1236,7 @@ const Message: FC = ({ data-has-unread-mention={message.hasUnreadMention || undefined} data-has-unread-reaction={hasUnreadReaction || undefined} data-is-pinned={isPinned || undefined} + data-should-update-views={message.views !== undefined} /> {!isInDocumentGroup && (
diff --git a/src/global/actions/api/messages.ts b/src/global/actions/api/messages.ts index 6b1a35cd1..7ff60db7a 100644 --- a/src/global/actions/api/messages.ts +++ b/src/global/actions/api/messages.ts @@ -1,5 +1,7 @@ import type { RequiredGlobalActions } from '../../index'; -import { addActionHandler, getGlobal, setGlobal } from '../../index'; +import { + addActionHandler, getActions, getGlobal, setGlobal, +} from '../../index'; import type { ActionReturnType, ApiDraft, GlobalState, TabArgs, @@ -1473,8 +1475,43 @@ addActionHandler('translateMessages', (global, actions, payload): ActionReturnTy return global; }); +// https://github.com/telegramdesktop/tdesktop/blob/11906297d82b6ff57b277da5251d2e6eb3d8b6d0/Telegram/SourceFiles/api/api_views.cpp#L22 +const SEND_VIEWS_TIMEOUT = 1000; +let viewsIncrementTimeout: number | undefined; +let idsToIncrementViews: Record> = {}; + +function incrementViews() { + if (viewsIncrementTimeout) { + clearTimeout(viewsIncrementTimeout); + viewsIncrementTimeout = undefined; + } + + // eslint-disable-next-line eslint-multitab-tt/no-getactions-in-actions + const { loadMessageViews } = getActions(); + Object.entries(idsToIncrementViews).forEach(([chatId, ids]) => { + loadMessageViews({ chatId, ids: Array.from(ids), shouldIncrement: true }); + }); + + idsToIncrementViews = {}; +} +addActionHandler('scheduleForViewsIncrement', (global, actions, payload): ActionReturnType => { + const { ids, chatId } = payload; + + if (!viewsIncrementTimeout) { + setTimeout(incrementViews, SEND_VIEWS_TIMEOUT); + } + + if (!idsToIncrementViews[chatId]) { + idsToIncrementViews[chatId] = new Set(); + } + + ids.forEach((id) => { + idsToIncrementViews[chatId].add(id); + }); +}); + addActionHandler('loadMessageViews', async (global, actions, payload): Promise => { - const { chatId, ids } = payload; + const { chatId, ids, shouldIncrement } = payload; const chat = selectChat(global, chatId); if (!chat) return; @@ -1482,6 +1519,7 @@ addActionHandler('loadMessageViews', async (global, actions, payload): Promise