[Glitch] Add search results pagination to web UI (#11409)

Port 8a4674f2c3 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
This commit is contained in:
Eugen Rochko 2019-07-27 05:49:50 +02:00 committed by Thibaut Girka
parent 7f147acea6
commit 51411267fd
5 changed files with 76 additions and 8 deletions

View File

@ -10,6 +10,10 @@ export const SEARCH_FETCH_REQUEST = 'SEARCH_FETCH_REQUEST';
export const SEARCH_FETCH_SUCCESS = 'SEARCH_FETCH_SUCCESS'; export const SEARCH_FETCH_SUCCESS = 'SEARCH_FETCH_SUCCESS';
export const SEARCH_FETCH_FAIL = 'SEARCH_FETCH_FAIL'; export const SEARCH_FETCH_FAIL = 'SEARCH_FETCH_FAIL';
export const SEARCH_EXPAND_REQUEST = 'SEARCH_EXPAND_REQUEST';
export const SEARCH_EXPAND_SUCCESS = 'SEARCH_EXPAND_SUCCESS';
export const SEARCH_EXPAND_FAIL = 'SEARCH_EXPAND_FAIL';
export function changeSearch(value) { export function changeSearch(value) {
return { return {
type: SEARCH_CHANGE, type: SEARCH_CHANGE,
@ -77,8 +81,50 @@ export function fetchSearchFail(error) {
}; };
}; };
export function showSearch() { export const expandSearch = type => (dispatch, getState) => {
return { const value = getState().getIn(['search', 'value']);
type: SEARCH_SHOW, const offset = getState().getIn(['search', 'results', type]).size;
};
dispatch(expandSearchRequest());
api(getState).get('/api/v2/search', {
params: {
q: value,
type,
offset,
},
}).then(({ data }) => {
if (data.accounts) {
dispatch(importFetchedAccounts(data.accounts));
}
if (data.statuses) {
dispatch(importFetchedStatuses(data.statuses));
}
dispatch(expandSearchSuccess(data, value, type));
dispatch(fetchRelationships(data.accounts.map(item => item.id)));
}).catch(error => {
dispatch(expandSearchFail(error));
});
}; };
export const expandSearchRequest = () => ({
type: SEARCH_EXPAND_REQUEST,
});
export const expandSearchSuccess = (results, searchTerm, searchType) => ({
type: SEARCH_EXPAND_SUCCESS,
results,
searchTerm,
searchType,
});
export const expandSearchFail = error => ({
type: SEARCH_EXPAND_FAIL,
error,
});
export const showSearch = () => ({
type: SEARCH_SHOW,
});

View File

@ -8,6 +8,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import Hashtag from 'flavours/glitch/components/hashtag'; import Hashtag from 'flavours/glitch/components/hashtag';
import Icon from 'flavours/glitch/components/icon'; import Icon from 'flavours/glitch/components/icon';
import { searchEnabled } from 'flavours/glitch/util/initial_state'; import { searchEnabled } from 'flavours/glitch/util/initial_state';
import LoadMore from 'flavours/glitch/components/load_more';
const messages = defineMessages({ const messages = defineMessages({
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' }, dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
@ -20,14 +21,23 @@ class SearchResults extends ImmutablePureComponent {
results: ImmutablePropTypes.map.isRequired, results: ImmutablePropTypes.map.isRequired,
suggestions: ImmutablePropTypes.list.isRequired, suggestions: ImmutablePropTypes.list.isRequired,
fetchSuggestions: PropTypes.func.isRequired, fetchSuggestions: PropTypes.func.isRequired,
expandSearch: PropTypes.func.isRequired,
dismissSuggestion: PropTypes.func.isRequired, dismissSuggestion: PropTypes.func.isRequired,
searchTerm: PropTypes.string, searchTerm: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
}; };
componentDidMount () { componentDidMount () {
if (this.props.searchTerm === '') {
this.props.fetchSuggestions(); this.props.fetchSuggestions();
} }
}
handleLoadMoreAccounts = () => this.props.expandSearch('accounts');
handleLoadMoreStatuses = () => this.props.expandSearch('statuses');
handleLoadMoreHashtags = () => this.props.expandSearch('hashtags');
render () { render () {
const { intl, results, suggestions, dismissSuggestion, searchTerm } = this.props; const { intl, results, suggestions, dismissSuggestion, searchTerm } = this.props;
@ -75,6 +85,8 @@ class SearchResults extends ImmutablePureComponent {
<h5><Icon icon='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='People' /></h5> <h5><Icon icon='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='People' /></h5>
{results.get('accounts').map(accountId => <AccountContainer id={accountId} key={accountId} />)} {results.get('accounts').map(accountId => <AccountContainer id={accountId} key={accountId} />)}
{results.get('accounts').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreAccounts} />}
</section> </section>
); );
} }
@ -86,6 +98,8 @@ class SearchResults extends ImmutablePureComponent {
<h5><Icon icon='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5> <h5><Icon icon='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5>
{results.get('statuses').map(statusId => <StatusContainer id={statusId} key={statusId}/>)} {results.get('statuses').map(statusId => <StatusContainer id={statusId} key={statusId}/>)}
{results.get('statuses').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreStatuses} />}
</section> </section>
); );
} }
@ -97,6 +111,8 @@ class SearchResults extends ImmutablePureComponent {
<h5><Icon icon='hashtag' fixedWidth /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></h5> <h5><Icon icon='hashtag' fixedWidth /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></h5>
{results.get('hashtags').map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} {results.get('hashtags').map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
{results.get('hashtags').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreHashtags} />}
</section> </section>
); );
} }

