[Perf] Optimistically mark messages read

This commit is contained in:
Alexander Zinchuk 2021-08-16 20:42:21 +03:00
parent 13b62d0199
commit 5a7bbb19b1
4 changed files with 52 additions and 8 deletions

View File

@ -140,6 +140,7 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
const anchorIdRef = useRef<string>();
const anchorTopRef = useRef<number>();
const listItemElementsRef = useRef<HTMLDivElement[]>();
// Not updated (as we want the unread divider to keep its position)
const memoUnreadDividerBeforeIdRef = useRef<number | undefined>(firstUnreadId);
// Updated every time (to be used from intersection callback closure)
const memoFirstUnreadIdRef = useRef<number>();
@ -485,7 +486,8 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
noAvatars={noAvatars}
containerRef={containerRef}
anchorIdRef={anchorIdRef}
memoFirstUnreadIdRef={memoUnreadDividerBeforeIdRef}
memoUnreadDividerBeforeIdRef={memoUnreadDividerBeforeIdRef}
memoFirstUnreadIdRef={memoFirstUnreadIdRef}
threadId={threadId}
type={type}
threadTopMessageId={threadTopMessageId}

View File

@ -26,6 +26,7 @@ interface OwnProps {
noAvatars: boolean;
containerRef: RefObject<HTMLDivElement>;
anchorIdRef: { current: string | undefined };
memoUnreadDividerBeforeIdRef: { current: number | undefined };
memoFirstUnreadIdRef: { current: number | undefined };
threadId: number;
type: MessageListType;
@ -49,6 +50,7 @@ const MessageListContent: FC<OwnProps> = ({
noAvatars,
containerRef,
anchorIdRef,
memoUnreadDividerBeforeIdRef,
memoFirstUnreadIdRef,
threadId,
type,
@ -111,7 +113,7 @@ const MessageListContent: FC<OwnProps> = ({
);
return compact([
message.id === memoFirstUnreadIdRef.current && unreadDivider,
message.id === memoUnreadDividerBeforeIdRef.current && unreadDivider,
<ActionMessage
key={message.id}
message={message}
@ -162,7 +164,7 @@ const MessageListContent: FC<OwnProps> = ({
const key = type !== 'scheduled' ? originalId : `${message.date}_${originalId}`;
return compact([
message.id === memoFirstUnreadIdRef.current ? unreadDivider : undefined,
message.id === memoUnreadDividerBeforeIdRef.current && unreadDivider,
<Message
key={key}
message={message}

View File

@ -9,6 +9,7 @@ import useBackgroundMode from '../../../hooks/useBackgroundMode';
const INTERSECTION_THROTTLE_FOR_MEDIA = IS_ANDROID ? 1000 : 350;
const INTERSECTION_MARGIN_FOR_MEDIA = IS_SINGLE_COLUMN_LAYOUT ? 300 : 500;
const INTERSECTION_THROTTLE_FOR_READING = 150;
export default function useMessageObservers(
type: MessageListType,
@ -29,6 +30,7 @@ export default function useMessageObservers(
observe: observeIntersectionForReading, freeze: freezeForReading, unfreeze: unfreezeForReading,
} = useIntersectionObserver({
rootRef: containerRef,
throttleMs: INTERSECTION_THROTTLE_FOR_READING,
}, (entries) => {
if (type !== 'thread') {
return;

View File

@ -50,13 +50,14 @@ import {
selectEditingMessage,
selectScheduledMessage,
selectNoWebPage,
selectFirstUnreadId,
} from '../../selectors';
import { rafPromise, throttle } from '../../../util/schedulers';
import { debounce, rafPromise } from '../../../util/schedulers';
import { IS_IOS } from '../../../util/environment';
const uploadProgressCallbacks = new Map<number, ApiOnProgress>();
const runThrottledForMarkRead = throttle((cb) => cb(), 1000, true);
const runDebouncedForMarkRead = debounce((cb) => cb(), 1000, false);
addReducer('loadViewportMessages', (global, actions, payload) => {
const {
@ -443,22 +444,43 @@ addReducer('markMessageListRead', (global, actions, payload) => {
const { serverTimeOffset } = global;
const currentMessageList = selectCurrentMessageList(global);
if (!currentMessageList) {
return;
return undefined;
}
const { chatId, threadId } = currentMessageList;
const chat = selectThreadOriginChat(global, chatId, threadId);
if (!chat) {
return;
return undefined;
}
const { maxId } = payload!;
runThrottledForMarkRead(() => {
runDebouncedForMarkRead(() => {
void callApi('markMessageListRead', {
serverTimeOffset, chat, threadId, maxId,
});
});
// TODO Support local marking read for threads
if (threadId !== MAIN_THREAD_ID) {
return undefined;
}
const viewportIds = selectViewportIds(global, chatId, threadId);
const minId = selectFirstUnreadId(global, chatId, threadId);
if (!viewportIds || !minId || !chat.unreadCount) {
return undefined;
}
const readCount = countSortedIds(viewportIds!, minId, maxId);
if (!readCount) {
return undefined;
}
return updateChat(global, chatId, {
lastReadInboxMessageId: maxId,
unreadCount: Math.max(0, chat.unreadCount - readCount),
});
});
addReducer('markMessagesRead', (global, actions, payload) => {
@ -892,3 +914,19 @@ async function loadScheduledHistory(chat: ApiChat, historyHash?: number) {
global = replaceThreadParam(global, chat.id, MAIN_THREAD_ID, 'scheduledIds', ids);
setGlobal(global);
}
function countSortedIds(ids: number[], from: number, to: number) {
let count = 0;
for (let i = 0, l = ids.length; i < l; i++) {
if (ids[i] >= from && ids[i] <= to) {
count++;
}
if (ids[i] >= to) {
break;
}
}
return count;
}