web/composer: fix selecting autocomplete item when not at end of composer

This commit is contained in:
Tulir Asokan 2024-11-26 22:26:47 +02:00
parent 573fc6a052
commit a59d10ae0c
2 changed files with 14 additions and 4 deletions

View file

@ -13,7 +13,7 @@
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import { JSX, use, useEffect } from "react" import { JSX, RefObject, use, useEffect } from "react"
import { getAvatarURL, getMediaURL } from "@/api/media.ts" import { getAvatarURL, getMediaURL } from "@/api/media.ts"
import { RoomStateStore, useCustomEmojis } from "@/api/statestore" import { RoomStateStore, useCustomEmojis } from "@/api/statestore"
import { Emoji, emojiToMarkdown, useSortedAndFilteredEmojis } from "@/util/emoji" import { Emoji, emojiToMarkdown, useSortedAndFilteredEmojis } from "@/util/emoji"
@ -37,6 +37,7 @@ export interface AutocompleteQuery {
export interface AutocompleterProps { export interface AutocompleterProps {
setState: (state: Partial<ComposerState>) => void setState: (state: Partial<ComposerState>) => void
setAutocomplete: (params: AutocompleteQuery | null) => void setAutocomplete: (params: AutocompleteQuery | null) => void
textInput: RefObject<HTMLTextAreaElement | null>
state: ComposerState state: ComposerState
params: AutocompleteQuery params: AutocompleteQuery
room: RoomStateStore room: RoomStateStore
@ -52,7 +53,7 @@ interface InnerAutocompleterProps<T> extends AutocompleterProps {
} }
function useAutocompleter<T>({ function useAutocompleter<T>({
params, state, setState, setAutocomplete, params, state, setState, setAutocomplete, textInput,
items, getText, getKey, render, items, getText, getKey, render,
}: InnerAutocompleterProps<T>) { }: InnerAutocompleterProps<T>) {
const onSelect = useEvent((index: number) => { const onSelect = useEvent((index: number) => {
@ -61,12 +62,20 @@ function useAutocompleter<T>({
} }
index = positiveMod(index, items.length) index = positiveMod(index, items.length)
const replacementText = getText(items[index]) 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({ setState({
text: state.text.slice(0, params.startPos) + replacementText + state.text.slice(params.endPos), text: newText,
}) })
setAutocomplete({ setAutocomplete({
...params, ...params,
endPos: params.startPos + replacementText.length, endPos,
frozenQuery: params.frozenQuery ?? params.query, frozenQuery: params.frozenQuery ?? params.query,
}) })
document.querySelector(`div.autocompletion-item[data-index='${index}']`)?.scrollIntoView({ block: "nearest" }) document.querySelector(`div.autocompletion-item[data-index='${index}']`)?.scrollIntoView({ block: "nearest" })

View file

@ -393,6 +393,7 @@ const MessageComposer = () => {
state={state} state={state}
setState={setState} setState={setState}
setAutocomplete={setAutocomplete} setAutocomplete={setAutocomplete}
textInput={textInput}
/></div>} /></div>}
<div className="message-composer" ref={composerRef}> <div className="message-composer" ref={composerRef}>
{replyToEvt && <ReplyBody {replyToEvt && <ReplyBody