[Perf] Message: Delay some effects while opening message list

This commit is contained in:
Alexander Zinchuk 2023-07-23 10:07:54 +02:00
parent 62d3d7cc42
commit 6a57908045
4 changed files with 91 additions and 59 deletions

View File

@ -22,6 +22,7 @@ import { preventMessageInputBlur } from './helpers/preventMessageInputBlur';
import useScrollHooks from './hooks/useScrollHooks';
import useMessageObservers from './hooks/useMessageObservers';
import usePrevious from '../../hooks/usePrevious';
import useDerivedSignal from '../../hooks/useDerivedSignal';
import Message from './message/Message';
import SponsoredMessage from './message/SponsoredMessage';
@ -89,6 +90,8 @@ const MessageListContent: FC<OwnProps> = ({
}) => {
const { openHistoryCalendar } = getActions();
const getIsReady = useDerivedSignal(isReady);
const {
observeIntersectionForReading,
observeIntersectionForLoading,
@ -237,6 +240,7 @@ const MessageListContent: FC<OwnProps> = ({
isLastInList={position.isLastInList}
memoFirstUnreadIdRef={memoFirstUnreadIdRef}
onPinnedIntersectionChange={onPinnedIntersectionChange}
getIsMessageListReady={getIsReady}
/>,
message.id === threadTopMessageId && (
<div className="local-action-message" key="discussion-started">

View File

@ -1,11 +1,6 @@
import type { FC } from '../../../lib/teact/teact';
import React, {
memo,
useCallback,
useEffect,
useMemo,
useRef,
useState,
memo, useCallback, useEffect, useMemo, useRef, useState,
} from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
import type {
@ -33,6 +28,7 @@ import { AudioOrigin } from '../../../types';
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
import { useOnIntersect } from '../../../hooks/useIntersectionObserver';
import type { PinnedIntersectionChangedCallback } from '../hooks/usePinnedMessage';
import type { Signal } from '../../../util/signals';
import { IS_ANDROID, IS_TRANSLATION_SUPPORTED } from '../../../util/windowEnvironment';
import { EMOJI_STATUS_LOOP_LIMIT, GENERAL_TOPIC_ID, IS_ELECTRON } from '../../../config';
@ -191,6 +187,7 @@ type OwnProps =
isJustAdded: boolean;
memoFirstUnreadIdRef: { current: number | undefined };
onPinnedIntersectionChange: PinnedIntersectionChangedCallback;
getIsMessageListReady: Signal<boolean>;
}
& MessagePositionProperties;
@ -366,6 +363,7 @@ const Message: FC<OwnProps & StateProps> = ({
withStickerEffects,
isConnected,
onPinnedIntersectionChange,
getIsMessageListReady,
}) => {
const {
toggleMessageSelection,
@ -400,7 +398,13 @@ const Message: FC<OwnProps & StateProps> = ({
handleContextMenu: onContextMenu,
handleContextMenuClose,
handleContextMenuHide,
} = useContextMenuHandlers(ref, isTouchScreen && isInSelectMode, !IS_ELECTRON, IS_ANDROID);
} = useContextMenuHandlers(
ref,
isTouchScreen && isInSelectMode,
!IS_ELECTRON,
IS_ANDROID,
getIsMessageListReady,
);
useEffect(() => {
if (isContextMenuOpen) {
@ -512,6 +516,7 @@ const Message: FC<OwnProps & StateProps> = ({
isContextMenuShown,
quickReactionRef,
isInDocumentGroupNotLast,
getIsMessageListReady,
);
const {
@ -716,11 +721,6 @@ const Message: FC<OwnProps & StateProps> = ({
}
}, [hasUnreadReaction, messageId, animateUnreadReaction]);
let style = '';
let calculatedWidth;
let reactionsMaxWidth;
let contentWidth: number | undefined;
let noMediaCorners = false;
const albumLayout = useMemo(() => {
return isAlbum
? calculateAlbumLayout(isOwn, Boolean(asForwarded), Boolean(noAvatars), album!, isMobile)
@ -728,56 +728,76 @@ const Message: FC<OwnProps & StateProps> = ({
}, [isAlbum, isOwn, asForwarded, noAvatars, album, isMobile]);
const extraPadding = asForwarded ? 28 : 0;
if (!isAlbum && (photo || video || invoice?.extendedMedia)) {
let width: number | undefined;
if (photo) {
width = calculateMediaDimensions(message, asForwarded, noAvatars, isMobile).width;
} else if (video) {
if (video.isRound) {
width = ROUND_VIDEO_DIMENSIONS_PX;
} else {
width = calculateMediaDimensions(message, asForwarded, noAvatars, isMobile).width;
}
} else if (invoice?.extendedMedia && (
invoice.extendedMedia.width && invoice.extendedMedia.height
)) {
const { width: previewWidth, height: previewHeight } = invoice.extendedMedia;
width = calculateDimensionsForMessageMedia({
width: previewWidth,
height: previewHeight,
fromOwnMessage: isOwn,
asForwarded,
noAvatars,
isMobile,
}).width;
}
if (width) {
if (width < MIN_MEDIA_WIDTH_WITH_TEXT) {
contentWidth = width;
const sizeCalculations = useMemo(() => {
let calculatedWidth;
let contentWidth: number | undefined;
let noMediaCorners = false;
let style = '';
let reactionsMaxWidth;
if (!isAlbum && (photo || video || invoice?.extendedMedia)) {
let width: number | undefined;
if (photo) {
width = calculateMediaDimensions(message, asForwarded, noAvatars, isMobile).width;
} else if (video) {
if (video.isRound) {
width = ROUND_VIDEO_DIMENSIONS_PX;
} else {
width = calculateMediaDimensions(message, asForwarded, noAvatars, isMobile).width;
}
} else if (invoice?.extendedMedia && (
invoice.extendedMedia.width && invoice.extendedMedia.height
)) {
const { width: previewWidth, height: previewHeight } = invoice.extendedMedia;
width = calculateDimensionsForMessageMedia({
width: previewWidth,
height: previewHeight,
fromOwnMessage: isOwn,
asForwarded,
noAvatars,
isMobile,
}).width;
}
calculatedWidth = Math.max(getMinMediaWidth(text?.text, isMediaWithCommentButton), width);
if (invoice?.extendedMedia && calculatedWidth - width > NO_MEDIA_CORNERS_THRESHOLD) {
if (width) {
if (width < MIN_MEDIA_WIDTH_WITH_TEXT) {
contentWidth = width;
}
calculatedWidth = Math.max(getMinMediaWidth(text?.text, isMediaWithCommentButton), width);
if (invoice?.extendedMedia && calculatedWidth - width > NO_MEDIA_CORNERS_THRESHOLD) {
noMediaCorners = true;
}
}
} else if (albumLayout) {
calculatedWidth = Math.max(
getMinMediaWidth(text?.text, isMediaWithCommentButton), albumLayout.containerStyle.width,
);
if (calculatedWidth - albumLayout.containerStyle.width > NO_MEDIA_CORNERS_THRESHOLD) {
noMediaCorners = true;
}
}
} else if (albumLayout) {
calculatedWidth = Math.max(
getMinMediaWidth(text?.text, isMediaWithCommentButton), albumLayout.containerStyle.width,
);
if (calculatedWidth - albumLayout.containerStyle.width > NO_MEDIA_CORNERS_THRESHOLD) {
noMediaCorners = true;
}
}
if (calculatedWidth) {
style = `width: ${calculatedWidth + extraPadding}px`;
reactionsMaxWidth = calculatedWidth + EXTRA_SPACE_FOR_REACTIONS;
} else if (sticker && !hasSubheader) {
const { width } = getStickerDimensions(sticker, isMobile);
style = `width: ${width + extraPadding}px`;
reactionsMaxWidth = width + EXTRA_SPACE_FOR_REACTIONS;
}
if (calculatedWidth) {
style = `width: ${calculatedWidth + extraPadding}px`;
reactionsMaxWidth = calculatedWidth + EXTRA_SPACE_FOR_REACTIONS;
} else if (sticker && !hasSubheader) {
const { width } = getStickerDimensions(sticker, isMobile);
style = `width: ${width + extraPadding}px`;
reactionsMaxWidth = width + EXTRA_SPACE_FOR_REACTIONS;
}
return {
contentWidth, noMediaCorners, style, reactionsMaxWidth,
};
}, [
albumLayout, asForwarded, extraPadding, hasSubheader, invoice?.extendedMedia, isAlbum, isMediaWithCommentButton,
isMobile, isOwn, message, noAvatars, photo, sticker, text?.text, video,
]);
const {
contentWidth, noMediaCorners, style, reactionsMaxWidth,
} = sizeCalculations;
function renderAvatar() {
const hiddenName = (!avatarPeer && forwardInfo) ? forwardInfo.hiddenUserName : undefined;

View File

@ -1,5 +1,7 @@
import type { RefObject } from 'react';
import type React from '../../../../lib/teact/teact';
import type { Signal } from '../../../../util/signals';
import { useEffect, useRef } from '../../../../lib/teact/teact';
import { requestMeasure } from '../../../../lib/fasterdom/fasterdom';
import { getActions } from '../../../../global';
@ -33,6 +35,7 @@ export default function useOuterHandlers(
isContextMenuShown: boolean,
quickReactionRef: RefObject<HTMLDivElement>,
shouldHandleMouseLeave: boolean,
getIsMessageListReady: Signal<boolean>,
) {
const { setReplyingToId, sendDefaultReaction } = getActions();
@ -142,7 +145,7 @@ export default function useOuterHandlers(
}
useEffect(() => {
if (!IS_TOUCH_ENV || isInSelectMode || !canReply || isContextMenuShown) {
if (!IS_TOUCH_ENV || isInSelectMode || !canReply || isContextMenuShown || !getIsMessageListReady()) {
return undefined;
}
@ -176,6 +179,7 @@ export default function useOuterHandlers(
});
}, [
containerRef, isInSelectMode, messageId, setReplyingToId, markSwiped, unmarkSwiped, canReply, isContextMenuShown,
getIsMessageListReady,
]);
function handleMouseLeave(e: React.MouseEvent<HTMLDivElement>) {

View File

@ -1,4 +1,5 @@
import type { RefObject } from 'react';
import type { Signal } from '../util/signals';
import { useState, useEffect } from '../lib/teact/teact';
import { addExtraClass, removeExtraClass } from '../lib/teact/teact-dom';
import { requestMutation } from '../lib/fasterdom/fasterdom';
@ -23,6 +24,7 @@ const useContextMenuHandlers = (
isMenuDisabled?: boolean,
shouldDisableOnLink?: boolean,
shouldDisableOnLongTap?: boolean,
getIsReady?: Signal<boolean>,
) => {
const [isContextMenuOpen, setIsContextMenuOpen] = useState(false);
const [contextMenuPosition, setContextMenuPosition] = useState<IAnchorPosition | undefined>(undefined);
@ -65,7 +67,7 @@ const useContextMenuHandlers = (
// Support context menu on touch devices
useEffect(() => {
if (isMenuDisabled || !IS_TOUCH_ENV || shouldDisableOnLongTap) {
if (isMenuDisabled || !IS_TOUCH_ENV || shouldDisableOnLongTap || (getIsReady && !getIsReady())) {
return undefined;
}
@ -149,7 +151,9 @@ const useContextMenuHandlers = (
element.removeEventListener('touchend', clearLongPressTimer, true);
element.removeEventListener('touchmove', clearLongPressTimer);
};
}, [contextMenuPosition, isMenuDisabled, shouldDisableOnLongTap, elementRef, shouldDisableOnLink]);
}, [
contextMenuPosition, isMenuDisabled, shouldDisableOnLongTap, elementRef, shouldDisableOnLink, getIsReady,
]);
return {
isContextMenuOpen,