Attachment Modal: Send attachments on enter when input not focused (#6397)

This commit is contained in:
Alexander Zinchuk 2025-11-06 11:36:33 +01:00
parent c9f9b27332
commit 28a41a6764
2 changed files with 47 additions and 34 deletions

View File

@ -14,7 +14,7 @@ import type {
} from '../../../types';
import type { Signal } from '../../../util/signals';
import { EDITABLE_INPUT_ID } from '../../../config';
import { EDITABLE_INPUT_ID, EDITABLE_INPUT_MODAL_ID } from '../../../config';
import { requestForcedReflow, requestMutation } from '../../../lib/fasterdom/fasterdom';
import { selectCanPlayAnimatedEmojis, selectDraft, selectIsInSelectMode } from '../../../global/selectors';
import { selectSharedSettings } from '../../../global/selectors/sharedState';
@ -23,7 +23,7 @@ import {
IS_ANDROID, IS_EMOJI_SUPPORTED, IS_IOS, IS_TOUCH_ENV,
} from '../../../util/browser/windowEnvironment';
import buildClassName from '../../../util/buildClassName';
import captureKeyboardListeners from '../../../util/captureKeyboardListeners';
import captureKeyboardListeners, { hasActiveHandler } from '../../../util/captureKeyboardListeners';
import { getIsDirectTextInputDisabled } from '../../../util/directInputManager';
import parseEmojiOnlyString from '../../../util/emoji/parseEmojiOnlyString';
import focusEditableElement from '../../../util/focusEditableElement';
@ -379,6 +379,22 @@ const MessageInput: FC<OwnProps & StateProps> = ({
document.addEventListener('keydown', handleCloseContextMenu);
}
const isSendShortcut = useLastCallback((e: KeyboardEvent | React.KeyboardEvent<HTMLDivElement>) => {
return e.key === 'Enter'
&& !e.shiftKey
&& !isMobileDevice
&& (
(messageSendKeyCombo === 'enter' && !e.shiftKey)
|| (messageSendKeyCombo === 'ctrl-enter' && (e.ctrlKey || e.metaKey))
);
});
const handleSendShortcut = useLastCallback((e: KeyboardEvent | React.KeyboardEvent<HTMLDivElement>) => {
e.preventDefault();
closeTextFormatter();
onSend();
});
function handleKeyDown(e: React.KeyboardEvent<HTMLDivElement>) {
// https://levelup.gitconnected.com/javascript-events-handlers-keyboard-and-load-events-1b3e46a6b0c3#1960
const { isComposing } = e;
@ -394,19 +410,8 @@ const MessageInput: FC<OwnProps & StateProps> = ({
}
}
if (!isComposing && e.key === 'Enter' && !e.shiftKey) {
if (
!isMobileDevice
&& (
(messageSendKeyCombo === 'enter' && !e.shiftKey)
|| (messageSendKeyCombo === 'ctrl-enter' && (e.ctrlKey || e.metaKey))
)
) {
e.preventDefault();
closeTextFormatter();
onSend();
}
if (!isComposing && isSendShortcut(e)) {
handleSendShortcut(e);
} else if (!isComposing && e.key === 'ArrowUp' && !html && !e.metaKey && !e.ctrlKey && !e.altKey) {
e.preventDefault();
editLastMessage();
@ -474,8 +479,7 @@ const MessageInput: FC<OwnProps & StateProps> = ({
useEffect(() => {
if (
!chatId
|| editableInputId !== EDITABLE_INPUT_ID
|| noFocusInterception
|| (editableInputId !== EDITABLE_INPUT_ID && editableInputId !== EDITABLE_INPUT_MODAL_ID)
|| isMobileDevice
|| isSelectModeActive
) {
@ -483,18 +487,29 @@ const MessageInput: FC<OwnProps & StateProps> = ({
}
const handleDocumentKeyDown = (e: KeyboardEvent) => {
if (getIsDirectTextInputDisabled()) {
const target = e.target as HTMLElement | undefined;
const input = inputRef.current!;
const shouldHandleDocumentKeyDown =
isActive && input && target
&& target !== input
&& target.tagName !== 'INPUT'
&& target.tagName !== 'TEXTAREA'
&& !target.isContentEditable
&& !hasActiveHandler('Enter');
if (!shouldHandleDocumentKeyDown) return;
if (isSendShortcut(e)) {
handleSendShortcut(e);
return;
}
const { key } = e;
const target = e.target as HTMLElement | undefined;
if (!target || IGNORE_KEYS.includes(key)) {
if (noFocusInterception || getIsDirectTextInputDisabled() || IGNORE_KEYS.includes(key)) {
return;
}
const input = inputRef.current!;
const isSelectionCollapsed = document.getSelection()?.isCollapsed;
if (
@ -504,18 +519,10 @@ const MessageInput: FC<OwnProps & StateProps> = ({
return;
}
if (
input
&& target !== input
&& target.tagName !== 'INPUT'
&& target.tagName !== 'TEXTAREA'
&& !target.isContentEditable
) {
focusEditableElement(input, true, true);
focusEditableElement(input, true, true);
const newEvent = new KeyboardEvent(e.type, e as any);
input.dispatchEvent(newEvent);
}
const newEvent = new KeyboardEvent(e.type, e as any);
input.dispatchEvent(newEvent);
};
document.addEventListener('keydown', handleDocumentKeyDown, true);
@ -523,7 +530,8 @@ const MessageInput: FC<OwnProps & StateProps> = ({
return () => {
document.removeEventListener('keydown', handleDocumentKeyDown, true);
};
}, [chatId, editableInputId, isMobileDevice, isSelectModeActive, noFocusInterception]);
}, [chatId, editableInputId, isMobileDevice,
isActive, isSelectModeActive, noFocusInterception]);
useEffect(() => {
const captureFirstTab = debounce((e: KeyboardEvent) => {

View File

@ -62,6 +62,11 @@ function hasActiveHandlers() {
return Object.values(handlers).some((keyHandlers) => Boolean(keyHandlers.length));
}
export function hasActiveHandler(key: string) {
const handlerName = keyToHandlerName[key];
return handlerName ? Boolean(handlers[handlerName].length) : false;
}
function handleKeyDown(e: KeyboardEvent) {
const handlerName = keyToHandlerName[e.key];
if (!handlerName) {