Composer Embedded Message: Fix closing animation (#6634)

This commit is contained in:
Alexander Zinchuk 2026-01-20 12:01:22 +01:00
parent c2eb1abd3c
commit a662e58e78
2 changed files with 124 additions and 69 deletions

View File

@ -31,6 +31,7 @@ import { unique } from '../../../util/iteratees';
import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers';
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
import useFrozenProps from '../../../hooks/useFrozenProps';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
@ -81,33 +82,21 @@ type OwnProps = {
const CLOSE_DURATION = 350;
const ComposerEmbeddedMessage = ({
replyInfo,
suggestedPostInfo,
editingId,
message,
sender,
shouldAnimate,
forwardedMessagesCount,
noAuthors,
noCaptions,
forwardsHaveCaptions,
shouldForceShowEditing,
isCurrentUserPremium,
isContextMenuDisabled,
isReplyToDiscussion,
isInChangingRecipientMode,
shouldPreventComposerAnimation,
senderChat,
chatId,
currentUserId,
isSenderChannel,
forwardMessageIds,
fromChatId,
isMediaNsfw,
theme,
onClear,
}: OwnProps & StateProps) => {
const ComposerEmbeddedMessage = (props: OwnProps & StateProps) => {
const {
shouldAnimate,
isReplyToDiscussion,
isInChangingRecipientMode,
forwardMessageIds,
fromChatId,
replyInfo,
editingId,
suggestedPostInfo,
shouldForceShowEditing,
message,
forwardedMessagesCount,
} = props;
const {
resetDraftReplyInfo,
resetDraftSuggestedPostInfo,
@ -127,19 +116,16 @@ const ComposerEmbeddedMessage = ({
const lang = useLang();
const isReplyToTopicStart = message?.content.action?.type === 'topicCreate';
const isShowingReply = replyInfo && !shouldForceShowEditing;
const isReplyWithQuote = Boolean(replyInfo?.quoteText);
const isShowingSuggestedPost = Boolean(suggestedPostInfo) && !shouldForceShowEditing;
const isForwarding = Boolean(forwardedMessagesCount);
const selectSenderFromForwardedMessage = useLastCallback((forwardedMessage: ApiMessage) => {
const global = getGlobal();
sender = selectForwardedSender(global, forwardedMessage);
if (!sender) {
sender = selectSender(global, forwardedMessage);
let localSender = selectForwardedSender(global, forwardedMessage);
if (!localSender) {
localSender = selectSender(global, forwardedMessage);
}
return sender;
return localSender;
});
const forwardSenders = useMemo(() => {
@ -159,7 +145,7 @@ const ComposerEmbeddedMessage = ({
})();
const {
shouldRender, transitionClassNames,
shouldRender, transitionClassNames, isClosing,
} = useShowTransitionDeprecated(
isShown && !isReplyToTopicStart && !isReplyToDiscussion,
undefined,
@ -169,6 +155,35 @@ const ComposerEmbeddedMessage = ({
CLOSE_DURATION,
!shouldAnimate,
);
const {
chatId,
currentUserId,
theme,
onClear,
isCurrentUserPremium,
isContextMenuDisabled,
shouldPreventComposerAnimation,
sender,
senderChat,
isMediaNsfw,
noAuthors,
noCaptions,
forwardsHaveCaptions,
forwardedMessagesCount: frozenForwardedMessagesCount,
message: frozenMessage,
shouldForceShowEditing: frozenShouldForceShowEditing,
suggestedPostInfo: frozenSuggestedPostInfo,
replyInfo: frozenReplyInfo,
editingId: frozenEditingId,
isSenderChannel,
} = useFrozenProps(props, isClosing);
const isForwardingRendering = Boolean(frozenForwardedMessagesCount);
const isShowingReplyRendering = Boolean(frozenReplyInfo) && !frozenShouldForceShowEditing;
const isReplyWithQuoteRendering = Boolean(frozenReplyInfo?.quoteText);
const isShowingSuggestedPostRendering = Boolean(frozenSuggestedPostInfo) && !frozenShouldForceShowEditing;
useEffect(() => {
if (shouldPreventComposerAnimation) {
setShouldPreventComposerAnimation({ shouldPreventComposerAnimation: false });
@ -176,14 +191,14 @@ const ComposerEmbeddedMessage = ({
});
const clearEmbedded = useLastCallback(() => {
if (editingId) {
if (frozenEditingId) {
setEditingId({ messageId: undefined });
} else if (forwardedMessagesCount) {
} else if (frozenForwardedMessagesCount) {
exitForwardMode();
} else if (isShowingSuggestedPost) {
} else if (isShowingSuggestedPostRendering) {
resetDraftSuggestedPostInfo();
resetDraftReplyInfo();
} else if (replyInfo && !shouldForceShowEditing) {
} else if (frozenReplyInfo && !frozenShouldForceShowEditing) {
resetDraftReplyInfo();
}
onClear?.();
@ -197,10 +212,10 @@ const ComposerEmbeddedMessage = ({
} = useContextMenuHandlers(ref);
const focusMessageFromDraft = () => {
focusMessage({ chatId: message!.chatId, messageId: message!.id, noForumTopicPanel: true });
focusMessage({ chatId: frozenMessage!.chatId, messageId: frozenMessage!.id, noForumTopicPanel: true });
};
const handleMessageClick = useLastCallback((e: React.MouseEvent): void => {
if (suggestedPostInfo) {
if (frozenSuggestedPostInfo) {
openSuggestMessageModal({ chatId });
return;
}
@ -231,6 +246,13 @@ const ComposerEmbeddedMessage = ({
});
const handleDoNotReplyClick = useLastCallback(buildAutoCloseMenuItemHandler(clearEmbedded));
const handleIconKeyDown = useLastCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleContextMenu(e as unknown as React.MouseEvent);
}
});
const getTriggerElement = useLastCallback(() => ref.current);
const getRootElement = useLastCallback(() => ref.current!);
const getMenuElement = useLastCallback(() => ref.current!.querySelector('.forward-context-menu .bubble'));
@ -243,47 +265,47 @@ const ComposerEmbeddedMessage = ({
}, [handleContextMenuClose, handleContextMenuHide, shouldRender]);
const className = buildClassName('ComposerEmbeddedMessage', transitionClassNames);
const renderingSender = useCurrentOrPrev(sender, true);
const { className: peerColorClass, style: peerColorStyle } = usePeerColor({
peer: renderingSender,
peer: sender,
theme,
});
const innerClassName = buildClassName('ComposerEmbeddedMessage_inner', peerColorClass);
const leftIcon = useMemo(() => {
if (editingId) {
if (frozenEditingId) {
return 'edit';
}
if (isShowingSuggestedPost) {
if (isShowingSuggestedPostRendering) {
return 'cash-circle';
}
if (isForwarding) {
if (isForwardingRendering) {
return 'forward';
}
if (isShowingReply) {
if (isShowingReplyRendering) {
return 'reply';
}
return undefined;
}, [editingId, isForwarding, isShowingReply, isShowingSuggestedPost]);
}, [frozenEditingId, isForwardingRendering, isShowingReplyRendering, isShowingSuggestedPostRendering]);
const customText = forwardedMessagesCount && forwardedMessagesCount > 1
? oldLang('ForwardedMessageCount', forwardedMessagesCount)
const customText = frozenForwardedMessagesCount && frozenForwardedMessagesCount > 1
? oldLang('ForwardedMessageCount', frozenForwardedMessagesCount)
: undefined;
const strippedMessage = useMemo(() => {
if (!message || !isForwarding || !message.content.text || !noAuthors || isCurrentUserPremium) return message;
if (!frozenMessage || !isForwardingRendering || !frozenMessage.content.text
|| !noAuthors || isCurrentUserPremium) return frozenMessage;
const strippedText = stripCustomEmoji(message.content.text);
const strippedText = stripCustomEmoji(frozenMessage.content.text);
return {
...message,
...frozenMessage,
content: {
...message.content,
...frozenMessage.content,
text: strippedText,
},
};
}, [isCurrentUserPremium, isForwarding, message, noAuthors]);
}, [isCurrentUserPremium, isForwardingRendering, frozenMessage, noAuthors]);
const renderingLeftIcon = useCurrentOrPrev(leftIcon, true);
@ -291,22 +313,29 @@ const ComposerEmbeddedMessage = ({
return undefined;
}
const canReplyInSenderChat = sender && !isSenderChannel && chatId !== sender.id && sender.id !== currentUserId;
const canReplyInSenderChat = sender && !isSenderChannel
&& chatId !== sender.id && sender.id !== currentUserId;
return (
<div className={className} ref={ref} onContextMenu={handleContextMenu}>
<div className={innerClassName} style={peerColorStyle}>
<div className="embedded-left-icon" onClick={handleContextMenu}>
<div
className="embedded-left-icon"
role="button"
tabIndex={0}
onClick={handleContextMenu}
onKeyDown={handleIconKeyDown}
>
{renderingLeftIcon && <Icon name={renderingLeftIcon} />}
{Boolean(replyInfo?.quoteText) && (
{Boolean(frozenReplyInfo?.quoteText) && (
<Icon name="quote" className="quote-reply" />
)}
</div>
<EmbeddedMessage
isOpen={isShown}
className="inside-input"
replyInfo={replyInfo}
suggestedPostInfo={suggestedPostInfo}
replyInfo={frozenReplyInfo}
suggestedPostInfo={frozenSuggestedPostInfo}
isMediaNsfw={isMediaNsfw}
isInComposer
message={strippedMessage}
@ -314,7 +343,7 @@ const ComposerEmbeddedMessage = ({
composerForwardSenders={forwardSenders}
customText={customText}
noCaptions={noCaptions}
title={(editingId && !isShowingReply) ? oldLang('EditMessage')
title={(frozenEditingId && !isShowingReplyRendering) ? oldLang('EditMessage')
: noAuthors ? oldLang('HiddenSendersNameDescription') : undefined}
onClick={handleMessageClick}
senderChat={senderChat}
@ -328,7 +357,7 @@ const ComposerEmbeddedMessage = ({
onClick={handleClearClick}
iconName="close"
/>
{(isShowingReply || isForwarding) && !isContextMenuDisabled && (
{(isShowingReplyRendering || isForwardingRendering) && !isContextMenuDisabled && (
<Menu
isOpen={isContextMenuOpen}
anchor={contextMenuAnchor}
@ -339,7 +368,7 @@ const ComposerEmbeddedMessage = ({
onClose={handleContextMenuClose}
onCloseAnimationEnd={handleContextMenuHide}
>
{isForwarding && (
{isForwardingRendering && (
<>
<MenuItem
icon={!noAuthors ? 'message-succeeded' : undefined}
@ -349,7 +378,7 @@ const ComposerEmbeddedMessage = ({
noAuthors: false,
})}
>
{oldLang(forwardedMessagesCount > 1 ? 'ShowSenderNames' : 'ShowSendersName')}
{oldLang(frozenForwardedMessagesCount > 1 ? 'ShowSenderNames' : 'ShowSendersName')}
</MenuItem>
<MenuItem
icon={noAuthors ? 'message-succeeded' : undefined}
@ -359,7 +388,7 @@ const ComposerEmbeddedMessage = ({
noAuthors: true,
})}
>
{oldLang(forwardedMessagesCount > 1 ? 'HideSenderNames' : 'HideSendersName')}
{oldLang(frozenForwardedMessagesCount > 1 ? 'HideSenderNames' : 'HideSendersName')}
</MenuItem>
{forwardsHaveCaptions && (
<>
@ -372,7 +401,8 @@ const ComposerEmbeddedMessage = ({
noCaptions: false,
})}
>
{oldLang(forwardedMessagesCount > 1 ? 'Conversation.ForwardOptions.ShowCaption' : 'ShowCaption')}
{oldLang(frozenForwardedMessagesCount > 1
? 'Conversation.ForwardOptions.ShowCaption' : 'ShowCaption')}
</MenuItem>
<MenuItem
icon={noCaptions ? 'message-succeeded' : undefined}
@ -382,7 +412,8 @@ const ComposerEmbeddedMessage = ({
noCaptions: true,
})}
>
{oldLang(forwardedMessagesCount > 1 ? 'Conversation.ForwardOptions.HideCaption' : 'HideCaption')}
{oldLang(frozenForwardedMessagesCount > 1
? 'Conversation.ForwardOptions.HideCaption' : 'HideCaption')}
</MenuItem>
</>
)}
@ -392,7 +423,7 @@ const ComposerEmbeddedMessage = ({
</MenuItem>
</>
)}
{isShowingReply && (
{isShowingReplyRendering && (
<>
<MenuItem
icon="show-message"
@ -400,7 +431,7 @@ const ComposerEmbeddedMessage = ({
>
{oldLang('Message.Context.Goto')}
</MenuItem>
{isReplyWithQuote && (
{isReplyWithQuoteRendering && (
<MenuItem
icon="remove-quote"
onClick={handleRemoveQuoteClick}

View File

@ -0,0 +1,24 @@
import { useRef } from '../lib/teact/teact';
export default function useFrozenProps<T extends Record<string, unknown>>(
props: T,
shouldFreeze: boolean,
alwaysFreshKeys?: readonly (keyof T)[],
): T {
const frozenRef = useRef<T>(props);
if (!shouldFreeze) {
frozenRef.current = props;
} else if (alwaysFreshKeys?.length) {
const updates: Partial<T> = {};
for (const key of alwaysFreshKeys) {
updates[key] = props[key];
}
frozenRef.current = {
...frozenRef.current,
...updates,
};
}
return frozenRef.current;
}