Composer / Embedded Message: Fix edit and reply (#2372)
This commit is contained in:
parent
8d4e447981
commit
55a9715bca
@ -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,
|
||||||
|
|||||||
@ -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)) {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user