Add search results pagination to web UI (#11409)
* Add search results pagination to web UI Fix #10737 * Fix code style issue
This commit is contained in:
		
					parent
					
						
							
								b9fbcbfe4e
							
						
					
				
			
			
				commit
				
					
						8a4674f2c3
					
				
			
		
					 5 changed files with 76 additions and 8 deletions
				
			
		|  | @ -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,14 +21,23 @@ 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 () { | ||||
|     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 { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue