Composer / Embedded Message: Fix edit and reply (#2372)

This commit is contained in:
Alexander Zinchuk 2023-01-28 02:16:13 +01:00
parent 8d4e447981
commit 55a9715bca
3 changed files with 69 additions and 28 deletions

View File

@ -54,6 +54,7 @@ import {
selectChatType, selectChatType,
selectRequestedDraftFiles, selectRequestedDraftFiles,
selectTabState, selectTabState,
selectReplyingToId,
} from '../../../global/selectors'; } from '../../../global/selectors';
import { import {
getAllowedAttachmentOptions, getAllowedAttachmentOptions,
@ -145,6 +146,7 @@ type StateProps =
isChatWithBot?: boolean; isChatWithBot?: boolean;
isChatWithSelf?: boolean; isChatWithSelf?: boolean;
isChannel?: boolean; isChannel?: boolean;
replyingToId?: number;
isForCurrentMessageList: boolean; isForCurrentMessageList: boolean;
isRightColumnShown?: boolean; isRightColumnShown?: boolean;
isSelectModeActive?: boolean; isSelectModeActive?: boolean;
@ -260,6 +262,7 @@ const Composer: FC<OwnProps & StateProps> = ({
sendAsChat, sendAsChat,
sendAsId, sendAsId,
editingDraft, editingDraft,
replyingToId,
requestedDraftText, requestedDraftText,
requestedDraftFiles, requestedDraftFiles,
botMenuButton, botMenuButton,
@ -394,12 +397,6 @@ const Composer: FC<OwnProps & StateProps> = ({
sendMessageAction({ type: 'typing' }); sendMessageAction({ type: 'typing' });
}, [editingMessage, html, sendMessageAction]); }, [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 { const {
isMentionTooltipOpen, closeMentionTooltip, insertMention, mentionFilteredUsers, isMentionTooltipOpen, closeMentionTooltip, insertMention, mentionFilteredUsers,
} = useMentionTooltip( } = useMentionTooltip(
@ -438,13 +435,6 @@ const Composer: FC<OwnProps & StateProps> = ({
chatBotCommands, chatBotCommands,
); );
const {
isContextMenuOpen: isCustomSendMenuOpen,
handleContextMenu,
handleContextMenuClose,
handleContextMenuHide,
} = useContextMenuHandlers(mainButtonRef, !(mainButtonState === MainButtonState.Send && canShowCustomSendMenu));
const { const {
canSendStickers, canSendGifs, canAttachMedia, canAttachPolls, canAttachEmbedLinks, canSendStickers, canSendGifs, canAttachMedia, canAttachPolls, canAttachEmbedLinks,
} = useMemo(() => getAllowedAttachmentOptions(chat, isChatWithBot), [chat, isChatWithBot]); } = useMemo(() => getAllowedAttachmentOptions(chat, isChatWithBot), [chat, isChatWithBot]);
@ -597,7 +587,7 @@ const Composer: FC<OwnProps & StateProps> = ({
customEmojiNotificationNumber.current = Number(!notificationNumber); customEmojiNotificationNumber.current = Number(!notificationNumber);
}, [currentUserId, lang, showNotification]); }, [currentUserId, lang, showNotification]);
const [handleEditComplete, handleEditCancel] = useEditing( const [handleEditComplete, handleEditCancel, shouldForceShowEditing] = useEditing(
htmlRef, htmlRef,
setHtml, setHtml,
editingMessage, editingMessage,
@ -608,7 +598,36 @@ const Composer: FC<OwnProps & StateProps> = ({
messageListType, messageListType,
draft, draft,
editingDraft, 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); useDraft(draft, chatId, threadId, htmlRef, setHtml, editingMessage, lastSyncTime);
useClipboardPaste( useClipboardPaste(
isForCurrentMessageList, isForCurrentMessageList,
@ -1264,7 +1283,10 @@ const Composer: FC<OwnProps & StateProps> = ({
/> />
<div id="message-compose"> <div id="message-compose">
<div className="svg-appendix" ref={appendixRef} /> <div className="svg-appendix" ref={appendixRef} />
<ComposerEmbeddedMessage onClear={handleEmbeddedClear} /> <ComposerEmbeddedMessage
onClear={handleEmbeddedClear}
shouldForceShowEditing={Boolean(shouldForceShowEditing && editingMessage)}
/>
<WebPagePreview <WebPagePreview
chatId={chatId} chatId={chatId}
threadId={threadId} threadId={threadId}
@ -1529,11 +1551,14 @@ export default memo(withGlobal<OwnProps>(
? selectEditingScheduledDraft(global, chatId) ? selectEditingScheduledDraft(global, chatId)
: selectEditingDraft(global, chatId, threadId); : selectEditingDraft(global, chatId, threadId);
const replyingToId = selectReplyingToId(global, chatId, threadId);
const tabState = selectTabState(global); const tabState = selectTabState(global);
return { return {
editingMessage: selectEditingMessage(global, chatId, threadId, messageListType), editingMessage: selectEditingMessage(global, chatId, threadId, messageListType),
connectionState: global.connectionState, connectionState: global.connectionState,
replyingToId,
draft: selectDraft(global, chatId, threadId), draft: selectDraft(global, chatId, threadId),
chat, chat,
isChatWithBot, isChatWithBot,

View File

@ -54,6 +54,7 @@ type StateProps = {
type OwnProps = { type OwnProps = {
onClear?: () => void; onClear?: () => void;
shouldForceShowEditing?: boolean;
}; };
const FORWARD_RENDERING_DELAY = 300; const FORWARD_RENDERING_DELAY = 300;
@ -68,6 +69,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
noAuthors, noAuthors,
noCaptions, noCaptions,
forwardsHaveCaptions, forwardsHaveCaptions,
shouldForceShowEditing,
isCurrentUserPremium, isCurrentUserPremium,
onClear, onClear,
}) => { }) => {
@ -99,7 +101,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
} = useShowTransition(canAnimate && isShown, undefined, !shouldAnimate, undefined, !shouldAnimate); } = useShowTransition(canAnimate && isShown, undefined, !shouldAnimate, undefined, !shouldAnimate);
const clearEmbedded = useCallback(() => { const clearEmbedded = useCallback(() => {
if (replyingToId) { if (replyingToId && !shouldForceShowEditing) {
setReplyingToId({ messageId: undefined }); setReplyingToId({ messageId: undefined });
} else if (editingId) { } else if (editingId) {
setEditingId({ messageId: undefined }); setEditingId({ messageId: undefined });
@ -107,7 +109,10 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
exitForwardMode(); exitForwardMode();
} }
onClear?.(); onClear?.();
}, [replyingToId, editingId, forwardedMessagesCount, onClear, setReplyingToId, setEditingId, exitForwardMode]); }, [
replyingToId, shouldForceShowEditing, editingId, forwardedMessagesCount, onClear, setReplyingToId, setEditingId,
exitForwardMode,
]);
useEffect(() => (isShown ? captureEscKeyListener(clearEmbedded) : undefined), [isShown, clearEmbedded]); useEffect(() => (isShown ? captureEscKeyListener(clearEmbedded) : undefined), [isShown, clearEmbedded]);
@ -146,7 +151,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
const className = buildClassName('ComposerEmbeddedMessage', transitionClassNames); const className = buildClassName('ComposerEmbeddedMessage', transitionClassNames);
const leftIcon = useMemo(() => { const leftIcon = useMemo(() => {
if (replyingToId) { if (replyingToId && !shouldForceShowEditing) {
return 'icon-reply'; return 'icon-reply';
} }
if (editingId) { if (editingId) {
@ -157,7 +162,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
} }
return undefined; return undefined;
}, [editingId, isForwarding, replyingToId]); }, [editingId, isForwarding, replyingToId, shouldForceShowEditing]);
const customText = forwardedMessagesCount && forwardedMessagesCount > 1 const customText = forwardedMessagesCount && forwardedMessagesCount > 1
? lang('ForwardedMessageCount', forwardedMessagesCount) ? lang('ForwardedMessageCount', forwardedMessagesCount)
@ -274,7 +279,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
}; };
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(
(global): StateProps => { (global, { shouldForceShowEditing }): StateProps => {
const { chatId, threadId, type: messageListType } = selectCurrentMessageList(global) || {}; const { chatId, threadId, type: messageListType } = selectCurrentMessageList(global) || {};
if (!chatId || !threadId || !messageListType) { if (!chatId || !threadId || !messageListType) {
return {}; return {};
@ -295,7 +300,7 @@ export default memo(withGlobal<OwnProps>(
const forwardedMessages = forwardMessageIds?.map((id) => selectChatMessage(global, fromChatId!, id)!); const forwardedMessages = forwardMessageIds?.map((id) => selectChatMessage(global, fromChatId!, id)!);
let message: ApiMessage | undefined; let message: ApiMessage | undefined;
if (replyingToId) { if (replyingToId && !shouldForceShowEditing) {
message = selectChatMessage(global, chatId, replyingToId); message = selectChatMessage(global, chatId, replyingToId);
} else if (editingId) { } else if (editingId) {
message = selectEditingMessage(global, chatId, threadId, messageListType); message = selectEditingMessage(global, chatId, threadId, messageListType);
@ -304,7 +309,7 @@ export default memo(withGlobal<OwnProps>(
} }
let sender: ApiChat | ApiUser | undefined; let sender: ApiChat | ApiUser | undefined;
if (replyingToId && message) { if (replyingToId && message && !shouldForceShowEditing) {
const { forwardInfo } = message; const { forwardInfo } = message;
const isChatWithSelf = selectIsChatWithSelf(global, chatId); const isChatWithSelf = selectIsChatWithSelf(global, chatId);
if (forwardInfo && (forwardInfo.isChannelPost || isChatWithSelf)) { if (forwardInfo && (forwardInfo.isChannelPost || isChatWithSelf)) {

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect } from '../../../../lib/teact/teact'; import { useCallback, useEffect, useState } from '../../../../lib/teact/teact';
import { getActions } from '../../../../global'; import { getActions } from '../../../../global';
import type { ApiFormattedText, ApiMessage } from '../../../../api/types'; import type { ApiFormattedText, ApiMessage } from '../../../../api/types';
@ -25,19 +25,30 @@ const useEditing = (
type: MessageListType, type: MessageListType,
draft?: ApiFormattedText, draft?: ApiFormattedText,
editingDraft?: ApiFormattedText, editingDraft?: ApiFormattedText,
) => { replyingToId?: number,
): [VoidFunction, VoidFunction, boolean] => {
const { editMessage, setEditingDraft } = getActions(); const { editMessage, setEditingDraft } = getActions();
const [shouldForceShowEditing, setShouldForceShowEditing] = useState<boolean>();
useEffectWithPrevDeps(([prevEditedMessage]) => { useEffectWithPrevDeps(([prevEditedMessage, prevReplyingToId]) => {
if (!editedMessage) { if (!editedMessage) {
return; return;
} }
if (prevEditedMessage?.id === editedMessage.id) {
if (replyingToId && prevReplyingToId !== replyingToId) {
setHtml('');
setShouldForceShowEditing(false);
return; return;
} }
if (prevEditedMessage?.id === editedMessage.id && replyingToId === prevReplyingToId) {
return;
}
const text = !prevEditedMessage && editingDraft?.text.length ? editingDraft : editedMessage.content.text; const text = !prevEditedMessage && editingDraft?.text.length ? editingDraft : editedMessage.content.text;
const html = getTextWithEntitiesAsHtml(text); const html = getTextWithEntitiesAsHtml(text);
setHtml(html); setHtml(html);
setShouldForceShowEditing(true);
// `fastRaf` would execute syncronously in this case // `fastRaf` would execute syncronously in this case
requestAnimationFrame(() => { requestAnimationFrame(() => {
const messageInput = document.querySelector<HTMLDivElement>(EDITABLE_INPUT_CSS_SELECTOR); const messageInput = document.querySelector<HTMLDivElement>(EDITABLE_INPUT_CSS_SELECTOR);
@ -45,7 +56,7 @@ const useEditing = (
focusEditableElement(messageInput, true); focusEditableElement(messageInput, true);
} }
}); });
}, [editedMessage, setHtml] as const); }, [editedMessage, replyingToId, setHtml] as const);
useEffect(() => { useEffect(() => {
if (!editedMessage) return undefined; if (!editedMessage) return undefined;
@ -111,7 +122,7 @@ const useEditing = (
useBackgroundMode(handleBlur); useBackgroundMode(handleBlur);
useBeforeUnload(handleBlur); useBeforeUnload(handleBlur);
return [handleEditComplete, handleEditCancel]; return [handleEditComplete, handleEditCancel, shouldForceShowEditing];
}; };
export default useEditing; export default useEditing;