Skip to content

Commit 91cf23a

Browse files
committed
fix: remove insertText from MessageInputContext and always update TextComposer selection state
1 parent c2b2ddf commit 91cf23a

File tree

7 files changed

+58
-74
lines changed

7 files changed

+58
-74
lines changed

src/components/MessageInput/hooks/useCreateMessageInputContext.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export const useCreateMessageInputContext = (value: MessageInputContextValue) =>
1414
focus,
1515
handleSubmit,
1616
hideSendButton,
17-
insertText,
1817
isThreadInput,
1918
maxRows,
2019
minRows,
@@ -40,7 +39,6 @@ export const useCreateMessageInputContext = (value: MessageInputContextValue) =>
4039
focus,
4140
handleSubmit,
4241
hideSendButton,
43-
insertText,
4442
isThreadInput,
4543
maxRows,
4644
minRows,

src/components/MessageInput/hooks/useMessageInputControls.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type React from 'react';
2-
import { useMessageInputText } from './useMessageInputText';
2+
import { useTextareaRef } from './useTextareaRef';
33
import { useSubmitHandler } from './useSubmitHandler';
44
import { usePasteHandler } from './usePasteHandler';
55
import { useMediaRecorder } from '../../MediaRecorder/hooks/useMediaRecorder';
@@ -12,7 +12,6 @@ export type MessageInputHookProps = {
1212
event?: React.BaseSyntheticEvent,
1313
customMessageData?: Omit<UpdatedMessage, 'mentioned_users'>,
1414
) => void;
15-
insertText: (textToInsert: string) => void;
1615
onPaste: (event: React.ClipboardEvent<HTMLTextAreaElement>) => void;
1716
recordingController: RecordingController;
1817
textareaRef: React.MutableRefObject<HTMLTextAreaElement | null | undefined>;
@@ -24,7 +23,7 @@ export const useMessageInputControls = (
2423
const { asyncMessagesMultiSendEnabled, audioRecordingConfig, audioRecordingEnabled } =
2524
props;
2625

27-
const { insertText, textareaRef } = useMessageInputText(props);
26+
const { textareaRef } = useTextareaRef(props);
2827

2928
const { handleSubmit } = useSubmitHandler(props);
3029

@@ -35,11 +34,10 @@ export const useMessageInputControls = (
3534
recordingConfig: audioRecordingConfig,
3635
});
3736

38-
const { onPaste } = usePasteHandler(insertText);
37+
const { onPaste } = usePasteHandler();
3938

