From a59d10ae0cf7830aca2828abc3670bee2cc5b6dd Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 26 Nov 2024 22:26:47 +0200 Subject: [PATCH] web/composer: fix selecting autocomplete item when not at end of composer --- web/src/ui/composer/Autocompleter.tsx | 17 +++++++++++++---- web/src/ui/composer/MessageComposer.tsx | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/web/src/ui/composer/Autocompleter.tsx b/web/src/ui/composer/Autocompleter.tsx index f312066..1c510bd 100644 --- a/web/src/ui/composer/Autocompleter.tsx +++ b/web/src/ui/composer/Autocompleter.tsx @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { JSX, use, useEffect } from "react" +import { JSX, RefObject, use, useEffect } from "react" import { getAvatarURL, getMediaURL } from "@/api/media.ts" import { RoomStateStore, useCustomEmojis } from "@/api/statestore" import { Emoji, emojiToMarkdown, useSortedAndFilteredEmojis } from "@/util/emoji" @@ -37,6 +37,7 @@ export interface AutocompleteQuery { export interface AutocompleterProps { setState: (state: Partial) => void setAutocomplete: (params: AutocompleteQuery | null) => void + textInput: RefObject state: ComposerState params: AutocompleteQuery room: RoomStateStore @@ -52,7 +53,7 @@ interface InnerAutocompleterProps extends AutocompleterProps { } function useAutocompleter({ - params, state, setState, setAutocomplete, + params, state, setState, setAutocomplete, textInput, items, getText, getKey, render, }: InnerAutocompleterProps) { const onSelect = useEvent((index: number) => { @@ -61,12 +62,20 @@ function useAutocompleter({ } index = positiveMod(index, items.length) const replacementText = getText(items[index]) + const newText = state.text.slice(0, params.startPos) + replacementText + state.text.slice(params.endPos) + const endPos = params.startPos + replacementText.length + if (textInput.current) { + // React messes up the selection when changing the value for some reason, + // so bypass react here to avoid the caret jumping to the end and closing the autocompleter + textInput.current.value = newText + textInput.current.setSelectionRange(endPos, endPos) + } setState({ - text: state.text.slice(0, params.startPos) + replacementText + state.text.slice(params.endPos), + text: newText, }) setAutocomplete({ ...params, - endPos: params.startPos + replacementText.length, + endPos, frozenQuery: params.frozenQuery ?? params.query, }) document.querySelector(`div.autocompletion-item[data-index='${index}']`)?.scrollIntoView({ block: "nearest" }) diff --git a/web/src/ui/composer/MessageComposer.tsx b/web/src/ui/composer/MessageComposer.tsx index e7a42d1..5371e89 100644 --- a/web/src/ui/composer/MessageComposer.tsx +++ b/web/src/ui/composer/MessageComposer.tsx @@ -393,6 +393,7 @@ const MessageComposer = () => { state={state} setState={setState} setAutocomplete={setAutocomplete} + textInput={textInput} />}
{replyToEvt &&