From 8dbee455f1ce4902ca5647b8abf61970a8d88bdd Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 4 Jun 2022 17:10:41 -0500 Subject: [PATCH 1/7] Chats: fix search input on /chats --- app/styles/basics.scss | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/app/styles/basics.scss b/app/styles/basics.scss index 33257bf2a..987a6d58d 100644 --- a/app/styles/basics.scss +++ b/app/styles/basics.scss @@ -100,18 +100,7 @@ noscript { } .floating-link { - width: 100%; - height: 100%; - top: 0; - right: 0; - bottom: 0; - left: 0; - position: absolute; - z-index: 201; - background: transparent; - border: 0; - margin: 0; - padding: 0; + @apply w-full h-full inset-0 absolute z-10; } .greentext { From a261e02329b2f5cb33c40bf402087a03492b526d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 4 Jun 2022 17:23:25 -0500 Subject: [PATCH 2/7] Chats: fix search results position on main pane --- app/soapbox/components/account_search.tsx | 2 ++ app/soapbox/components/autosuggest_input.js | 8 ++++++-- app/soapbox/features/chats/components/chat_panes.js | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/soapbox/components/account_search.tsx b/app/soapbox/components/account_search.tsx index 961a4b2ef..3acda1152 100644 --- a/app/soapbox/components/account_search.tsx +++ b/app/soapbox/components/account_search.tsx @@ -14,6 +14,8 @@ interface IAccountSearch { onSelected: (accountId: string) => void, /** Override the default placeholder of the input. */ placeholder?: string, + /** Position of results relative to the input. */ + resultsPosition?: 'above' | 'below', } /** Input to search for accounts. */ diff --git a/app/soapbox/components/autosuggest_input.js b/app/soapbox/components/autosuggest_input.js index 7fd4a23ed..a252eb537 100644 --- a/app/soapbox/components/autosuggest_input.js +++ b/app/soapbox/components/autosuggest_input.js @@ -57,12 +57,14 @@ export default class AutosuggestInput extends ImmutablePureComponent { searchTokens: PropTypes.arrayOf(PropTypes.string), maxLength: PropTypes.number, menu: PropTypes.arrayOf(PropTypes.object), + resultsPosition: PropTypes.string, }; static defaultProps = { autoFocus: false, autoSelect: true, searchTokens: ImmutableList(['@', ':', '#']), + resultsPosition: 'below', }; getFirstIndex = () => { @@ -255,7 +257,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { }; render() { - const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength, menu } = this.props; + const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength, menu, resultsPosition } = this.props; const { suggestionsHidden } = this.state; const style = { direction: 'ltr' }; @@ -293,7 +295,9 @@ export default class AutosuggestInput extends ImmutablePureComponent { />
)} From e6c25a1887dd7a106c4489c1995e347fc9a5f55d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 4 Jun 2022 17:47:40 -0500 Subject: [PATCH 3/7] AutosuggestAccountInput: convert to TSX --- app/soapbox/actions/accounts.js | 4 +- .../components/autosuggest_account_input.js | 95 ------------------- .../components/autosuggest_account_input.tsx | 86 +++++++++++++++++ .../features/compose/components/search.tsx | 4 +- 4 files changed, 90 insertions(+), 99 deletions(-) delete mode 100644 app/soapbox/components/autosuggest_account_input.js create mode 100644 app/soapbox/components/autosuggest_account_input.tsx diff --git a/app/soapbox/actions/accounts.js b/app/soapbox/actions/accounts.js index b93f0e599..88306c2ad 100644 --- a/app/soapbox/actions/accounts.js +++ b/app/soapbox/actions/accounts.js @@ -1050,10 +1050,10 @@ export function fetchPinnedAccountsFail(id, error) { }; } -export function accountSearch(params, cancelToken) { +export function accountSearch(params, signal) { return (dispatch, getState) => { dispatch({ type: ACCOUNT_SEARCH_REQUEST, params }); - return api(getState).get('/api/v1/accounts/search', { params, cancelToken }).then(({ data: accounts }) => { + return api(getState).get('/api/v1/accounts/search', { params, signal }).then(({ data: accounts }) => { dispatch(importFetchedAccounts(accounts)); dispatch({ type: ACCOUNT_SEARCH_SUCCESS, accounts }); return accounts; diff --git a/app/soapbox/components/autosuggest_account_input.js b/app/soapbox/components/autosuggest_account_input.js deleted file mode 100644 index fd93f52a9..000000000 --- a/app/soapbox/components/autosuggest_account_input.js +++ /dev/null @@ -1,95 +0,0 @@ -import { CancelToken } from 'axios'; -import { OrderedSet as ImmutableOrderedSet } from 'immutable'; -import { throttle } from 'lodash'; -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { connect } from 'react-redux'; - -import { accountSearch } from 'soapbox/actions/accounts'; - -import AutosuggestInput from './autosuggest_input'; - -const noOp = () => {}; - -export default @connect() -class AutosuggestAccountInput extends ImmutablePureComponent { - - static propTypes = { - dispatch: PropTypes.func.isRequired, - onChange: PropTypes.func.isRequired, - onSelected: PropTypes.func.isRequired, - value: PropTypes.string.isRequired, - limit: PropTypes.number.isRequired, - } - - static defaultProps = { - value: '', - limit: 4, - } - - state = { - accountIds: ImmutableOrderedSet(), - } - - source = CancelToken.source(); - - refreshCancelToken = () => { - this.source.cancel(); - this.source = CancelToken.source(); - return this.source; - } - - clearResults = () => { - this.setState({ accountIds: ImmutableOrderedSet() }); - } - - handleAccountSearch = throttle(q => { - const { dispatch, limit } = this.props; - const source = this.refreshCancelToken(); - - const params = { q, limit, resolve: false }; - - dispatch(accountSearch(params, source.token)) - .then(accounts => { - const accountIds = accounts.map(account => account.id); - this.setState({ accountIds: ImmutableOrderedSet(accountIds) }); - }) - .catch(noOp); - - }, 900, { leading: true, trailing: true }) - - handleChange = e => { - this.handleAccountSearch(e.target.value); - this.props.onChange(e); - } - - handleSelected = (tokenStart, lastToken, accountId) => { - this.props.onSelected(accountId); - } - - componentDidUpdate(prevProps) { - if (this.props.value === '' && prevProps.value !== '') { - this.clearResults(); - } - } - - render() { - const { intl, value, onChange, ...rest } = this.props; - const { accountIds } = this.state; - - return ( - - ); - } - -} diff --git a/app/soapbox/components/autosuggest_account_input.tsx b/app/soapbox/components/autosuggest_account_input.tsx new file mode 100644 index 000000000..d437e79fc --- /dev/null +++ b/app/soapbox/components/autosuggest_account_input.tsx @@ -0,0 +1,86 @@ +import { OrderedSet as ImmutableOrderedSet } from 'immutable'; +import { throttle } from 'lodash'; +import React, { useState, useRef, useCallback, useEffect } from 'react'; + +import { accountSearch } from 'soapbox/actions/accounts'; +import AutosuggestInput from 'soapbox/components/autosuggest_input'; +import { useAppDispatch } from 'soapbox/hooks'; + +import type { Menu } from 'soapbox/components/dropdown_menu'; + +const noOp = () => {}; + +interface IAutosuggestAccountInput { + onChange: React.ChangeEventHandler, + onSelected: (accountId: string) => void, + value: string, + limit?: number, + className?: string, + autoSelect?: boolean, + menu?: Menu, + onKeyDown?: React.KeyboardEventHandler, +} + +const AutosuggestAccountInput: React.FC = ({ + onChange, + onSelected, + value = '', + limit = 4, + ...rest +}) => { + const dispatch = useAppDispatch(); + const [accountIds, setAccountIds] = useState(ImmutableOrderedSet()); + const controller = useRef(new AbortController()); + + const refreshCancelToken = () => { + controller.current.abort(); + controller.current = new AbortController(); + }; + + const clearResults = () => { + setAccountIds(ImmutableOrderedSet()); + }; + + const handleAccountSearch = useCallback(throttle(q => { + const params = { q, limit, resolve: false }; + + dispatch(accountSearch(params, controller.current.signal)) + .then((accounts: { id: string }[]) => { + const accountIds = accounts.map(account => account.id); + setAccountIds(ImmutableOrderedSet(accountIds)); + }) + .catch(noOp); + + }, 900, { leading: true, trailing: true }), [limit]); + + const handleChange: React.ChangeEventHandler = e => { + refreshCancelToken(); + handleAccountSearch(e.target.value); + onChange(e); + }; + + const handleSelected = (_tokenStart: string, _lastToken: string, accountId: string) => { + onSelected(accountId); + }; + + useEffect(() => { + if (value === '') { + clearResults(); + } + }, [value]); + + return ( + + ); +}; + +export default AutosuggestAccountInput; diff --git a/app/soapbox/features/compose/components/search.tsx b/app/soapbox/features/compose/components/search.tsx index 401001fc9..d68f82bc2 100644 --- a/app/soapbox/features/compose/components/search.tsx +++ b/app/soapbox/features/compose/components/search.tsx @@ -21,7 +21,7 @@ const messages = defineMessages({ action: { id: 'search.action', defaultMessage: 'Search for “{query}”' }, }); -function redirectToAccount(accountId: number, routerHistory: any) { +function redirectToAccount(accountId: string, routerHistory: any) { return (_dispatch: any, getState: () => ImmutableMap) => { const acct = getState().getIn(['accounts', accountId, 'acct']); @@ -97,7 +97,7 @@ const Search = (props: ISearch) => { dispatch(showSearch()); }; - const handleSelected = (accountId: number) => { + const handleSelected = (accountId: string) => { dispatch(clearSearch()); dispatch(redirectToAccount(accountId, history)); }; From ba803e200c7159f83151b78681e086308c6d48c2 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 4 Jun 2022 18:21:16 -0500 Subject: [PATCH 4/7] AutosuggestInput: convert to TSX --- .../components/autosuggest_account_input.tsx | 2 +- ...suggest_input.js => autosuggest_input.tsx} | 124 ++++++++++-------- 2 files changed, 67 insertions(+), 59 deletions(-) rename app/soapbox/components/{autosuggest_input.js => autosuggest_input.tsx} (71%) diff --git a/app/soapbox/components/autosuggest_account_input.tsx b/app/soapbox/components/autosuggest_account_input.tsx index d437e79fc..f58e37623 100644 --- a/app/soapbox/components/autosuggest_account_input.tsx +++ b/app/soapbox/components/autosuggest_account_input.tsx @@ -59,7 +59,7 @@ const AutosuggestAccountInput: React.FC = ({ onChange(e); }; - const handleSelected = (_tokenStart: string, _lastToken: string, accountId: string) => { + const handleSelected = (_tokenStart: number, _lastToken: string | null, accountId: string) => { onSelected(accountId); }; diff --git a/app/soapbox/components/autosuggest_input.js b/app/soapbox/components/autosuggest_input.tsx similarity index 71% rename from app/soapbox/components/autosuggest_input.js rename to app/soapbox/components/autosuggest_input.tsx index a252eb537..493b541a6 100644 --- a/app/soapbox/components/autosuggest_input.js +++ b/app/soapbox/components/autosuggest_input.tsx @@ -1,22 +1,25 @@ import classNames from 'classnames'; import { List as ImmutableList } from 'immutable'; -import PropTypes from 'prop-types'; import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import AutosuggestEmoji from 'soapbox/components/autosuggest_emoji'; import Icon from 'soapbox/components/icon'; +import AutosuggestAccount from 'soapbox/features/compose/components/autosuggest_account'; +import { isRtl } from 'soapbox/rtl'; -import AutosuggestAccount from '../features/compose/components/autosuggest_account'; -import { isRtl } from '../rtl'; +import type { Menu, MenuItem } from 'soapbox/components/dropdown_menu'; -import AutosuggestEmoji from './autosuggest_emoji'; +type CursorMatch = [ + tokenStart: number | null, + token: string | null, +]; -const textAtCursorMatchesToken = (str, caretPosition, searchTokens) => { - let word; +const textAtCursorMatchesToken = (str: string, caretPosition: number, searchTokens: string[]): CursorMatch => { + let word: string; - const left = str.slice(0, caretPosition).search(/\S+$/); - const right = str.slice(caretPosition).search(/\s/); + const left: number = str.slice(0, caretPosition).search(/\S+$/); + const right: number = str.slice(caretPosition).search(/\s/); if (right < 0) { word = str.slice(left); @@ -37,28 +40,25 @@ const textAtCursorMatchesToken = (str, caretPosition, searchTokens) => { } }; -export default class AutosuggestInput extends ImmutablePureComponent { +interface IAutosuggestInput extends Pick, 'onChange' | 'onKeyUp' | 'onKeyDown'> { + value: string, + suggestions: ImmutableList, + disabled?: boolean, + placeholder?: string, + onSuggestionSelected: (tokenStart: number, lastToken: string | null, suggestion: any) => void, + onSuggestionsClearRequested: () => void, + onSuggestionsFetchRequested: (token: string) => void, + autoFocus: boolean, + autoSelect: boolean, + className?: string, + id?: string, + searchTokens: string[], + maxLength?: number, + menu?: Menu, + resultsPosition: string, +} - static propTypes = { - value: PropTypes.string, - suggestions: ImmutablePropTypes.list, - disabled: PropTypes.bool, - placeholder: PropTypes.string, - onSuggestionSelected: PropTypes.func.isRequired, - onSuggestionsClearRequested: PropTypes.func.isRequired, - onSuggestionsFetchRequested: PropTypes.func.isRequired, - onChange: PropTypes.func.isRequired, - onKeyUp: PropTypes.func, - onKeyDown: PropTypes.func, - autoFocus: PropTypes.bool, - autoSelect: PropTypes.bool, - className: PropTypes.string, - id: PropTypes.string, - searchTokens: PropTypes.arrayOf(PropTypes.string), - maxLength: PropTypes.number, - menu: PropTypes.arrayOf(PropTypes.object), - resultsPosition: PropTypes.string, - }; +export default class AutosuggestInput extends ImmutablePureComponent { static defaultProps = { autoFocus: false, @@ -79,8 +79,10 @@ export default class AutosuggestInput extends ImmutablePureComponent { tokenStart: 0, }; - onChange = (e) => { - const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart, this.props.searchTokens); + input: HTMLInputElement | null = null; + + onChange: React.ChangeEventHandler = (e) => { + const [tokenStart, token] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart || 0, this.props.searchTokens); if (token !== null && this.state.lastToken !== token) { this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart }); @@ -90,10 +92,12 @@ export default class AutosuggestInput extends ImmutablePureComponent { this.props.onSuggestionsClearRequested(); } - this.props.onChange(e); + if (this.props.onChange) { + this.props.onChange(e); + } } - onKeyDown = (e) => { + onKeyDown: React.KeyboardEventHandler = (e) => { const { suggestions, menu, disabled } = this.props; const { selectedSuggestion, suggestionsHidden } = this.state; const firstIndex = this.getFirstIndex(); @@ -104,7 +108,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { return; } - if (e.which === 229 || e.isComposing) { + if (e.which === 229) { // Ignore key events during text composition // e.key may be a name of the physical key even in this case (e.x. Safari / Chrome on Mac) return; @@ -113,7 +117,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { switch (e.key) { case 'Escape': if (suggestions.size === 0 || suggestionsHidden) { - document.querySelector('.ui').parentElement.focus(); + document.querySelector('.ui')?.parentElement?.focus(); } else { e.preventDefault(); this.setState({ suggestionsHidden: true }); @@ -136,7 +140,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { break; case 'Enter': case 'Tab': - // Select suggestion + // Select suggestion if (!suggestionsHidden && selectedSuggestion > -1 && (suggestions.size > 0 || menu)) { e.preventDefault(); e.stopPropagation(); @@ -144,9 +148,9 @@ export default class AutosuggestInput extends ImmutablePureComponent { if (selectedSuggestion < suggestions.size) { this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion)); - } else { + } else if (menu) { const item = menu[selectedSuggestion - suggestions.size]; - this.handleMenuItemAction(item); + this.handleMenuItemAction(item, e); } } @@ -157,7 +161,9 @@ export default class AutosuggestInput extends ImmutablePureComponent { return; } - this.props.onKeyDown(e); + if (this.props.onKeyDown) { + this.props.onKeyDown(e); + } } onBlur = () => { @@ -168,25 +174,26 @@ export default class AutosuggestInput extends ImmutablePureComponent { this.setState({ focused: true }); } - onSuggestionClick = (e) => { - const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index')); - e.preventDefault(); + onSuggestionClick: React.MouseEventHandler = (e) => { + const index = Number(e.currentTarget?.getAttribute('data-index')); + const suggestion = this.props.suggestions.get(index); this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion); - this.input.focus(); + this.input?.focus(); + e.preventDefault(); } - componentDidUpdate(prevProps, prevState) { + componentDidUpdate(prevProps: IAutosuggestInput, prevState: any) { const { suggestions } = this.props; if (suggestions !== prevProps.suggestions && suggestions.size > 0 && prevState.suggestionsHidden && prevState.focused) { this.setState({ suggestionsHidden: false }); } } - setInput = (c) => { + setInput = (c: HTMLInputElement) => { this.input = c; } - renderSuggestion = (suggestion, i) => { + renderSuggestion = (suggestion: any, i: number) => { const { selectedSuggestion } = this.state; let inner, key; @@ -204,7 +211,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { return (
{ + handleMenuItemAction = (item: MenuItem | null, e: React.MouseEvent | React.KeyboardEvent) => { this.onBlur(); - item.action(); + if (item?.action) { + item.action(e); + } } - handleMenuItemClick = item => { + handleMenuItemClick = (item: MenuItem | null): React.MouseEventHandler => { return e => { e.preventDefault(); - this.handleMenuItemAction(item); + this.handleMenuItemAction(item, e); }; } @@ -243,15 +252,15 @@ export default class AutosuggestInput extends ImmutablePureComponent { className={classNames('flex items-center space-x-2 px-4 py-2.5 text-sm text-gray-700 dark:text-gray-400 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700', { selected: suggestions.size - selectedSuggestion === i })} href='#' role='button' - tabIndex='0' + tabIndex={0} onMouseDown={this.handleMenuItemClick(item)} key={i} > - {item.icon && ( + {item?.icon && ( )} - {item.text} + {item?.text} )); }; @@ -259,7 +268,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { render() { const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength, menu, resultsPosition } = this.props; const { suggestionsHidden } = this.state; - const style = { direction: 'ltr' }; + const style: React.CSSProperties = { direction: 'ltr' }; const visible = !suggestionsHidden && (!suggestions.isEmpty() || (menu && value)); @@ -275,8 +284,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { type='text' className={classNames({ 'block w-full sm:text-sm dark:bg-slate-800 dark:text-white dark:placeholder:text-gray-500 focus:ring-indigo-500 focus:border-indigo-500': true, - [className]: typeof className !== 'undefined', - })} + }, className)} ref={this.setInput} disabled={disabled} placeholder={placeholder} From 0ff1887fec85b47ae027ec05ffad6bd15b7deb20 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 4 Jun 2022 18:29:58 -0500 Subject: [PATCH 5/7] AutosuggestEmoji: convert to TSX --- app/soapbox/components/autosuggest_emoji.js | 43 ----------------- app/soapbox/components/autosuggest_emoji.tsx | 50 ++++++++++++++++++++ 2 files changed, 50 insertions(+), 43 deletions(-) delete mode 100644 app/soapbox/components/autosuggest_emoji.js create mode 100644 app/soapbox/components/autosuggest_emoji.tsx diff --git a/app/soapbox/components/autosuggest_emoji.js b/app/soapbox/components/autosuggest_emoji.js deleted file mode 100644 index e16d2a2c5..000000000 --- a/app/soapbox/components/autosuggest_emoji.js +++ /dev/null @@ -1,43 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; - -import { joinPublicPath } from 'soapbox/utils/static'; - -import unicodeMapping from '../features/emoji/emoji_unicode_mapping_light'; - -export default class AutosuggestEmoji extends React.PureComponent { - - static propTypes = { - emoji: PropTypes.object.isRequired, - }; - - render() { - const { emoji } = this.props; - let url; - - if (emoji.custom) { - url = emoji.imageUrl; - } else { - const mapping = unicodeMapping[emoji.native] || unicodeMapping[emoji.native.replace(/\uFE0F$/, '')]; - - if (!mapping) { - return null; - } - - url = joinPublicPath(`packs/emoji/${mapping.filename}.svg`); - } - - return ( -
- {emoji.native - - {emoji.colons} -
- ); - } - -} diff --git a/app/soapbox/components/autosuggest_emoji.tsx b/app/soapbox/components/autosuggest_emoji.tsx new file mode 100644 index 000000000..ba6f5671a --- /dev/null +++ b/app/soapbox/components/autosuggest_emoji.tsx @@ -0,0 +1,50 @@ +import React from 'react'; + +import unicodeMapping from 'soapbox/features/emoji/emoji_unicode_mapping_light'; +import { joinPublicPath } from 'soapbox/utils/static'; + +type Emoji = { + custom: boolean, + imageUrl: string, + native: string, + colons: string, +} + +type UnicodeMapping = { + filename: string, +} + +interface IAutosuggestEmoji { + emoji: Emoji, +} + +const AutosuggestEmoji: React.FC = ({ emoji }) => { + let url; + + if (emoji.custom) { + url = emoji.imageUrl; + } else { + // @ts-ignore + const mapping: UnicodeMapping = unicodeMapping[emoji.native] || unicodeMapping[emoji.native.replace(/\uFE0F$/, '')]; + + if (!mapping) { + return null; + } + + url = joinPublicPath(`packs/emoji/${mapping.filename}.svg`); + } + + return ( +
+ {emoji.native + + {emoji.colons} +
+ ); +}; + +export default AutosuggestEmoji; From 318b9f0d3b979cd8943b0b8760e7acc2dff398e0 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 4 Jun 2022 18:35:04 -0500 Subject: [PATCH 6/7] AutosuggestAccountInput: improve types --- app/soapbox/components/autosuggest_account_input.tsx | 8 +++++--- app/soapbox/components/autosuggest_emoji.tsx | 3 ++- app/soapbox/components/autosuggest_input.tsx | 8 +++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/soapbox/components/autosuggest_account_input.tsx b/app/soapbox/components/autosuggest_account_input.tsx index f58e37623..13e604f7c 100644 --- a/app/soapbox/components/autosuggest_account_input.tsx +++ b/app/soapbox/components/autosuggest_account_input.tsx @@ -3,7 +3,7 @@ import { throttle } from 'lodash'; import React, { useState, useRef, useCallback, useEffect } from 'react'; import { accountSearch } from 'soapbox/actions/accounts'; -import AutosuggestInput from 'soapbox/components/autosuggest_input'; +import AutosuggestInput, { Suggestion } from 'soapbox/components/autosuggest_input'; import { useAppDispatch } from 'soapbox/hooks'; import type { Menu } from 'soapbox/components/dropdown_menu'; @@ -59,8 +59,10 @@ const AutosuggestAccountInput: React.FC = ({ onChange(e); }; - const handleSelected = (_tokenStart: number, _lastToken: string | null, accountId: string) => { - onSelected(accountId); + const handleSelected = (_tokenStart: number, _lastToken: string | null, suggestion: Suggestion) => { + if (typeof suggestion === 'string' && suggestion[0] !== '#') { + onSelected(suggestion); + } }; useEffect(() => { diff --git a/app/soapbox/components/autosuggest_emoji.tsx b/app/soapbox/components/autosuggest_emoji.tsx index ba6f5671a..22979d454 100644 --- a/app/soapbox/components/autosuggest_emoji.tsx +++ b/app/soapbox/components/autosuggest_emoji.tsx @@ -3,7 +3,8 @@ import React from 'react'; import unicodeMapping from 'soapbox/features/emoji/emoji_unicode_mapping_light'; import { joinPublicPath } from 'soapbox/utils/static'; -type Emoji = { +export type Emoji = { + id: string, custom: boolean, imageUrl: string, native: string, diff --git a/app/soapbox/components/autosuggest_input.tsx b/app/soapbox/components/autosuggest_input.tsx index 493b541a6..f9b4d76a6 100644 --- a/app/soapbox/components/autosuggest_input.tsx +++ b/app/soapbox/components/autosuggest_input.tsx @@ -3,7 +3,7 @@ import { List as ImmutableList } from 'immutable'; import React from 'react'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import AutosuggestEmoji from 'soapbox/components/autosuggest_emoji'; +import AutosuggestEmoji, { Emoji } from 'soapbox/components/autosuggest_emoji'; import Icon from 'soapbox/components/icon'; import AutosuggestAccount from 'soapbox/features/compose/components/autosuggest_account'; import { isRtl } from 'soapbox/rtl'; @@ -15,6 +15,8 @@ type CursorMatch = [ token: string | null, ]; +export type Suggestion = string | Emoji; + const textAtCursorMatchesToken = (str: string, caretPosition: number, searchTokens: string[]): CursorMatch => { let word: string; @@ -45,7 +47,7 @@ interface IAutosuggestInput extends Pick, suggestions: ImmutableList, disabled?: boolean, placeholder?: string, - onSuggestionSelected: (tokenStart: number, lastToken: string | null, suggestion: any) => void, + onSuggestionSelected: (tokenStart: number, lastToken: string | null, suggestion: Suggestion) => void, onSuggestionsClearRequested: () => void, onSuggestionsFetchRequested: (token: string) => void, autoFocus: boolean, @@ -193,7 +195,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { + renderSuggestion = (suggestion: Suggestion, i: number) => { const { selectedSuggestion } = this.state; let inner, key; From 328d3b9b59a21cf1687695cd8c09e7d2011397f2 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 4 Jun 2022 18:36:34 -0500 Subject: [PATCH 7/7] Suggestion --> AutoSuggestion --- app/soapbox/components/autosuggest_account_input.tsx | 4 ++-- app/soapbox/components/autosuggest_input.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/soapbox/components/autosuggest_account_input.tsx b/app/soapbox/components/autosuggest_account_input.tsx index 13e604f7c..e103d7722 100644 --- a/app/soapbox/components/autosuggest_account_input.tsx +++ b/app/soapbox/components/autosuggest_account_input.tsx @@ -3,7 +3,7 @@ import { throttle } from 'lodash'; import React, { useState, useRef, useCallback, useEffect } from 'react'; import { accountSearch } from 'soapbox/actions/accounts'; -import AutosuggestInput, { Suggestion } from 'soapbox/components/autosuggest_input'; +import AutosuggestInput, { AutoSuggestion } from 'soapbox/components/autosuggest_input'; import { useAppDispatch } from 'soapbox/hooks'; import type { Menu } from 'soapbox/components/dropdown_menu'; @@ -59,7 +59,7 @@ const AutosuggestAccountInput: React.FC = ({ onChange(e); }; - const handleSelected = (_tokenStart: number, _lastToken: string | null, suggestion: Suggestion) => { + const handleSelected = (_tokenStart: number, _lastToken: string | null, suggestion: AutoSuggestion) => { if (typeof suggestion === 'string' && suggestion[0] !== '#') { onSelected(suggestion); } diff --git a/app/soapbox/components/autosuggest_input.tsx b/app/soapbox/components/autosuggest_input.tsx index f9b4d76a6..cac979337 100644 --- a/app/soapbox/components/autosuggest_input.tsx +++ b/app/soapbox/components/autosuggest_input.tsx @@ -15,7 +15,7 @@ type CursorMatch = [ token: string | null, ]; -export type Suggestion = string | Emoji; +export type AutoSuggestion = string | Emoji; const textAtCursorMatchesToken = (str: string, caretPosition: number, searchTokens: string[]): CursorMatch => { let word: string; @@ -47,7 +47,7 @@ interface IAutosuggestInput extends Pick, suggestions: ImmutableList, disabled?: boolean, placeholder?: string, - onSuggestionSelected: (tokenStart: number, lastToken: string | null, suggestion: Suggestion) => void, + onSuggestionSelected: (tokenStart: number, lastToken: string | null, suggestion: AutoSuggestion) => void, onSuggestionsClearRequested: () => void, onSuggestionsFetchRequested: (token: string) => void, autoFocus: boolean, @@ -195,7 +195,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { + renderSuggestion = (suggestion: AutoSuggestion, i: number) => { const { selectedSuggestion } = this.state; let inner, key;