4039
return {
4140
handleSubmit,
42-
insertText,
4341
onPaste,
4442
recordingController,
4543
textareaRef,

src/components/MessageInput/hooks/useMessageInputText.ts

Lines changed: 0 additions & 58 deletions
This file was deleted.

src/components/MessageInput/hooks/usePasteHandler.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { useCallback } from 'react';
22
import { useMessageComposer } from './useMessageComposer';
33
import { dataTransferItemsToFiles } from '../../ReactFileUtilities';
44

5-
export const usePasteHandler = (insertText: (textToInsert: string) => void) => {
6-
const { attachmentManager } = useMessageComposer();
5+
export const usePasteHandler = () => {
6+
const { attachmentManager, textComposer } = useMessageComposer();
77
const onPaste = useCallback(
88
(clipboardEvent: React.ClipboardEvent<HTMLTextAreaElement>) => {
99
(async (event) => {
@@ -29,13 +29,13 @@ export const usePasteHandler = (insertText: (textToInsert: string) => void) => {
2929

3030
if (plainTextPromise) {
3131
const pastedText = await plainTextPromise;
32-
insertText(pastedText);
32+
textComposer.insertText({ text: pastedText });
3333
} else {
3434
attachmentManager.uploadFiles(fileLikes);
3535
}
3636
})(clipboardEvent);
3737
},
38-
[attachmentManager, insertText],
38+
[attachmentManager, textComposer],
3939
);
4040

4141
return { onPaste };
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { useEffect, useRef } from 'react';
2+
import type { MessageInputProps } from '../MessageInput';
3+
4+
export const useTextareaRef = (props: MessageInputProps) => {
5+
const { focus } = props;
6+
const textareaRef = useRef<HTMLTextAreaElement>(undefined);
7+
// Focus
8+
useEffect(() => {
9+
if (focus && textareaRef.current) {
10+
textareaRef.current.focus();
11+
}
12+
}, [focus]);
13+
14+
return {
15+
textareaRef,
16+
};
17+
};

src/components/TextareaComposer/TextareaComposer.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1+
import debounce from 'lodash.debounce';
12
import clsx from 'clsx';
2-
import type { ChangeEventHandler, TextareaHTMLAttributes, UIEventHandler } from 'react';
3+
import type {
4+
ChangeEventHandler,
5+
SyntheticEvent,
6+
TextareaHTMLAttributes,
7+
UIEventHandler,
8+
} from 'react';
9+
import { useMemo } from 'react';
310
import React, { useCallback, useEffect, useRef, useState } from 'react';
411
import Textarea from 'react-textarea-autosize';
512
import { useMessageComposer } from '../MessageInput';
@@ -63,6 +70,7 @@ export const TextareaComposer = ({
6370
onChange,
6471
onKeyDown,
6572
onScroll,
73+
onSelect,
6674
placeholder: placeholderProp,
6775
shouldSubmit: shouldSubmitProp,
6876
...restTextareaProps
@@ -201,6 +209,22 @@ export const TextareaComposer = ({
201209
[onScroll, textComposer],
202210
);
203211

212+
const setSelectionDebounced = useMemo(
213+
() =>
214+
debounce(
215+
(e: SyntheticEvent<HTMLTextAreaElement>) => {
216+
onSelect?.(e);
217+
textComposer.setSelection({
218+
end: (e.target as HTMLTextAreaElement).selectionEnd,
219+
start: (e.target as HTMLTextAreaElement).selectionStart,
220+
});
221+
},
222+
100,
223+
{ leading: false, trailing: true },
224+
),
225+
[onSelect, textComposer],
226+
);
227+
204228
useEffect(() => {
205229
// FIXME: find the real reason for cursor being set to the end on each change
206230
// This is a workaround to prevent the cursor from jumping
@@ -249,6 +273,7 @@ export const TextareaComposer = ({
249273
onKeyDown={keyDownHandler}
250274
onPaste={onPaste}
251275
onScroll={scrollHandler}
276+
onSelect={setSelectionDebounced}
252277
placeholder={placeholder || t('Type your message')}
253278
ref={(ref) => {
254279
textareaRef.current = ref;

src/plugins/Emojis/EmojiPicker.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import Picker from '@emoji-mart/react';
44

55
import type { Options } from '@popperjs/core';
66

7-
import { useMessageInputContext, useTranslationContext } from '../../context';
87
import { EmojiPickerIcon } from './icons';
8+
import { useMessageInputContext, useTranslationContext } from '../../context';
9+
import { useMessageComposer } from '../../components';
910

1011
const isShadowRoot = (node: Node): node is ShadowRoot => !!(node as ShadowRoot).host;
1112

@@ -35,7 +36,8 @@ const classNames: EmojiPickerProps = {
3536

3637
export const EmojiPicker = (props: EmojiPickerProps) => {
3738
const { t } = useTranslationContext('EmojiPicker');
38-
const { insertText, textareaRef } = useMessageInputContext('EmojiPicker');
39+
const { textareaRef } = useMessageInputContext('EmojiPicker');
40+
const { textComposer } = useMessageComposer();
3941
const [displayPicker, setDisplayPicker] = useState(false);
4042
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(
4143
null,
@@ -84,8 +86,10 @@ export const EmojiPicker = (props: EmojiPickerProps) => {
8486
<Picker
8587
data={async () => (await import('@emoji-mart/data')).default}
8688
onEmojiSelect={(e: { native: string }) => {
87-
insertText(e.native);
88-
textareaRef.current?.focus();
89+
const textarea = textareaRef.current;
90+
if (!textarea) return;
91+
textComposer.insertText({ text: e.native });
92+
textarea.focus();
8993
if (props.closeOnEmojiSelect) {
9094
setDisplayPicker(false);
9195
}

0 commit comments

Comments
 (0)