Message List: Prevent scroll jumps when selecting (again)

This commit is contained in:
Alexander Zinchuk 2021-08-21 13:33:26 +03:00
parent 2d7b28401f
commit 9170dc1863

View File

@ -333,23 +333,30 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
}, FOCUSING_DURATION); }, FOCUSING_DURATION);
} }
const { scrollTop, scrollHeight, offsetHeight } = container;
const scrollOffset = scrollOffsetRef.current!;
const lastItemElement = listItemElementsRef.current[listItemElementsRef.current.length - 1];
const isAtBottom = isViewportNewest && prevIsViewportNewest && (
scrollOffset - (prevContainerHeight || offsetHeight) <= BOTTOM_THRESHOLD
);
let newScrollTop!: number;
const hasFirstMessageChanged = messageIds && prevMessageIds && messageIds[0] !== prevMessageIds[0]; const hasFirstMessageChanged = messageIds && prevMessageIds && messageIds[0] !== prevMessageIds[0];
const hasLastMessageChanged = ( const hasLastMessageChanged = (
messageIds && prevMessageIds && messageIds[messageIds.length - 1] !== prevMessageIds[prevMessageIds.length - 1] messageIds && prevMessageIds && messageIds[messageIds.length - 1] !== prevMessageIds[prevMessageIds.length - 1]
); );
const wasMessageAdded = hasLastMessageChanged && !hasFirstMessageChanged;
const isAlreadyFocusing = messageIds && memoFocusingIdRef.current === messageIds[messageIds.length - 1]; const isAlreadyFocusing = messageIds && memoFocusingIdRef.current === messageIds[messageIds.length - 1];
if (isAtBottom && hasLastMessageChanged && !hasFirstMessageChanged && !isAlreadyFocusing) { const { scrollTop, scrollHeight, offsetHeight } = container;
const scrollOffset = scrollOffsetRef.current!;
const lastItemElement = listItemElementsRef.current[listItemElementsRef.current.length - 1];
let bottomOffset = scrollOffset - (prevContainerHeight || offsetHeight);
if (wasMessageAdded) {
// If two new messages come at once (e.g. when bot responds) then the first message will update `scrollOffset`
// right away (before animation) which creates inconsistency until the animation completes. To workaround that,
// we calculate `isAtBottom` with a "buffer" of the latest message height (this is approximate).
const lastItemHeight = lastItemElement ? lastItemElement.offsetHeight : 0;
bottomOffset -= lastItemHeight;
}
const isAtBottom = isViewportNewest && prevIsViewportNewest && bottomOffset <= BOTTOM_THRESHOLD;
let newScrollTop!: number;
if (wasMessageAdded && isAtBottom && !isAlreadyFocusing) {
if (lastItemElement) { if (lastItemElement) {
fastRaf(() => { fastRaf(() => {
fastSmoothScroll( fastSmoothScroll(
@ -431,10 +438,12 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
const withUsers = Boolean((!isPrivate && !isChannelChat) || isChatWithSelf); const withUsers = Boolean((!isPrivate && !isChannelChat) || isChatWithSelf);
const noAvatars = Boolean(!withUsers || isChannelChat); const noAvatars = Boolean(!withUsers || isChannelChat);
const shouldRenderGreeting = isChatPrivate(chatId) && !isChatWithSelf && !isBot const shouldRenderGreeting = isChatPrivate(chatId) && !isChatWithSelf && !isBot
&& (( && (
(
!messageGroups && !lastMessage && messageIds !messageGroups && !lastMessage && messageIds
// Used to avoid flickering when deleting a greeting that has just been sent // Used to avoid flickering when deleting a greeting that has just been sent
&& (!listItemElementsRef.current || listItemElementsRef.current.length === 0)) && (!listItemElementsRef.current || listItemElementsRef.current.length === 0)
)
|| checkSingleMessageActionByType('contactSignUp', messageGroups) || checkSingleMessageActionByType('contactSignUp', messageGroups)
|| (lastMessage && lastMessage.content.action && lastMessage.content.action.type === 'contactSignUp') || (lastMessage && lastMessage.content.action && lastMessage.content.action.type === 'contactSignUp')
); );