import classNames from 'clsx'; import { Map as ImmutableMap } from 'immutable'; import debounce from 'lodash/debounce'; import React, { useCallback } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { useDispatch } from 'react-redux'; import { useHistory } from 'react-router-dom'; import { changeSearch, clearSearch, clearSearchResults, setSearchAccount, showSearch, submitSearch, } from 'soapbox/actions/search'; import AutosuggestAccountInput from 'soapbox/components/autosuggest-account-input'; import { Input } from 'soapbox/components/ui'; import SvgIcon from 'soapbox/components/ui/icon/svg-icon'; import { useAppSelector } from 'soapbox/hooks'; const messages = defineMessages({ placeholder: { id: 'search.placeholder', defaultMessage: 'Search' }, action: { id: 'search.action', defaultMessage: 'Search for “{query}”' }, }); function redirectToAccount(accountId: string, routerHistory: any) { return (_dispatch: any, getState: () => ImmutableMap) => { const acct = getState().getIn(['accounts', accountId, 'acct']); if (acct && routerHistory) { routerHistory.push(`/@${acct}`); } }; } interface ISearch { autoFocus?: boolean, autoSubmit?: boolean, autosuggest?: boolean, openInRoute?: boolean } const Search = (props: ISearch) => { const { autoFocus = false, autoSubmit = false, autosuggest = false, openInRoute = false, } = props; const dispatch = useDispatch(); const history = useHistory(); const intl = useIntl(); const value = useAppSelector((state) => state.search.value); const submitted = useAppSelector((state) => state.search.submitted); const debouncedSubmit = useCallback(debounce(() => { dispatch(submitSearch()); }, 900), []); const handleChange = (event: React.ChangeEvent) => { const { value } = event.target; dispatch(changeSearch(value)); if (autoSubmit) { debouncedSubmit(); } }; const handleClear = (event: React.MouseEvent) => { event.preventDefault(); if (value.length > 0 || submitted) { dispatch(clearSearchResults()); } }; const handleSubmit = () => { if (openInRoute) { dispatch(setSearchAccount(null)); dispatch(submitSearch()); history.push('/search'); } else { dispatch(submitSearch()); } }; const handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { event.preventDefault(); handleSubmit(); } else if (event.key === 'Escape') { document.querySelector('.ui')?.parentElement?.focus(); } }; const handleFocus = () => { dispatch(showSearch()); }; const handleSelected = (accountId: string) => { dispatch(clearSearch()); dispatch(redirectToAccount(accountId, history)); }; const makeMenu = () => [ { text: intl.formatMessage(messages.action, { query: value }), icon: require('@tabler/icons/search.svg'), action: handleSubmit, }, ]; const hasValue = value.length > 0 || submitted; const componentProps: any = { type: 'text', id: 'search', placeholder: intl.formatMessage(messages.placeholder), value, onChange: handleChange, onKeyDown: handleKeyDown, onFocus: handleFocus, autoFocus: autoFocus, theme: 'search', }; if (autosuggest) { componentProps.onSelected = handleSelected; componentProps.menu = makeMenu(); componentProps.autoSelect = false; } return (
{autosuggest ? ( ) : ( )}
); }; export default Search;