Only animate typing when viewing conversation
This commit is contained in:
parent
c9af8d3ce2
commit
69c0cad14c
3 changed files with 59 additions and 26 deletions
|
@ -1384,6 +1384,11 @@ $message-padding-horizontal: 12px;
|
|||
&--with-reactions {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
&--typing {
|
||||
flex-direction: row-reverse;
|
||||
overflow-y: clip;
|
||||
}
|
||||
}
|
||||
|
||||
.module-message__container-outer--typing-bubble {
|
||||
|
@ -1391,16 +1396,8 @@ $message-padding-horizontal: 12px;
|
|||
}
|
||||
|
||||
.module-message__typing-avatar-container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: center;
|
||||
margin-inline-end: 8px;
|
||||
overflow-y: clip;
|
||||
|
||||
&--with-reactions {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.module-message__typing-avatar {
|
||||
|
@ -2848,6 +2845,10 @@ button.module-image__border-overlay:focus {
|
|||
padding-inline: 1px;
|
||||
}
|
||||
|
||||
.module-message__typing-animation-container .module-typing-animation {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.module-typing-animation__dot {
|
||||
border-radius: 50%;
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ export type TypingBubblePropsType = {
|
|||
|
||||
const SPRING_CONFIG = {
|
||||
mass: 1,
|
||||
tension: 986,
|
||||
friction: 64,
|
||||
tension: 439,
|
||||
friction: 42,
|
||||
precision: 0,
|
||||
velocity: 0,
|
||||
};
|
||||
|
@ -53,17 +53,15 @@ const SPRING_CONFIG = {
|
|||
const AVATAR_ANIMATION_PROPS: Record<'visible' | 'hidden', object> = {
|
||||
visible: {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
width: '28px',
|
||||
x: '0px',
|
||||
top: '0px',
|
||||
},
|
||||
hidden: {
|
||||
opacity: 0.5,
|
||||
scale: 0.5,
|
||||
width: '4px', // Match value of module-message__typing-avatar margin-inline-start
|
||||
x: '14px',
|
||||
top: '30px',
|
||||
top: '34px',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -71,6 +69,7 @@ function TypingBubbleAvatar({
|
|||
conversationId,
|
||||
contact,
|
||||
visible,
|
||||
shouldAnimate,
|
||||
getPreferredBadge,
|
||||
onContactExit,
|
||||
showContactModal,
|
||||
|
@ -80,6 +79,7 @@ function TypingBubbleAvatar({
|
|||
conversationId: string;
|
||||
contact: TypingContactType | undefined;
|
||||
visible: boolean;
|
||||
shouldAnimate: boolean;
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
onContactExit: (id: string | undefined) => void;
|
||||
showContactModal: (contactId: string, conversationId?: string) => void;
|
||||
|
@ -89,7 +89,9 @@ function TypingBubbleAvatar({
|
|||
const [springProps, springApi] = useSpring(
|
||||
{
|
||||
config: SPRING_CONFIG,
|
||||
from: AVATAR_ANIMATION_PROPS[visible ? 'hidden' : 'visible'],
|
||||
from: shouldAnimate
|
||||
? AVATAR_ANIMATION_PROPS[visible ? 'hidden' : 'visible']
|
||||
: {},
|
||||
to: AVATAR_ANIMATION_PROPS[visible ? 'visible' : 'hidden'],
|
||||
onRest: () => {
|
||||
if (!visible) {
|
||||
|
@ -138,6 +140,7 @@ function TypingBubbleAvatar({
|
|||
function TypingBubbleGroupAvatars({
|
||||
conversationId,
|
||||
typingContactIds,
|
||||
shouldAnimate,
|
||||
getConversation,
|
||||
getPreferredBadge,
|
||||
showContactModal,
|
||||
|
@ -153,6 +156,7 @@ function TypingBubbleGroupAvatars({
|
|||
| 'theme'
|
||||
> & {
|
||||
typingContactIds: ReadonlyArray<string>;
|
||||
shouldAnimate: boolean;
|
||||
}): ReactElement {
|
||||
const [allContactsById, setAllContactsById] = useState<
|
||||
Map<string, TypingContactType>
|
||||
|
@ -195,7 +199,7 @@ function TypingBubbleGroupAvatars({
|
|||
|
||||
// Avatars are rendered Right-to-Left so the leftmost avatars can render on top.
|
||||
return (
|
||||
<div className="module-message__typing-avatar-container">
|
||||
<div className="module-message__author-avatar-container module-message__author-avatar-container--typing">
|
||||
{typingContactsOverflowCount > 0 && (
|
||||
<div
|
||||
className="module-message__typing-avatar module-message__typing-avatar--overflow-count
|
||||
|
@ -228,6 +232,7 @@ function TypingBubbleGroupAvatars({
|
|||
i18n={i18n}
|
||||
theme={theme}
|
||||
visible={visibleContactIds.has(contactId)}
|
||||
shouldAnimate={shouldAnimate}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
@ -241,12 +246,10 @@ const OUTER_DIV_ANIMATION_PROPS: Record<'visible' | 'hidden', object> = {
|
|||
const BUBBLE_ANIMATION_PROPS: Record<'visible' | 'hidden', object> = {
|
||||
visible: {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
top: '0px',
|
||||
},
|
||||
hidden: {
|
||||
opacity: 0.5,
|
||||
scale: 0.5,
|
||||
top: '30px',
|
||||
},
|
||||
};
|
||||
|
@ -269,13 +272,17 @@ export function TypingBubble({
|
|||
() => Object.keys(typingContactIdTimestamps),
|
||||
[typingContactIdTimestamps]
|
||||
);
|
||||
const [shouldAnimate, setShouldAnimate] = useState(false);
|
||||
const prevTypingContactIds = React.useRef<
|
||||
ReadonlyArray<string> | undefined
|
||||
>();
|
||||
const isSomeoneTyping = useMemo(
|
||||
() => typingContactIds.length > 0,
|
||||
[typingContactIds]
|
||||
);
|
||||
|
||||
const [outerDivStyle, outerDivSpringApi] = useSpring(
|
||||
{
|
||||
from: OUTER_DIV_ANIMATION_PROPS[isSomeoneTyping ? 'hidden' : 'visible'],
|
||||
to: OUTER_DIV_ANIMATION_PROPS[isSomeoneTyping ? 'visible' : 'hidden'],
|
||||
config: SPRING_CONFIG,
|
||||
},
|
||||
|
@ -283,7 +290,6 @@ export function TypingBubble({
|
|||
);
|
||||
const [typingAnimationStyle, typingAnimationSpringApi] = useSpring(
|
||||
{
|
||||
from: BUBBLE_ANIMATION_PROPS[isSomeoneTyping ? 'hidden' : 'visible'],
|
||||
to: BUBBLE_ANIMATION_PROPS[isSomeoneTyping ? 'visible' : 'hidden'],
|
||||
config: SPRING_CONFIG,
|
||||
onRest: () => {
|
||||
|
@ -336,6 +342,23 @@ export function TypingBubble({
|
|||
typingContactIdTimestamps,
|
||||
]);
|
||||
|
||||
// Only animate when the user observes a change in typing contacts, not when first
|
||||
// switching to a conversation.
|
||||
useEffect(() => {
|
||||
if (shouldAnimate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!prevTypingContactIds.current) {
|
||||
prevTypingContactIds.current = typingContactIds;
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevTypingContactIds.current !== typingContactIds) {
|
||||
setShouldAnimate(true);
|
||||
}
|
||||
}, [shouldAnimate, typingContactIds]);
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
@ -360,6 +383,7 @@ export function TypingBubble({
|
|||
<TypingBubbleGroupAvatars
|
||||
conversationId={conversationId}
|
||||
typingContactIds={typingContactIds}
|
||||
shouldAnimate={shouldAnimate}
|
||||
getConversation={getConversation}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
showContactModal={showContactModal}
|
||||
|
|
|
@ -2688,6 +2688,14 @@
|
|||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2022-11-03T14:21:47.456Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/components/conversation/TypingBubble.tsx",
|
||||
"line": " const prevTypingContactIds = React.useRef<",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-09-28T21:48:57.488Z",
|
||||
"reasonDetail": "Used to track change of typing contacts while a conversation is actively viewed."
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/components/conversation/WaveformScrubber.tsx",
|
||||
|
@ -2804,6 +2812,13 @@
|
|||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2021-10-22T00:52:39.251Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/hooks/useScrollLock.tsx",
|
||||
"line": " const onUserInterruptRef = useRef(onUserInterrupt);",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-09-19T17:05:51.321Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/hooks/useSizeObserver.tsx",
|
||||
|
@ -2853,13 +2868,6 @@
|
|||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-07-25T21:55:26.191Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/hooks/useScrollLock.tsx",
|
||||
"line": " const onUserInterruptRef = useRef(onUserInterrupt);",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-09-19T17:05:51.321Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/quill/formatting/menu.tsx",
|
||||
|
|
Loading…
Reference in a new issue