results.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import ImmutablePropTypes from 'react-immutable-proptypes';
  4. import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
  5. import { connect } from 'react-redux';
  6. import { expandSearch } from 'mastodon/actions/search';
  7. import Account from 'mastodon/containers/account_container';
  8. import Status from 'mastodon/containers/status_container';
  9. import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
  10. import { List as ImmutableList } from 'immutable';
  11. import LoadMore from 'mastodon/components/load_more';
  12. import LoadingIndicator from 'mastodon/components/loading_indicator';
  13. import { Helmet } from 'react-helmet';
  14. const messages = defineMessages({
  15. title: { id: 'search_results.title', defaultMessage: 'Search for {q}' },
  16. });
  17. const mapStateToProps = state => ({
  18. isLoading: state.getIn(['search', 'isLoading']),
  19. results: state.getIn(['search', 'results']),
  20. q: state.getIn(['search', 'searchTerm']),
  21. });
  22. const appendLoadMore = (id, list, onLoadMore) => {
  23. if (list.size >= 5) {
  24. return list.push(<LoadMore key={`${id}-load-more`} visible onClick={onLoadMore} />);
  25. } else {
  26. return list;
  27. }
  28. };
  29. const renderAccounts = (results, onLoadMore) => appendLoadMore('accounts', results.get('accounts', ImmutableList()).map(item => (
  30. <Account key={`account-${item}`} id={item} />
  31. )), onLoadMore);
  32. const renderHashtags = (results, onLoadMore) => appendLoadMore('hashtags', results.get('hashtags', ImmutableList()).map(item => (
  33. <Hashtag key={`tag-${item.get('name')}`} hashtag={item} />
  34. )), onLoadMore);
  35. const renderStatuses = (results, onLoadMore) => appendLoadMore('statuses', results.get('statuses', ImmutableList()).map(item => (
  36. <Status key={`status-${item}`} id={item} />
  37. )), onLoadMore);
  38. export default @connect(mapStateToProps)
  39. @injectIntl
  40. class Results extends React.PureComponent {
  41. static propTypes = {
  42. results: ImmutablePropTypes.map,
  43. isLoading: PropTypes.bool,
  44. multiColumn: PropTypes.bool,
  45. dispatch: PropTypes.func.isRequired,
  46. q: PropTypes.string,
  47. intl: PropTypes.object,
  48. };
  49. state = {
  50. type: 'all',
  51. };
  52. handleSelectAll = () => this.setState({ type: 'all' });
  53. handleSelectAccounts = () => this.setState({ type: 'accounts' });
  54. handleSelectHashtags = () => this.setState({ type: 'hashtags' });
  55. handleSelectStatuses = () => this.setState({ type: 'statuses' });
  56. handleLoadMoreAccounts = () => this.loadMore('accounts');
  57. handleLoadMoreStatuses = () => this.loadMore('statuses');
  58. handleLoadMoreHashtags = () => this.loadMore('hashtags');
  59. loadMore (type) {
  60. const { dispatch } = this.props;
  61. dispatch(expandSearch(type));
  62. }
  63. render () {
  64. const { intl, isLoading, q, results } = this.props;
  65. const { type } = this.state;
  66. let filteredResults = ImmutableList();
  67. if (!isLoading) {
  68. switch(type) {
  69. case 'all':
  70. filteredResults = filteredResults.concat(renderAccounts(results, this.handleLoadMoreAccounts), renderHashtags(results, this.handleLoadMoreHashtags), renderStatuses(results, this.handleLoadMoreStatuses));
  71. break;
  72. case 'accounts':
  73. filteredResults = filteredResults.concat(renderAccounts(results, this.handleLoadMoreAccounts));
  74. break;
  75. case 'hashtags':
  76. filteredResults = filteredResults.concat(renderHashtags(results, this.handleLoadMoreHashtags));
  77. break;
  78. case 'statuses':
  79. filteredResults = filteredResults.concat(renderStatuses(results, this.handleLoadMoreStatuses));
  80. break;
  81. }
  82. if (filteredResults.size === 0) {
  83. filteredResults = (
  84. <div className='empty-column-indicator'>
  85. <FormattedMessage id='search_results.nothing_found' defaultMessage='Could not find anything for these search terms' />
  86. </div>
  87. );
  88. }
  89. }
  90. return (
  91. <React.Fragment>
  92. <div className='account__section-headline'>
  93. <button onClick={this.handleSelectAll} className={type === 'all' && 'active'}><FormattedMessage id='search_results.all' defaultMessage='All' /></button>
  94. <button onClick={this.handleSelectAccounts} className={type === 'accounts' && 'active'}><FormattedMessage id='search_results.accounts' defaultMessage='People' /></button>
  95. <button onClick={this.handleSelectHashtags} className={type === 'hashtags' && 'active'}><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></button>
  96. <button onClick={this.handleSelectStatuses} className={type === 'statuses' && 'active'}><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></button>
  97. </div>
  98. <div className='explore__search-results'>
  99. {isLoading ? <LoadingIndicator /> : filteredResults}
  100. </div>
  101. <Helmet>
  102. <title>{intl.formatMessage(messages.title, { q })}</title>
  103. </Helmet>
  104. </React.Fragment>
  105. );
  106. }
  107. }