TelegramPWA/src/components/middle/hooks/useMessageObservers.ts

126 lines
3.7 KiB
TypeScript

import type { RefObject } from 'react';
import { getActions } from '../../../global';
import type { MessageListType } from '../../../types';
import type { OnIntersectPinnedMessage } from './usePinnedMessage';
import { IS_ANDROID } from '../../../util/windowEnvironment';
import useAppLayout from '../../../hooks/useAppLayout';
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
import useBackgroundMode, { isBackgroundModeActive } from '../../../hooks/window/useBackgroundMode';
const INTERSECTION_THROTTLE_FOR_READING = 150;
const INTERSECTION_THROTTLE_FOR_MEDIA = IS_ANDROID ? 1000 : 350;
export default function useMessageObservers(
type: MessageListType,
containerRef: RefObject<HTMLDivElement>,
memoFirstUnreadIdRef: { current: number | undefined },
onIntersectPinnedMessage: OnIntersectPinnedMessage,
chatId: string,
) {
const {
markMessageListRead, markMentionsRead, animateUnreadReaction,
scheduleForViewsIncrement,
} = getActions();
const { isMobile } = useAppLayout();
const INTERSECTION_MARGIN_FOR_LOADING = isMobile ? 300 : 500;
const {
observe: observeIntersectionForReading, freeze: freezeForReading, unfreeze: unfreezeForReading,
} = useIntersectionObserver({
rootRef: containerRef,
throttleMs: INTERSECTION_THROTTLE_FOR_READING,
}, (entries) => {
if (type !== 'thread' || isBackgroundModeActive()) {
return;
}
let maxId = 0;
const mentionIds: number[] = [];
const reactionIds: number[] = [];
const viewportPinnedIdsToAdd: number[] = [];
const viewportPinnedIdsToRemove: number[] = [];
const scheduledToUpdateViews: number[] = [];
entries.forEach((entry) => {
const { isIntersecting, target } = entry;
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) {
if (dataset.isPinned) {
viewportPinnedIdsToRemove.push(albumMainId || messageId);
}
return;
}
if (messageId > maxId) {
maxId = messageId;
}
if (dataset.hasUnreadMention) {
mentionIds.push(messageId);
}
if (dataset.hasUnreadReaction) {
reactionIds.push(messageId);
}
if (dataset.isPinned) {
viewportPinnedIdsToAdd.push(albumMainId || messageId);
}
if (shouldUpdateViews) {
scheduledToUpdateViews.push(albumMainId || messageId);
}
});
if (memoFirstUnreadIdRef.current && maxId >= memoFirstUnreadIdRef.current) {
markMessageListRead({ maxId });
}
if (mentionIds.length) {
markMentionsRead({ messageIds: mentionIds });
}
if (reactionIds.length) {
animateUnreadReaction({ messageIds: reactionIds });
}
if (viewportPinnedIdsToAdd.length || viewportPinnedIdsToRemove.length) {
onIntersectPinnedMessage({ viewportPinnedIdsToAdd, viewportPinnedIdsToRemove });
}
if (scheduledToUpdateViews.length) {
scheduleForViewsIncrement({ chatId, ids: scheduledToUpdateViews });
}
});
useBackgroundMode(freezeForReading, unfreezeForReading);
const {
observe: observeIntersectionForLoading,
} = useIntersectionObserver({
rootRef: containerRef,
throttleMs: INTERSECTION_THROTTLE_FOR_MEDIA,
margin: INTERSECTION_MARGIN_FOR_LOADING,
});
const { observe: observeIntersectionForPlaying } = useIntersectionObserver({
rootRef: containerRef,
throttleMs: INTERSECTION_THROTTLE_FOR_MEDIA,
});
return {
observeIntersectionForReading,
observeIntersectionForLoading,
observeIntersectionForPlaying,
};
}