Increment view count on view (#3097)

This commit is contained in:
Alexander Zinchuk 2023-04-27 18:53:34 +04:00
parent 565a281d02
commit f8513e3242
7 changed files with 66 additions and 7 deletions

View File

@ -875,14 +875,16 @@ export async function markMessagesRead({
} }
export async function fetchMessageViews({ 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({ const result = await invokeRequest(new GramJs.messages.GetMessagesViews({
peer: buildInputPeer(chat.id, chat.accessHash), peer: buildInputPeer(chat.id, chat.accessHash),
id: ids, id: ids,
increment: false, increment: shouldIncrement,
})); }));
if (!result) return undefined; if (!result) return undefined;

View File

@ -302,7 +302,8 @@ const MessageList: FC<OwnProps & StateProps> = ({
if (!messageIds || !messagesById || threadId !== MAIN_THREAD_ID) { if (!messageIds || !messagesById || threadId !== MAIN_THREAD_ID) {
return; 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; if (!ids.length) return;

View File

@ -90,7 +90,7 @@ const MessageListContent: FC<OwnProps> = ({
observeIntersectionForReading, observeIntersectionForReading,
observeIntersectionForLoading, observeIntersectionForLoading,
observeIntersectionForPlaying, observeIntersectionForPlaying,
} = useMessageObservers(type, containerRef, memoFirstUnreadIdRef, onPinnedIntersectionChange); } = useMessageObservers(type, containerRef, memoFirstUnreadIdRef, onPinnedIntersectionChange, chatId);
const { const {
backwardsTriggerRef, backwardsTriggerRef,

View File

@ -17,9 +17,11 @@ export default function useMessageObservers(
containerRef: RefObject<HTMLDivElement>, containerRef: RefObject<HTMLDivElement>,
memoFirstUnreadIdRef: { current: number | undefined }, memoFirstUnreadIdRef: { current: number | undefined },
onPinnedIntersectionChange: PinnedIntersectionChangedCallback, onPinnedIntersectionChange: PinnedIntersectionChangedCallback,
chatId: string,
) { ) {
const { const {
markMessageListRead, markMentionsRead, animateUnreadReaction, markMessageListRead, markMentionsRead, animateUnreadReaction,
scheduleForViewsIncrement,
} = getActions(); } = getActions();
const { isMobile } = useAppLayout(); const { isMobile } = useAppLayout();
@ -40,6 +42,7 @@ export default function useMessageObservers(
const reactionIds: number[] = []; const reactionIds: number[] = [];
const viewportPinnedIdsToAdd: number[] = []; const viewportPinnedIdsToAdd: number[] = [];
const viewportPinnedIdsToRemove: number[] = []; const viewportPinnedIdsToRemove: number[] = [];
const scheduledToUpdateViews: number[] = [];
let isReversed = false; let isReversed = false;
entries.forEach((entry) => { entries.forEach((entry) => {
@ -49,6 +52,7 @@ export default function useMessageObservers(
const { dataset } = target as HTMLDivElement; const { dataset } = target as HTMLDivElement;
const messageId = Number(dataset.lastMessageId || dataset.messageId); const messageId = Number(dataset.lastMessageId || dataset.messageId);
const shouldUpdateViews = dataset.shouldUpdateViews === 'true';
const albumMainId = dataset.albumMainId ? Number(dataset.albumMainId) : undefined; const albumMainId = dataset.albumMainId ? Number(dataset.albumMainId) : undefined;
if (!isIntersecting) { if (!isIntersecting) {
@ -76,6 +80,10 @@ export default function useMessageObservers(
if (dataset.isPinned) { if (dataset.isPinned) {
viewportPinnedIdsToAdd.push(albumMainId || messageId); viewportPinnedIdsToAdd.push(albumMainId || messageId);
} }
if (shouldUpdateViews) {
scheduledToUpdateViews.push(albumMainId || messageId);
}
}); });
if (memoFirstUnreadIdRef.current && maxId >= memoFirstUnreadIdRef.current) { if (memoFirstUnreadIdRef.current && maxId >= memoFirstUnreadIdRef.current) {
@ -93,6 +101,10 @@ export default function useMessageObservers(
if (viewportPinnedIdsToAdd.length || viewportPinnedIdsToRemove.length) { if (viewportPinnedIdsToAdd.length || viewportPinnedIdsToRemove.length) {
onPinnedIntersectionChange({ viewportPinnedIdsToAdd, viewportPinnedIdsToRemove, isReversed }); onPinnedIntersectionChange({ viewportPinnedIdsToAdd, viewportPinnedIdsToRemove, isReversed });
} }
if (scheduledToUpdateViews.length) {
scheduleForViewsIncrement({ chatId, ids: scheduledToUpdateViews });
}
}); });
useBackgroundMode(freezeForReading, unfreezeForReading); useBackgroundMode(freezeForReading, unfreezeForReading);

View File

@ -1236,6 +1236,7 @@ const Message: FC<OwnProps & StateProps> = ({
data-has-unread-mention={message.hasUnreadMention || undefined} data-has-unread-mention={message.hasUnreadMention || undefined}
data-has-unread-reaction={hasUnreadReaction || undefined} data-has-unread-reaction={hasUnreadReaction || undefined}
data-is-pinned={isPinned || undefined} data-is-pinned={isPinned || undefined}
data-should-update-views={message.views !== undefined}
/> />
{!isInDocumentGroup && ( {!isInDocumentGroup && (
<div className="message-select-control"> <div className="message-select-control">

View File

@ -1,5 +1,7 @@
import type { RequiredGlobalActions } from '../../index'; import type { RequiredGlobalActions } from '../../index';
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import {
addActionHandler, getActions, getGlobal, setGlobal,
} from '../../index';
import type { import type {
ActionReturnType, ApiDraft, GlobalState, TabArgs, ActionReturnType, ApiDraft, GlobalState, TabArgs,
@ -1473,8 +1475,43 @@ addActionHandler('translateMessages', (global, actions, payload): ActionReturnTy
return global; 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<string, Set<number>> = {};
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<void> => { addActionHandler('loadMessageViews', async (global, actions, payload): Promise<void> => {
const { chatId, ids } = payload; const { chatId, ids, shouldIncrement } = payload;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat) return; if (!chat) return;
@ -1482,6 +1519,7 @@ addActionHandler('loadMessageViews', async (global, actions, payload): Promise<v
const result = await callApi('fetchMessageViews', { const result = await callApi('fetchMessageViews', {
chat, chat,
ids, ids,
shouldIncrement,
}); });
if (!result) return; if (!result) return;

View File

@ -1736,9 +1736,14 @@ export interface ActionPayloads {
chatId: string; chatId: string;
offsetId?: number; offsetId?: number;
}; };
scheduleForViewsIncrement: {
chatId: string;
ids: number[];
};
loadMessageViews: { loadMessageViews: {
chatId: string; chatId: string;
ids: number[]; ids: number[];
shouldIncrement?: boolean;
}; };
animateUnreadReaction: { animateUnreadReaction: {
messageIds: number[]; messageIds: number[];