Message List: Fixes for appearance animation
This commit is contained in:
parent
5d0af382ec
commit
76c2ff41ec
@ -70,6 +70,21 @@
|
|||||||
.ActionMessage {
|
.ActionMessage {
|
||||||
position: relative;
|
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 {
|
&::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@ -39,7 +39,7 @@ import {
|
|||||||
pick,
|
pick,
|
||||||
} from '../../util/iteratees';
|
} from '../../util/iteratees';
|
||||||
import {
|
import {
|
||||||
fastRaf, debounce, throttleWithTickEnd,
|
fastRaf, debounce, throttleWithTickEnd, onTickEnd,
|
||||||
} from '../../util/schedulers';
|
} from '../../util/schedulers';
|
||||||
import { formatHumanDate } from '../../util/dateFormat';
|
import { formatHumanDate } from '../../util/dateFormat';
|
||||||
import useLayoutEffectWithPrevDeps from '../../hooks/useLayoutEffectWithPrevDeps';
|
import useLayoutEffectWithPrevDeps from '../../hooks/useLayoutEffectWithPrevDeps';
|
||||||
@ -143,12 +143,14 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const scrollOffsetRef = useRef<number>();
|
// We update local cached `scrollOffsetRef` when opening chat.
|
||||||
|
// Then we update global version every second on scrolling.
|
||||||
|
const scrollOffsetRef = useRef<number>((type === 'thread' && selectScrollOffset(getGlobal(), chatId, threadId)) || 0);
|
||||||
const anchorIdRef = useRef<string>();
|
const anchorIdRef = useRef<string>();
|
||||||
const anchorTopRef = useRef<number>();
|
const anchorTopRef = useRef<number>();
|
||||||
const listItemElementsRef = useRef<HTMLDivElement[]>();
|
const listItemElementsRef = useRef<HTMLDivElement[]>();
|
||||||
// Updated when opening chat (to preserve divider even after messages are read)
|
// Updated when opening chat (to preserve divider even after messages are read)
|
||||||
const memoUnreadDividerBeforeIdRef = useRef<number>();
|
const memoUnreadDividerBeforeIdRef = useRef<number | undefined>(firstUnreadId);
|
||||||
// Updated every time (to be used from intersection callback closure)
|
// Updated every time (to be used from intersection callback closure)
|
||||||
const memoFirstUnreadIdRef = useRef<number>();
|
const memoFirstUnreadIdRef = useRef<number>();
|
||||||
const memoFocusingIdRef = useRef<number>();
|
const memoFocusingIdRef = useRef<number>();
|
||||||
@ -158,21 +160,15 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
const [containerHeight, setContainerHeight] = useState<number | undefined>();
|
const [containerHeight, setContainerHeight] = useState<number | undefined>();
|
||||||
const [hasFocusing, setHasFocusing] = useState<boolean>(Boolean(focusingId));
|
const [hasFocusing, setHasFocusing] = useState<boolean>(Boolean(focusingId));
|
||||||
|
|
||||||
|
const areMessagesLoaded = Boolean(messageIds);
|
||||||
useOnChange(() => {
|
useOnChange(() => {
|
||||||
anchorIdRef.current = undefined;
|
// We only need it first time when message list appears
|
||||||
|
if (areMessagesLoaded) {
|
||||||
memoUnreadDividerBeforeIdRef.current = firstUnreadId;
|
onTickEnd(() => {
|
||||||
|
shouldAnimateAppearanceRef.current = false;
|
||||||
// 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;
|
}, [areMessagesLoaded]);
|
||||||
|
|
||||||
// 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)]);
|
|
||||||
|
|
||||||
useOnChange(() => {
|
useOnChange(() => {
|
||||||
memoFirstUnreadIdRef.current = firstUnreadId;
|
memoFirstUnreadIdRef.current = firstUnreadId;
|
||||||
@ -503,12 +499,13 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
) : botDescription ? (
|
) : botDescription ? (
|
||||||
<div className="empty rich"><span>{renderText(lang(botDescription), ['br', 'emoji', 'links'])}</span></div>
|
<div className="empty rich"><span>{renderText(lang(botDescription), ['br', 'emoji', 'links'])}</span></div>
|
||||||
) : messageIds && messageGroups ? (
|
) : messageIds && !messageGroups ? (
|
||||||
// @ts-ignore
|
<div className="empty"><span>{lang('NoMessages')}</span></div>
|
||||||
|
) : ((messageIds && messageGroups) || lastMessage) ? (
|
||||||
<MessageScroll
|
<MessageScroll
|
||||||
containerRef={containerRef}
|
containerRef={containerRef}
|
||||||
className="messages-container"
|
className="messages-container"
|
||||||
messageIds={messageIds}
|
messageIds={messageIds || [lastMessage!.id]}
|
||||||
containerHeight={containerHeight}
|
containerHeight={containerHeight}
|
||||||
listItemElementsRef={listItemElementsRef}
|
listItemElementsRef={listItemElementsRef}
|
||||||
focusingId={focusingId}
|
focusingId={focusingId}
|
||||||
@ -520,32 +517,9 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
firstUnreadId={firstUnreadId}
|
firstUnreadId={firstUnreadId}
|
||||||
onFabToggle={onFabToggle}
|
onFabToggle={onFabToggle}
|
||||||
>
|
>
|
||||||
{messageGroups && renderMessages(
|
|
||||||
lang,
|
|
||||||
messageGroups,
|
|
||||||
shouldAnimateAppearanceRef.current ? messageIds.length : 0,
|
|
||||||
observeIntersectionForReading,
|
|
||||||
observeIntersectionForMedia,
|
|
||||||
observeIntersectionForAnimatedStickers,
|
|
||||||
withUsers,
|
|
||||||
anchorIdRef,
|
|
||||||
memoUnreadDividerBeforeIdRef,
|
|
||||||
threadId,
|
|
||||||
type,
|
|
||||||
threadTopMessageId,
|
|
||||||
threadFirstMessageId,
|
|
||||||
hasLinkedChat,
|
|
||||||
type === 'scheduled',
|
|
||||||
)}
|
|
||||||
</MessageScroll>
|
|
||||||
) : messageIds ? (
|
|
||||||
<div className="empty"><span>{lang('NoMessages')}</span></div>
|
|
||||||
) : lastMessage ? (
|
|
||||||
<div className="messages-container">
|
|
||||||
{renderMessages(
|
{renderMessages(
|
||||||
lang,
|
lang,
|
||||||
groupMessages([lastMessage]),
|
messageGroups || groupMessages([lastMessage!]),
|
||||||
0,
|
|
||||||
observeIntersectionForReading,
|
observeIntersectionForReading,
|
||||||
observeIntersectionForMedia,
|
observeIntersectionForMedia,
|
||||||
observeIntersectionForAnimatedStickers,
|
observeIntersectionForAnimatedStickers,
|
||||||
@ -557,9 +531,10 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
threadTopMessageId,
|
threadTopMessageId,
|
||||||
threadFirstMessageId,
|
threadFirstMessageId,
|
||||||
hasLinkedChat,
|
hasLinkedChat,
|
||||||
false,
|
messageGroups ? type === 'scheduled' : false,
|
||||||
|
!messageGroups || !shouldAnimateAppearanceRef.current,
|
||||||
)}
|
)}
|
||||||
</div>
|
</MessageScroll>
|
||||||
) : (
|
) : (
|
||||||
<Loading color="white" />
|
<Loading color="white" />
|
||||||
)}
|
)}
|
||||||
@ -570,7 +545,6 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
function renderMessages(
|
function renderMessages(
|
||||||
lang: LangFn,
|
lang: LangFn,
|
||||||
messageGroups: MessageDateGroup[],
|
messageGroups: MessageDateGroup[],
|
||||||
messageCountToAnimate: number,
|
|
||||||
observeIntersectionForReading: ObserveFn,
|
observeIntersectionForReading: ObserveFn,
|
||||||
observeIntersectionForMedia: ObserveFn,
|
observeIntersectionForMedia: ObserveFn,
|
||||||
observeIntersectionForAnimatedStickers: ObserveFn,
|
observeIntersectionForAnimatedStickers: ObserveFn,
|
||||||
@ -583,6 +557,7 @@ function renderMessages(
|
|||||||
threadFirstMessageId?: number,
|
threadFirstMessageId?: number,
|
||||||
hasLinkedChat?: boolean,
|
hasLinkedChat?: boolean,
|
||||||
isSchedule = false,
|
isSchedule = false,
|
||||||
|
noAppearanceAnimation = false,
|
||||||
) {
|
) {
|
||||||
const unreadDivider = (
|
const unreadDivider = (
|
||||||
<div className={buildClassName(UNREAD_DIVIDER_CLASS, 'local-action-message')} key="unread-messages">
|
<div className={buildClassName(UNREAD_DIVIDER_CLASS, 'local-action-message')} key="unread-messages">
|
||||||
@ -590,6 +565,9 @@ function renderMessages(
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const messageCountToAnimate = noAppearanceAnimation ? 0 : messageGroups.reduce((acc, messageGroup) => {
|
||||||
|
return acc + flatten(messageGroup.senderGroups).length;
|
||||||
|
}, 0);
|
||||||
let appearanceIndex = 0;
|
let appearanceIndex = 0;
|
||||||
|
|
||||||
const dateGroups = messageGroups.map((
|
const dateGroups = messageGroups.map((
|
||||||
|
|||||||
@ -19,21 +19,6 @@
|
|||||||
--select-message-scale: 0.9;
|
--select-message-scale: 0.9;
|
||||||
--select-background-color: white;
|
--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,
|
> .Avatar,
|
||||||
> .message-content-wrapper {
|
> .message-content-wrapper {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user