Allow to search for posts from given account

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
merge-requests/1710/head
marcin mikołajczak 2022-08-08 13:40:41 +02:00
rodzic a8577fe0ee
commit c59f3947cb
7 zmienionych plików z 76 dodań i 9 usunięć

Wyświetl plik

@ -22,6 +22,8 @@ const SEARCH_EXPAND_REQUEST = 'SEARCH_EXPAND_REQUEST';
const SEARCH_EXPAND_SUCCESS = 'SEARCH_EXPAND_SUCCESS';
const SEARCH_EXPAND_FAIL = 'SEARCH_EXPAND_FAIL';
const SEARCH_ACCOUNT_SET = 'SEARCH_ACCOUNT_SET';
const changeSearch = (value: string) =>
(dispatch: AppDispatch) => {
// If backspaced all the way, clear the search
@ -43,6 +45,7 @@ const submitSearch = (filter?: SearchFilter) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const value = getState().search.value;
const type = filter || getState().search.filter || 'accounts';
const accountId = getState().search.accountId;
// An empty search doesn't return any results
if (value.length === 0) {
@ -51,13 +54,17 @@ const submitSearch = (filter?: SearchFilter) =>
dispatch(fetchSearchRequest(value));
const params: Record<string, any> = {
q: value,
resolve: true,
limit: 20,
type,
};
if (accountId) params.account_id = accountId;
api(getState).get('/api/v2/search', {
params: {
q: value,
resolve: true,
limit: 20,
type,
},
params,
}).then(response => {
if (response.data.accounts) {
dispatch(importFetchedAccounts(response.data.accounts));
@ -151,6 +158,11 @@ const showSearch = () => ({
type: SEARCH_SHOW,
});
const setSearchAccount = (accountId: string) => ({
type: SEARCH_ACCOUNT_SET,
accountId,
});
export {
SEARCH_CHANGE,
SEARCH_CLEAR,
@ -162,6 +174,7 @@ export {
SEARCH_EXPAND_REQUEST,
SEARCH_EXPAND_SUCCESS,
SEARCH_EXPAND_FAIL,
SEARCH_ACCOUNT_SET,
changeSearch,
clearSearch,
submitSearch,
@ -174,4 +187,5 @@ export {
expandSearchSuccess,
expandSearchFail,
showSearch,
setSearchAccount,
};

Wyświetl plik

@ -64,6 +64,7 @@ const messages = defineMessages({
demoteToUser: { id: 'admin.users.actions.demote_to_user', defaultMessage: 'Demote @{name} to a regular user' },
suggestUser: { id: 'admin.users.actions.suggest_user', defaultMessage: 'Suggest @{name}' },
unsuggestUser: { id: 'admin.users.actions.unsuggest_user', defaultMessage: 'Unsuggest @{name}' },
search: { id: 'account.search', defaultMessage: 'Search from @{name}' },
});
const mapStateToProps = state => {
@ -274,6 +275,14 @@ class Header extends ImmutablePureComponent {
});
}
if (features.searchFromAccount) {
menu.push({
text: intl.formatMessage(messages.search, { name: account.get('username') }),
action: this.props.onSearch,
icon: require('@tabler/icons/search.svg'),
});
}
if (features.removeFromFollowers && account.relationship?.followed_by) {
menu.push({
text: intl.formatMessage(messages.removeFromFollowers),

Wyświetl plik

@ -26,6 +26,7 @@ class Header extends ImmutablePureComponent {
onEndorseToggle: PropTypes.func.isRequired,
onAddToList: PropTypes.func.isRequired,
onRemoveFromFollowers: PropTypes.func.isRequired,
onSearch: PropTypes.func.isRequired,
username: PropTypes.string,
history: PropTypes.object,
};
@ -146,6 +147,10 @@ class Header extends ImmutablePureComponent {
this.props.onRemoveFromFollowers(this.props.account);
}
handleSearch = () => {
this.props.onSearch(this.props.account, this.props.history);
}
render() {
const { account } = this.props;
const moved = (account) ? account.get('moved') : false;
@ -183,6 +188,7 @@ class Header extends ImmutablePureComponent {
onUnsuggestUser={this.handleUnsuggestUser}
onShowNote={this.handleShowNote}
onRemoveFromFollowers={this.handleRemoveFromFollowers}
onSearch={this.handleSearch}
username={this.props.username}
/>
</>

Wyświetl plik

@ -36,6 +36,7 @@ import { openModal } from 'soapbox/actions/modals';
import { deactivateUserModal, deleteUserModal } from 'soapbox/actions/moderation';
import { initMuteModal } from 'soapbox/actions/mutes';
import { initReport } from 'soapbox/actions/reports';
import { setSearchAccount } from 'soapbox/actions/search';
import { getSettings } from 'soapbox/actions/settings';
import snackbar from 'soapbox/actions/snackbar';
import { makeGetAccount } from 'soapbox/selectors';
@ -291,6 +292,13 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}
});
},
onSearch(account, router) {
dispatch((dispatch) => {
dispatch(setSearchAccount(account.id));
router.push('/search');
});
},
});
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));

Wyświetl plik

@ -2,11 +2,12 @@ import classNames from 'classnames';
import React, { useEffect, useRef } from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import { expandSearch, setFilter } from 'soapbox/actions/search';
import { clearSearch, expandSearch, setFilter } from 'soapbox/actions/search';
import { fetchTrendingStatuses } from 'soapbox/actions/trending_statuses';
import Hashtag from 'soapbox/components/hashtag';
import IconButton from 'soapbox/components/icon_button';
import ScrollableList from 'soapbox/components/scrollable_list';
import { Tabs } from 'soapbox/components/ui';
import { HStack, Tabs, Text } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account_container';
import StatusContainer from 'soapbox/containers/status_container';
import PlaceholderAccount from 'soapbox/features/placeholder/components/placeholder_account';
@ -37,9 +38,13 @@ const SearchResults = () => {
const trends = useAppSelector((state) => state.trends.items);
const submitted = useAppSelector((state) => state.search.submitted);
const selectedFilter = useAppSelector((state) => state.search.filter);
const filterByAccount = useAppSelector((state) => state.search.accountId);
const account = useAppSelector((state) => state.accounts.get(filterByAccount)?.acct);
const handleLoadMore = () => dispatch(expandSearch(selectedFilter));
const handleClearSearch = () => dispatch(clearSearch());
const selectFilter = (newActiveFilter: SearchFilter) => dispatch(setFilter(newActiveFilter));
const renderFilterBar = () => {
@ -189,7 +194,18 @@ const SearchResults = () => {
return (
<>
{renderFilterBar()}
{filterByAccount ? (
<HStack className='mb-4 pb-4 px-2 border-solid border-b border-gray-200 dark:border-gray-800' space={2}>
<IconButton iconClassName='h-5 w-5' src={require('@tabler/icons/x.svg')} onClick={handleClearSearch} />
<Text>
<FormattedMessage
id='search_results.filter_message'
defaultMessage='You are searching for posts from @{acct}.'
values={{ acct: account }}
/>
</Text>
</HStack>
) : renderFilterBar()}
{noResultsMessage || (
<ScrollableList

Wyświetl plik

@ -17,6 +17,7 @@ import {
SEARCH_FILTER_SET,
SEARCH_EXPAND_REQUEST,
SEARCH_EXPAND_SUCCESS,
SEARCH_ACCOUNT_SET,
} from '../actions/search';
import type { AnyAction } from 'redux';
@ -41,6 +42,7 @@ const ReducerRecord = ImmutableRecord({
hidden: false,
results: ResultsRecord(),
filter: 'accounts' as SearchFilter,
accountId: null as string | null,
});
type State = ReturnType<typeof ReducerRecord>;
@ -120,6 +122,8 @@ export default function search(state = ReducerRecord(), action: AnyAction) {
return state.setIn(['results', `${action.searchType}Loaded`], false);
case SEARCH_EXPAND_SUCCESS:
return paginateResults(state, action.searchType, action.results, action.searchTerm);
case SEARCH_ACCOUNT_SET:
return ReducerRecord({ accountId: action.accountId, filter: 'statuses' });
default:
return state;
}

Wyświetl plik

@ -536,6 +536,16 @@ const getInstanceFeatures = (instance: Instance) => {
*/
scopes: v.software === PLEROMA ? 'read write follow push admin' : 'read write follow push',
/**
* Ability to search statuses from the given account.
* @see {@link https://docs.joinmastodon.org/methods/search/}
* @see POST /api/v2/search
*/
searchFromAccount: any([
v.software === MASTODON && gte(v.version, '2.8.0'),
v.software === PLEROMA && gte(v.version, '1.0.0'),
]),
/**
* Ability to manage account security settings.
* @see POST /api/pleroma/change_password