View File

@ -1,6 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import SearchResults from '../components/search_results'; import SearchResults from '../components/search_results';
import { fetchSuggestions, dismissSuggestion } from '../../../actions/suggestions'; import { fetchSuggestions, dismissSuggestion } from 'mastodon/actions/suggestions';
import { expandSearch } from 'mastodon/actions/search';
const mapStateToProps = state => ({ const mapStateToProps = state => ({
results: state.getIn(['search', 'results']), results: state.getIn(['search', 'results']),
@ -10,6 +11,7 @@ const mapStateToProps = state => ({
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
fetchSuggestions: () => dispatch(fetchSuggestions()), fetchSuggestions: () => dispatch(fetchSuggestions()),
expandSearch: type => dispatch(expandSearch(type)),
dismissSuggestion: account => dispatch(dismissSuggestion(account.get('id'))), dismissSuggestion: account => dispatch(dismissSuggestion(account.get('id'))),
}); });

View File

@ -3,6 +3,7 @@ import {
SEARCH_CLEAR, SEARCH_CLEAR,
SEARCH_FETCH_SUCCESS, SEARCH_FETCH_SUCCESS,
SEARCH_SHOW, SEARCH_SHOW,
SEARCH_EXPAND_SUCCESS,
} from 'flavours/glitch/actions/search'; } from 'flavours/glitch/actions/search';
import { import {
COMPOSE_MENTION, COMPOSE_MENTION,
@ -42,6 +43,8 @@ export default function search(state = initialState, action) {
statuses: ImmutableList(action.results.statuses.map(item => item.id)), statuses: ImmutableList(action.results.statuses.map(item => item.id)),
hashtags: fromJS(action.results.hashtags), hashtags: fromJS(action.results.hashtags),
})).set('submitted', true).set('searchTerm', action.searchTerm); })).set('submitted', true).set('searchTerm', action.searchTerm);
case SEARCH_EXPAND_SUCCESS:
return state.updateIn(['results', action.searchType], list => list.concat(action.results[action.searchType].map(item => item.id)));
default: default:
return state; return state;
} }

View File

@ -79,8 +79,9 @@
} }
.search-results__info { .search-results__info {
padding: 10px; padding: 20px;
color: $secondary-text-color; color: $darker-text-color;
text-align: center;
} }
.trends { .trends {