From e4e19ba26487e03c9db62275efe960dc124aa280 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 25 Jun 2026 17:49:27 +0200 Subject: [PATCH] Change autosuggestions to include second word in web UI (#39622) --- .../mastodon/components/autosuggest/utils.ts | 29 +++++++++++++++++++ .../mastodon/components/autosuggest_input.jsx | 26 +---------------- .../components/autosuggest_textarea.jsx | 28 ++---------------- 3 files changed, 32 insertions(+), 51 deletions(-) create mode 100644 app/javascript/mastodon/components/autosuggest/utils.ts diff --git a/app/javascript/mastodon/components/autosuggest/utils.ts b/app/javascript/mastodon/components/autosuggest/utils.ts new file mode 100644 index 00000000000..bef96374bfd --- /dev/null +++ b/app/javascript/mastodon/components/autosuggest/utils.ts @@ -0,0 +1,29 @@ +export const textAtCursorMatchesToken = ( + str: string, + caretPosition: number, + searchTokens: string[], +) => { + let word: string; + + const regex = new RegExp(`[${searchTokens.join('')}\\w]+(\\s[\\w]+)?$`); + const left = str.slice(0, caretPosition).search(regex); + const right = str.slice(caretPosition).search(/\s/); + + if (right < 0) { + word = str.slice(left); + } else { + word = str.slice(left, right + caretPosition); + } + + word = word.trim(); + + if (word.length < 3 || (word[0] && !searchTokens.includes(word[0]))) { + return [null, null]; + } + + if (word.length > 0) { + return [left + 1, word]; + } else { + return [null, null]; + } +}; diff --git a/app/javascript/mastodon/components/autosuggest_input.jsx b/app/javascript/mastodon/components/autosuggest_input.jsx index 4a3b7d90400..b3062ec383f 100644 --- a/app/javascript/mastodon/components/autosuggest_input.jsx +++ b/app/javascript/mastodon/components/autosuggest_input.jsx @@ -12,31 +12,7 @@ import AutosuggestAccountContainer from '../features/compose/containers/autosugg import { AutosuggestEmoji } from './autosuggest_emoji'; import { AutosuggestHashtag } from './autosuggest_hashtag'; import { LocalCustomEmojiProvider } from './emoji/context'; - -const textAtCursorMatchesToken = (str, caretPosition, searchTokens) => { - let word; - - let left = str.slice(0, caretPosition).search(/\S+$/); - let right = str.slice(caretPosition).search(/\s/); - - if (right < 0) { - word = str.slice(left); - } else { - word = str.slice(left, right + caretPosition); - } - - if (!word || word.trim().length < 3 || searchTokens.indexOf(word[0]) === -1) { - return [null, null]; - } - - word = word.trim(); - - if (word.length > 0) { - return [left + 1, word]; - } else { - return [null, null]; - } -}; +import { textAtCursorMatchesToken } from './autosuggest/utils'; export default class AutosuggestInput extends ImmutablePureComponent { diff --git a/app/javascript/mastodon/components/autosuggest_textarea.jsx b/app/javascript/mastodon/components/autosuggest_textarea.jsx index fb99f62a73d..333c1954476 100644 --- a/app/javascript/mastodon/components/autosuggest_textarea.jsx +++ b/app/javascript/mastodon/components/autosuggest_textarea.jsx @@ -13,31 +13,7 @@ import AutosuggestAccountContainer from '../features/compose/containers/autosugg import { AutosuggestEmoji } from './autosuggest_emoji'; import { AutosuggestHashtag } from './autosuggest_hashtag'; import { LocalCustomEmojiProvider } from './emoji/context'; - -const textAtCursorMatchesToken = (str, caretPosition) => { - let word; - - let left = str.slice(0, caretPosition).search(/\S+$/); - let right = str.slice(caretPosition).search(/\s/); - - if (right < 0) { - word = str.slice(left); - } else { - word = str.slice(left, right + caretPosition); - } - - if (!word || word.trim().length < 3 || ['@', '@', ':', '#', '#'].indexOf(word[0]) === -1) { - return [null, null]; - } - - word = word.trim(); - - if (word.length > 0) { - return [left + 1, word]; - } else { - return [null, null]; - } -}; +import { textAtCursorMatchesToken } from './autosuggest/utils'; const AutosuggestTextarea = forwardRef(({ value, @@ -64,7 +40,7 @@ const AutosuggestTextarea = forwardRef(({ const tokenStartRef = useRef(0); const handleChange = useCallback((e) => { - const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart); + const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart, ['@', '@', ':', '#', '#']); if (token !== null && lastTokenRef.current !== token) { tokenStartRef.current = tokenStart;