* Add search results pagination to web UI Fix #10737 * Fix code style issuemaster^2
@@ -10,6 +10,10 @@ export const SEARCH_FETCH_REQUEST = 'SEARCH_FETCH_REQUEST'; | |||
export const SEARCH_FETCH_SUCCESS = 'SEARCH_FETCH_SUCCESS'; | |||
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) { | |||
return { | |||
type: SEARCH_CHANGE, | |||
@@ -77,8 +81,50 @@ export function fetchSearchFail(error) { | |||
}; | |||
}; | |||
export function showSearch() { | |||
return { | |||
type: SEARCH_SHOW, | |||
}; | |||
export const expandSearch = type => (dispatch, getState) => { | |||
const value = getState().getIn(['search', 'value']); | |||
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, | |||
}); |
@@ -8,6 +8,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; | |||
import Hashtag from '../../../components/hashtag'; | |||
import Icon from 'mastodon/components/icon'; | |||
import { searchEnabled } from '../../../initial_state'; | |||
import LoadMore from 'mastodon/components/load_more'; | |||
const messages = defineMessages({ | |||
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' }, | |||
@@ -20,15 +21,24 @@ class SearchResults extends ImmutablePureComponent { | |||
results: ImmutablePropTypes.map.isRequired, | |||
suggestions: ImmutablePropTypes.list.isRequired, | |||
fetchSuggestions: PropTypes.func.isRequired, | |||
expandSearch: PropTypes.func.isRequired, | |||
dismissSuggestion: PropTypes.func.isRequired, | |||
searchTerm: PropTypes.string, | |||
intl: PropTypes.object.isRequired, | |||
}; | |||
componentDidMount () { | |||
this.props.fetchSuggestions(); | |||
if (this.props.searchTerm === '') { | |||
this.props.fetchSuggestions(); | |||
} | |||
} | |||
handleLoadMoreAccounts = () => this.props.expandSearch('accounts'); | |||
handleLoadMoreStatuses = () => this.props.expandSearch('statuses'); | |||
handleLoadMoreHashtags = () => this.props.expandSearch('hashtags'); | |||
render () { | |||
const { intl, results, suggestions, dismissSuggestion, searchTerm } = this.props; | |||
@@ -65,6 +75,8 @@ class SearchResults extends ImmutablePureComponent { | |||
<h5><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='People' /></h5> | |||
{results.get('accounts').map(accountId => <AccountContainer key={accountId} id={accountId} />)} | |||
{results.get('accounts').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreAccounts} />} | |||
</div> | |||
); | |||
} | |||
@@ -76,6 +88,8 @@ class SearchResults extends ImmutablePureComponent { | |||
<h5><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5> | |||
{results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />)} | |||
{results.get('statuses').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreStatuses} />} | |||
</div> | |||
); | |||
} else if(results.get('statuses') && results.get('statuses').size === 0 && !searchEnabled && !(searchTerm.startsWith('@') || searchTerm.startsWith('#') || searchTerm.includes(' '))) { | |||
@@ -97,6 +111,8 @@ class SearchResults extends ImmutablePureComponent { | |||
<h5><Icon id='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').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreHashtags} />} | |||
</div> | |||
); | |||
} | |||
@@ -1,6 +1,7 @@ | |||
import { connect } from 'react-redux'; | |||
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 => ({ | |||
results: state.getIn(['search', 'results']), | |||
@@ -10,6 +11,7 @@ const mapStateToProps = state => ({ | |||
const mapDispatchToProps = dispatch => ({ | |||
fetchSuggestions: () => dispatch(fetchSuggestions()), | |||
expandSearch: type => dispatch(expandSearch(type)), | |||
dismissSuggestion: account => dispatch(dismissSuggestion(account.get('id'))), | |||
}); | |||
@@ -3,6 +3,7 @@ import { | |||
SEARCH_CLEAR, | |||
SEARCH_FETCH_SUCCESS, | |||
SEARCH_SHOW, | |||
SEARCH_EXPAND_SUCCESS, | |||
} from '../actions/search'; | |||
import { | |||
COMPOSE_MENTION, | |||
@@ -42,6 +43,8 @@ export default function search(state = initialState, action) { | |||
statuses: ImmutableList(action.results.statuses.map(item => item.id)), | |||
hashtags: fromJS(action.results.hashtags), | |||
})).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: | |||
return state; | |||
} | |||
@@ -4006,8 +4006,9 @@ a.status-card.compact:hover { | |||
} | |||
.search-results__info { | |||
padding: 10px; | |||
color: $secondary-text-color; | |||
padding: 20px; | |||
color: $darker-text-color; | |||
text-align: center; | |||
} | |||
.modal-root { | |||