Add search options to search popout in web UI (#26662)
parent
67166de865
commit
bceb893159
|
@ -1,7 +1,7 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage, FormattedList } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
@ -45,6 +45,16 @@ class Search extends PureComponent {
|
||||||
options: [],
|
options: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
defaultOptions = [
|
||||||
|
{ label: <><mark>has:</mark> <FormattedList type='disjunction' value={['media', 'poll', 'embed']} /></>, action: e => { e.preventDefault(); this._insertText('has:') } },
|
||||||
|
{ label: <><mark>is:</mark> <FormattedList type='disjunction' value={['reply', 'sensitive']} /></>, action: e => { e.preventDefault(); this._insertText('is:') } },
|
||||||
|
{ label: <><mark>language:</mark> <FormattedMessage id='search_popout.language_code' defaultMessage='ISO language code' /></>, action: e => { e.preventDefault(); this._insertText('language:') } },
|
||||||
|
{ label: <><mark>from:</mark> <FormattedMessage id='search_popout.user' defaultMessage='user' /></>, action: e => { e.preventDefault(); this._insertText('from:') } },
|
||||||
|
{ label: <><mark>before:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('before:') } },
|
||||||
|
{ label: <><mark>during:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('during:') } },
|
||||||
|
{ label: <><mark>after:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('after:') } },
|
||||||
|
];
|
||||||
|
|
||||||
setRef = c => {
|
setRef = c => {
|
||||||
this.searchForm = c;
|
this.searchForm = c;
|
||||||
};
|
};
|
||||||
|
@ -70,7 +80,7 @@ class Search extends PureComponent {
|
||||||
|
|
||||||
handleKeyDown = (e) => {
|
handleKeyDown = (e) => {
|
||||||
const { selectedOption } = this.state;
|
const { selectedOption } = this.state;
|
||||||
const options = this._getOptions();
|
const options = this._getOptions().concat(this.defaultOptions);
|
||||||
|
|
||||||
switch(e.key) {
|
switch(e.key) {
|
||||||
case 'Escape':
|
case 'Escape':
|
||||||
|
@ -100,11 +110,9 @@ class Search extends PureComponent {
|
||||||
if (selectedOption === -1) {
|
if (selectedOption === -1) {
|
||||||
this._submit();
|
this._submit();
|
||||||
} else if (options.length > 0) {
|
} else if (options.length > 0) {
|
||||||
options[selectedOption].action();
|
options[selectedOption].action(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._unfocus();
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'Delete':
|
case 'Delete':
|
||||||
if (selectedOption > -1 && options.length > 0) {
|
if (selectedOption > -1 && options.length > 0) {
|
||||||
|
@ -147,6 +155,7 @@ class Search extends PureComponent {
|
||||||
|
|
||||||
router.history.push(`/tags/${query}`);
|
router.history.push(`/tags/${query}`);
|
||||||
onClickSearchResult(query, 'hashtag');
|
onClickSearchResult(query, 'hashtag');
|
||||||
|
this._unfocus();
|
||||||
};
|
};
|
||||||
|
|
||||||
handleAccountClick = () => {
|
handleAccountClick = () => {
|
||||||
|
@ -157,6 +166,7 @@ class Search extends PureComponent {
|
||||||
|
|
||||||
router.history.push(`/@${query}`);
|
router.history.push(`/@${query}`);
|
||||||
onClickSearchResult(query, 'account');
|
onClickSearchResult(query, 'account');
|
||||||
|
this._unfocus();
|
||||||
};
|
};
|
||||||
|
|
||||||
handleURLClick = () => {
|
handleURLClick = () => {
|
||||||
|
@ -164,6 +174,7 @@ class Search extends PureComponent {
|
||||||
const { value, onOpenURL } = this.props;
|
const { value, onOpenURL } = this.props;
|
||||||
|
|
||||||
onOpenURL(value, router.history);
|
onOpenURL(value, router.history);
|
||||||
|
this._unfocus();
|
||||||
};
|
};
|
||||||
|
|
||||||
handleStatusSearch = () => {
|
handleStatusSearch = () => {
|
||||||
|
@ -182,6 +193,8 @@ class Search extends PureComponent {
|
||||||
} else if (search.get('type') === 'hashtag') {
|
} else if (search.get('type') === 'hashtag') {
|
||||||
router.history.push(`/tags/${search.get('q')}`);
|
router.history.push(`/tags/${search.get('q')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._unfocus();
|
||||||
};
|
};
|
||||||
|
|
||||||
handleForgetRecentSearchClick = search => {
|
handleForgetRecentSearchClick = search => {
|
||||||
|
@ -194,6 +207,18 @@ class Search extends PureComponent {
|
||||||
document.querySelector('.ui').parentElement.focus();
|
document.querySelector('.ui').parentElement.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_insertText (text) {
|
||||||
|
const { value, onChange } = this.props;
|
||||||
|
|
||||||
|
if (value === '') {
|
||||||
|
onChange(text);
|
||||||
|
} else if (value[value.length - 1] === ' ') {
|
||||||
|
onChange(`${value}${text}`);
|
||||||
|
} else {
|
||||||
|
onChange(`${value} ${text}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_submit (type) {
|
_submit (type) {
|
||||||
const { onSubmit, openInRoute } = this.props;
|
const { onSubmit, openInRoute } = this.props;
|
||||||
const { router } = this.context;
|
const { router } = this.context;
|
||||||
|
@ -203,6 +228,8 @@ class Search extends PureComponent {
|
||||||
if (openInRoute) {
|
if (openInRoute) {
|
||||||
router.history.push('/search');
|
router.history.push('/search');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._unfocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
_getOptions () {
|
_getOptions () {
|
||||||
|
@ -325,6 +352,16 @@ class Search extends PureComponent {
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<h4><FormattedMessage id='search_popout.options' defaultMessage='Search options' /></h4>
|
||||||
|
|
||||||
|
<div className='search__popout__menu'>
|
||||||
|
{this.defaultOptions.map(({ key, label, action }, i) => (
|
||||||
|
<button key={key} onMouseDown={action} className={classNames('search__popout__menu__item', { selected: selectedOption === (options.length + i) })}>
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -586,8 +586,12 @@
|
||||||
"search.quick_action.open_url": "Open URL in Mastodon",
|
"search.quick_action.open_url": "Open URL in Mastodon",
|
||||||
"search.quick_action.status_search": "Posts matching {x}",
|
"search.quick_action.status_search": "Posts matching {x}",
|
||||||
"search.search_or_paste": "Search or paste URL",
|
"search.search_or_paste": "Search or paste URL",
|
||||||
|
"search_popout.language_code": "ISO language code",
|
||||||
|
"search_popout.options": "Search options",
|
||||||
"search_popout.quick_actions": "Quick actions",
|
"search_popout.quick_actions": "Quick actions",
|
||||||
"search_popout.recent": "Recent searches",
|
"search_popout.recent": "Recent searches",
|
||||||
|
"search_popout.specific_date": "specific date",
|
||||||
|
"search_popout.user": "user",
|
||||||
"search_results.accounts": "Profiles",
|
"search_results.accounts": "Profiles",
|
||||||
"search_results.all": "All",
|
"search_results.all": "All",
|
||||||
"search_results.hashtags": "Hashtags",
|
"search_results.hashtags": "Hashtags",
|
||||||
|
|
|
@ -4991,6 +4991,12 @@ a.status-card {
|
||||||
}
|
}
|
||||||
|
|
||||||
&__menu {
|
&__menu {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&__message {
|
&__message {
|
||||||
color: $dark-text-color;
|
color: $dark-text-color;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
|
|
Reference in New Issue