diff --git a/src/components/middle/composer/Composer.tsx b/src/components/middle/composer/Composer.tsx index d151b2984..5051136f9 100644 --- a/src/components/middle/composer/Composer.tsx +++ b/src/components/middle/composer/Composer.tsx @@ -54,6 +54,7 @@ import { selectChatType, selectRequestedDraftFiles, selectTabState, + selectReplyingToId, } from '../../../global/selectors'; import { getAllowedAttachmentOptions, @@ -145,6 +146,7 @@ type StateProps = isChatWithBot?: boolean; isChatWithSelf?: boolean; isChannel?: boolean; + replyingToId?: number; isForCurrentMessageList: boolean; isRightColumnShown?: boolean; isSelectModeActive?: boolean; @@ -260,6 +262,7 @@ const Composer: FC = ({ sendAsChat, sendAsId, editingDraft, + replyingToId, requestedDraftText, requestedDraftFiles, botMenuButton, @@ -394,12 +397,6 @@ const Composer: FC = ({ sendMessageAction({ type: 'typing' }); }, [editingMessage, html, sendMessageAction]); - const mainButtonState = editingMessage ? MainButtonState.Edit - : (!IS_VOICE_RECORDING_SUPPORTED || activeVoiceRecording || (html && !attachments.length) || isForwarding) - ? (shouldSchedule ? MainButtonState.Schedule : MainButtonState.Send) - : MainButtonState.Record; - const canShowCustomSendMenu = !shouldSchedule; - const { isMentionTooltipOpen, closeMentionTooltip, insertMention, mentionFilteredUsers, } = useMentionTooltip( @@ -438,13 +435,6 @@ const Composer: FC = ({ chatBotCommands, ); - const { - isContextMenuOpen: isCustomSendMenuOpen, - handleContextMenu, - handleContextMenuClose, - handleContextMenuHide, - } = useContextMenuHandlers(mainButtonRef, !(mainButtonState === MainButtonState.Send && canShowCustomSendMenu)); - const { canSendStickers, canSendGifs, canAttachMedia, canAttachPolls, canAttachEmbedLinks, } = useMemo(() => getAllowedAttachmentOptions(chat, isChatWithBot), [chat, isChatWithBot]); @@ -597,7 +587,7 @@ const Composer: FC = ({ customEmojiNotificationNumber.current = Number(!notificationNumber); }, [currentUserId, lang, showNotification]); - const [handleEditComplete, handleEditCancel] = useEditing( + const [handleEditComplete, handleEditCancel, shouldForceShowEditing] = useEditing( htmlRef, setHtml, editingMessage, @@ -608,7 +598,36 @@ const Composer: FC = ({ messageListType, draft, editingDraft, + replyingToId, ); + + const mainButtonState = useMemo(() => { + if (editingMessage && shouldForceShowEditing) { + return MainButtonState.Edit; + } + + if (IS_VOICE_RECORDING_SUPPORTED && !activeVoiceRecording && !(html && !attachments.length) && !isForwarding) { + return MainButtonState.Record; + } + + if (shouldSchedule) { + return MainButtonState.Schedule; + } + + return MainButtonState.Send; + }, [ + activeVoiceRecording, attachments.length, editingMessage, html, isForwarding, shouldForceShowEditing, + shouldSchedule, + ]); + const canShowCustomSendMenu = !shouldSchedule; + + const { + isContextMenuOpen: isCustomSendMenuOpen, + handleContextMenu, + handleContextMenuClose, + handleContextMenuHide, + } = useContextMenuHandlers(mainButtonRef, !(mainButtonState === MainButtonState.Send && canShowCustomSendMenu)); + useDraft(draft, chatId, threadId, htmlRef, setHtml, editingMessage, lastSyncTime); useClipboardPaste( isForCurrentMessageList, @@ -1264,7 +1283,10 @@ const Composer: FC = ({ />
- + ( ? selectEditingScheduledDraft(global, chatId) : selectEditingDraft(global, chatId, threadId); + const replyingToId = selectReplyingToId(global, chatId, threadId); + const tabState = selectTabState(global); return { editingMessage: selectEditingMessage(global, chatId, threadId, messageListType), connectionState: global.connectionState, + replyingToId, draft: selectDraft(global, chatId, threadId), chat, isChatWithBot, diff --git a/src/components/middle/composer/ComposerEmbeddedMessage.tsx b/src/components/middle/composer/ComposerEmbeddedMessage.tsx index 331588049..654dc4fcc 100644 --- a/src/components/middle/composer/ComposerEmbeddedMessage.tsx +++ b/src/components/middle/composer/ComposerEmbeddedMessage.tsx @@ -54,6 +54,7 @@ type StateProps = { type OwnProps = { onClear?: () => void; + shouldForceShowEditing?: boolean; }; const FORWARD_RENDERING_DELAY = 300; @@ -68,6 +69,7 @@ const ComposerEmbeddedMessage: FC = ({ noAuthors, noCaptions, forwardsHaveCaptions, + shouldForceShowEditing, isCurrentUserPremium, onClear, }) => { @@ -99,7 +101,7 @@ const ComposerEmbeddedMessage: FC = ({ } = useShowTransition(canAnimate && isShown, undefined, !shouldAnimate, undefined, !shouldAnimate); const clearEmbedded = useCallback(() => { - if (replyingToId) { + if (replyingToId && !shouldForceShowEditing) { setReplyingToId({ messageId: undefined }); } else if (editingId) { setEditingId({ messageId: undefined }); @@ -107,7 +109,10 @@ const ComposerEmbeddedMessage: FC = ({ exitForwardMode(); } onClear?.(); - }, [replyingToId, editingId, forwardedMessagesCount, onClear, setReplyingToId, setEditingId, exitForwardMode]); + }, [ + replyingToId, shouldForceShowEditing, editingId, forwardedMessagesCount, onClear, setReplyingToId, setEditingId, + exitForwardMode, + ]); useEffect(() => (isShown ? captureEscKeyListener(clearEmbedded) : undefined), [isShown, clearEmbedded]); @@ -146,7 +151,7 @@ const ComposerEmbeddedMessage: FC = ({ const className = buildClassName('ComposerEmbeddedMessage', transitionClassNames); const leftIcon = useMemo(() => { - if (replyingToId) { + if (replyingToId && !shouldForceShowEditing) { return 'icon-reply'; } if (editingId) { @@ -157,7 +162,7 @@ const ComposerEmbeddedMessage: FC = ({ } return undefined; - }, [editingId, isForwarding, replyingToId]); + }, [editingId, isForwarding, replyingToId, shouldForceShowEditing]); const customText = forwardedMessagesCount && forwardedMessagesCount > 1 ? lang('ForwardedMessageCount', forwardedMessagesCount) @@ -274,7 +279,7 @@ const ComposerEmbeddedMessage: FC = ({ }; export default memo(withGlobal( - (global): StateProps => { + (global, { shouldForceShowEditing }): StateProps => { const { chatId, threadId, type: messageListType } = selectCurrentMessageList(global) || {}; if (!chatId || !threadId || !messageListType) { return {}; @@ -295,7 +300,7 @@ export default memo(withGlobal( const forwardedMessages = forwardMessageIds?.map((id) => selectChatMessage(global, fromChatId!, id)!); let message: ApiMessage | undefined; - if (replyingToId) { + if (replyingToId && !shouldForceShowEditing) { message = selectChatMessage(global, chatId, replyingToId); } else if (editingId) { message = selectEditingMessage(global, chatId, threadId, messageListType); @@ -304,7 +309,7 @@ export default memo(withGlobal( } let sender: ApiChat | ApiUser | undefined; - if (replyingToId && message) { + if (replyingToId && message && !shouldForceShowEditing) { const { forwardInfo } = message; const isChatWithSelf = selectIsChatWithSelf(global, chatId); if (forwardInfo && (forwardInfo.isChannelPost || isChatWithSelf)) { diff --git a/src/components/middle/composer/hooks/useEditing.ts b/src/components/middle/composer/hooks/useEditing.ts index dbefddc7b..3b1071486 100644 --- a/src/components/middle/composer/hooks/useEditing.ts +++ b/src/components/middle/composer/hooks/useEditing.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect } from '../../../../lib/teact/teact'; +import { useCallback, useEffect, useState } from '../../../../lib/teact/teact'; import { getActions } from '../../../../global'; import type { ApiFormattedText, ApiMessage } from '../../../../api/types'; @@ -25,19 +25,30 @@ const useEditing = ( type: MessageListType, draft?: ApiFormattedText, editingDraft?: ApiFormattedText, -) => { + replyingToId?: number, +): [VoidFunction, VoidFunction, boolean] => { const { editMessage, setEditingDraft } = getActions(); + const [shouldForceShowEditing, setShouldForceShowEditing] = useState(); - useEffectWithPrevDeps(([prevEditedMessage]) => { + useEffectWithPrevDeps(([prevEditedMessage, prevReplyingToId]) => { if (!editedMessage) { return; } - if (prevEditedMessage?.id === editedMessage.id) { + + if (replyingToId && prevReplyingToId !== replyingToId) { + setHtml(''); + setShouldForceShowEditing(false); return; } + + if (prevEditedMessage?.id === editedMessage.id && replyingToId === prevReplyingToId) { + return; + } + const text = !prevEditedMessage && editingDraft?.text.length ? editingDraft : editedMessage.content.text; const html = getTextWithEntitiesAsHtml(text); setHtml(html); + setShouldForceShowEditing(true); // `fastRaf` would execute syncronously in this case requestAnimationFrame(() => { const messageInput = document.querySelector(EDITABLE_INPUT_CSS_SELECTOR); @@ -45,7 +56,7 @@ const useEditing = ( focusEditableElement(messageInput, true); } }); - }, [editedMessage, setHtml] as const); + }, [editedMessage, replyingToId, setHtml] as const); useEffect(() => { if (!editedMessage) return undefined; @@ -111,7 +122,7 @@ const useEditing = ( useBackgroundMode(handleBlur); useBeforeUnload(handleBlur); - return [handleEditComplete, handleEditCancel]; + return [handleEditComplete, handleEditCancel, shouldForceShowEditing]; }; export default useEditing;