From 76c2ff41ec612699e9d291e68c26f5d606833541 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Sat, 24 Apr 2021 15:05:01 +0300 Subject: [PATCH] Message List: Fixes for appearance animation --- src/components/middle/MessageList.scss | 15 +++++ src/components/middle/MessageList.tsx | 72 ++++++++-------------- src/components/middle/message/Message.scss | 15 ----- 3 files changed, 40 insertions(+), 62 deletions(-) diff --git a/src/components/middle/MessageList.scss b/src/components/middle/MessageList.scss index b83c785f..132c11ba 100644 --- a/src/components/middle/MessageList.scss +++ b/src/components/middle/MessageList.scss @@ -70,6 +70,21 @@ .ActionMessage { position: relative; + opacity: 1; + transform: scale(1); + transition: opacity .2s ease, transform .2s ease; + + &:not(.open) { + transform: scale(0.8); + opacity: 0; + } + + // Restore stacking context + // https://developer.mozilla.org/ru/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context + &.open.shown { + transform: none; + } + &::before { content: ""; position: absolute; diff --git a/src/components/middle/MessageList.tsx b/src/components/middle/MessageList.tsx index d0c4edea..9d963a65 100644 --- a/src/components/middle/MessageList.tsx +++ b/src/components/middle/MessageList.tsx @@ -39,7 +39,7 @@ import { pick, } from '../../util/iteratees'; import { - fastRaf, debounce, throttleWithTickEnd, + fastRaf, debounce, throttleWithTickEnd, onTickEnd, } from '../../util/schedulers'; import { formatHumanDate } from '../../util/dateFormat'; import useLayoutEffectWithPrevDeps from '../../hooks/useLayoutEffectWithPrevDeps'; @@ -143,12 +143,14 @@ const MessageList: FC = ({ // eslint-disable-next-line no-null/no-null const containerRef = useRef(null); - const scrollOffsetRef = useRef(); + // We update local cached `scrollOffsetRef` when opening chat. + // Then we update global version every second on scrolling. + const scrollOffsetRef = useRef((type === 'thread' && selectScrollOffset(getGlobal(), chatId, threadId)) || 0); const anchorIdRef = useRef(); const anchorTopRef = useRef(); const listItemElementsRef = useRef(); // Updated when opening chat (to preserve divider even after messages are read) - const memoUnreadDividerBeforeIdRef = useRef(); + const memoUnreadDividerBeforeIdRef = useRef(firstUnreadId); // Updated every time (to be used from intersection callback closure) const memoFirstUnreadIdRef = useRef(); const memoFocusingIdRef = useRef(); @@ -158,21 +160,15 @@ const MessageList: FC = ({ const [containerHeight, setContainerHeight] = useState(); const [hasFocusing, setHasFocusing] = useState(Boolean(focusingId)); + const areMessagesLoaded = Boolean(messageIds); useOnChange(() => { - anchorIdRef.current = undefined; - - memoUnreadDividerBeforeIdRef.current = firstUnreadId; - - // We update local cached `scrollOffsetRef` when opening chat. - // Then we update global version every second on scrolling. - scrollOffsetRef.current = (type === 'thread' && selectScrollOffset(getGlobal(), chatId, threadId)) || 0; - - // We need it just first time when message list appears - // TODO Figure out why `onTickEnd`/100ms is not enough - setTimeout(() => { - shouldAnimateAppearanceRef.current = false; - }, 1000); - }, [Boolean(messageIds)]); + // We only need it first time when message list appears + if (areMessagesLoaded) { + onTickEnd(() => { + shouldAnimateAppearanceRef.current = false; + }); + } + }, [areMessagesLoaded]); useOnChange(() => { memoFirstUnreadIdRef.current = firstUnreadId; @@ -503,12 +499,13 @@ const MessageList: FC = ({ ) : botDescription ? (
{renderText(lang(botDescription), ['br', 'emoji', 'links'])}
- ) : messageIds && messageGroups ? ( - // @ts-ignore + ) : messageIds && !messageGroups ? ( +
{lang('NoMessages')}
+ ) : ((messageIds && messageGroups) || lastMessage) ? ( = ({ firstUnreadId={firstUnreadId} onFabToggle={onFabToggle} > - {messageGroups && renderMessages( - lang, - messageGroups, - shouldAnimateAppearanceRef.current ? messageIds.length : 0, - observeIntersectionForReading, - observeIntersectionForMedia, - observeIntersectionForAnimatedStickers, - withUsers, - anchorIdRef, - memoUnreadDividerBeforeIdRef, - threadId, - type, - threadTopMessageId, - threadFirstMessageId, - hasLinkedChat, - type === 'scheduled', - )} - - ) : messageIds ? ( -
{lang('NoMessages')}
- ) : lastMessage ? ( -
{renderMessages( lang, - groupMessages([lastMessage]), - 0, + messageGroups || groupMessages([lastMessage!]), observeIntersectionForReading, observeIntersectionForMedia, observeIntersectionForAnimatedStickers, @@ -557,9 +531,10 @@ const MessageList: FC = ({ threadTopMessageId, threadFirstMessageId, hasLinkedChat, - false, + messageGroups ? type === 'scheduled' : false, + !messageGroups || !shouldAnimateAppearanceRef.current, )} -
+ ) : ( )} @@ -570,7 +545,6 @@ const MessageList: FC = ({ function renderMessages( lang: LangFn, messageGroups: MessageDateGroup[], - messageCountToAnimate: number, observeIntersectionForReading: ObserveFn, observeIntersectionForMedia: ObserveFn, observeIntersectionForAnimatedStickers: ObserveFn, @@ -583,6 +557,7 @@ function renderMessages( threadFirstMessageId?: number, hasLinkedChat?: boolean, isSchedule = false, + noAppearanceAnimation = false, ) { const unreadDivider = (
@@ -590,6 +565,9 @@ function renderMessages(
); + const messageCountToAnimate = noAppearanceAnimation ? 0 : messageGroups.reduce((acc, messageGroup) => { + return acc + flatten(messageGroup.senderGroups).length; + }, 0); let appearanceIndex = 0; const dateGroups = messageGroups.map(( diff --git a/src/components/middle/message/Message.scss b/src/components/middle/message/Message.scss index 4275b4d4..0d718d7a 100644 --- a/src/components/middle/message/Message.scss +++ b/src/components/middle/message/Message.scss @@ -19,21 +19,6 @@ --select-message-scale: 0.9; --select-background-color: white; - opacity: 1; - transform: scale(1); - transition: opacity .2s ease, transform .2s ease; - - &:not(.open) { - transform: scale(0.8); - opacity: 0; - } - - // Restore stacking context - // https://developer.mozilla.org/ru/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context - &.open.shown { - transform: none; - } - > .Avatar, > .message-content-wrapper { opacity: 1;