diff --git a/app/soapbox/actions/search.js b/app/soapbox/actions/search.js
index ee7bc1657..c974819fc 100644
--- a/app/soapbox/actions/search.js
+++ b/app/soapbox/actions/search.js
@@ -10,6 +10,12 @@ 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 const SEARCH_FILTER_SET = 'SEARCH_FILTER_SET';
+
export function changeSearch(value) {
return {
type: SEARCH_CHANGE,
@@ -76,8 +82,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,
+});
diff --git a/app/soapbox/features/compose/components/search_results.js b/app/soapbox/features/compose/components/search_results.js
index c7838f96a..c6d990734 100644
--- a/app/soapbox/features/compose/components/search_results.js
+++ b/app/soapbox/features/compose/components/search_results.js
@@ -8,6 +8,9 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import Hashtag from '../../../components/hashtag';
import Icon from 'soapbox/components/icon';
import LoadingIndicator from 'soapbox/components/loading_indicator';
+import FilterBar from '../../search/components/filter_bar';
+import LoadMore from '../../../components/load_more';
+import classNames from 'classnames';
export default @injectIntl
class SearchResults extends ImmutablePureComponent {
@@ -15,11 +18,24 @@ class SearchResults extends ImmutablePureComponent {
static propTypes = {
results: ImmutablePropTypes.map.isRequired,
submitted: PropTypes.bool,
+ expandSearch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
+ state = {
+ selectedFilter: 'accounts',
+ };
+
+ handleLoadMore = () => this.props.expandSearch(this.state.selectedFilter);
+
+ handleSelectFilter = newActiveFilter => {
+ console.log(newActiveFilter);
+ this.setState({ selectedFilter: newActiveFilter });
+ };
+
render() {
const { results, submitted } = this.props;
+ const { selectedFilter } = this.state;
if (submitted && results.isEmpty()) {
return (
@@ -29,37 +45,38 @@ class SearchResults extends ImmutablePureComponent {
);
}
- let accounts, statuses, hashtags;
+ let searchResults;
let count = 0;
+ let hasMore = false;
- if (results.get('accounts') && results.get('accounts').size > 0) {
- count += results.get('accounts').size;
- accounts = (
-
-
+ if (selectedFilter === 'accounts' && results.get('accounts') && results.get('accounts').size > 0) {
+ count = results.get('accounts').size;
+ hasMore = results.get('accountsHasMore');
+ searchResults = (
+
{results.get('accounts').map(accountId =>
)}
);
}
- if (results.get('statuses') && results.get('statuses').size > 0) {
- count += results.get('statuses').size;
- statuses = (
-
-
+ if (selectedFilter === 'statuses' && results.get('statuses') && results.get('statuses').size > 0) {
+ count = results.get('statuses').size;
+ hasMore = results.get('statusesHasMore');
+ searchResults = (
+
{results.get('statuses').map(statusId => )}
);
}
- if (results.get('hashtags') && results.get('hashtags').size > 0) {
- count += results.get('hashtags').size;
- hashtags = (
-
-
+ if (selectedFilter === 'hashtags' && results.get('hashtags') && results.get('hashtags').size > 0) {
+ count = results.get('hashtags').size;
+ hasMore = results.get('hashtagsHasMore');
+ searchResults = (
+
{results.get('hashtags').map(hashtag => )}
);
@@ -72,9 +89,11 @@ class SearchResults extends ImmutablePureComponent {
- {accounts}
- {statuses}
- {hashtags}
+
+
+ {searchResults}
+
+ {hasMore &&
}
);
}
diff --git a/app/soapbox/features/compose/containers/search_results_container.js b/app/soapbox/features/compose/containers/search_results_container.js
index 046e374ac..734612dce 100644
--- a/app/soapbox/features/compose/containers/search_results_container.js
+++ b/app/soapbox/features/compose/containers/search_results_container.js
@@ -1,15 +1,19 @@
import { connect } from 'react-redux';
import SearchResults from '../components/search_results';
import { fetchSuggestions, dismissSuggestion } from '../../../actions/suggestions';
+import { expandSearch } from '../../../actions/search';
-const mapStateToProps = state => ({
- results: state.getIn(['search', 'results']),
- suggestions: state.getIn(['suggestions', 'items']),
- submitted: state.getIn(['search', 'submitted']),
-});
+const mapStateToProps = state => {
+ return {
+ results: state.getIn(['search', 'results']),
+ suggestions: state.getIn(['suggestions', 'items']),
+ submitted: state.getIn(['search', 'submitted']),
+ };
+};
const mapDispatchToProps = dispatch => ({
fetchSuggestions: () => dispatch(fetchSuggestions()),
+ expandSearch: type => dispatch(expandSearch(type)),
dismissSuggestion: account => dispatch(dismissSuggestion(account.get('id'))),
});
diff --git a/app/soapbox/features/search/components/filter_bar.js b/app/soapbox/features/search/components/filter_bar.js
new file mode 100644
index 000000000..917ad99c7
--- /dev/null
+++ b/app/soapbox/features/search/components/filter_bar.js
@@ -0,0 +1,53 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { FormattedMessage, injectIntl } from 'react-intl';
+
+export default @injectIntl
+class FilterBar extends React.PureComponent {
+
+ static propTypes = {
+ selectFilter: PropTypes.func.isRequired,
+ selectedFilter: PropTypes.string.isRequired,
+ };
+
+ onClick(searchType) {
+ return () => this.props.selectFilter(searchType);
+ }
+
+ render() {
+ const { selectedFilter } = this.props;
+
+ return (
+
+
+
+
+
+ );
+ }
+
+}
diff --git a/app/soapbox/reducers/search.js b/app/soapbox/reducers/search.js
index 4a0a729a1..7a44956c2 100644
--- a/app/soapbox/reducers/search.js
+++ b/app/soapbox/reducers/search.js
@@ -4,6 +4,7 @@ import {
SEARCH_FETCH_REQUEST,
SEARCH_FETCH_SUCCESS,
SEARCH_SHOW,
+ SEARCH_EXPAND_SUCCESS,
} from '../actions/search';
import {
COMPOSE_MENTION,
@@ -49,7 +50,15 @@ export default function search(state = initialState, action) {
accounts: ImmutableList(action.results.accounts.map(item => item.id)),
statuses: ImmutableList(action.results.statuses.map(item => item.id)),
hashtags: fromJS(action.results.hashtags),
+ accountsHasMore: action.results.accounts.length > 0,
+ statusesHasMore: action.results.statuses.length > 0,
+ hashtagsHasMore: action.results.hashtags.length > 0,
})).set('submitted', true);
+ case SEARCH_EXPAND_SUCCESS:
+ return state.withMutations((state) => {
+ state.setIn(['results', `${action.searchType}HasMore`], action.results[action.searchType].length > 0);
+ state.updateIn(['results', action.searchType], list => list.concat(action.results[action.searchType].map(item => item.id)));
+ });
default:
return state;
}
diff --git a/app/soapbox/reducers/settings.js b/app/soapbox/reducers/settings.js
index a409f5905..343a3f9d9 100644
--- a/app/soapbox/reducers/settings.js
+++ b/app/soapbox/reducers/settings.js
@@ -1,5 +1,6 @@
import { SETTING_CHANGE, SETTING_SAVE, FE_NAME } from '../actions/settings';
import { NOTIFICATIONS_FILTER_SET } from '../actions/notifications';
+import { SEARCH_FILTER_SET } from '../actions/search';
import { EMOJI_USE } from '../actions/emojis';
import { ME_FETCH_SUCCESS } from 'soapbox/actions/me';
import { Map as ImmutableMap, fromJS } from 'immutable';
@@ -25,6 +26,7 @@ export default function settings(state = initialState, action) {
case ME_FETCH_SUCCESS:
return importSettings(state, action.me);
case NOTIFICATIONS_FILTER_SET:
+ case SEARCH_FILTER_SET:
case SETTING_CHANGE:
return state
.setIn(action.path, action.value)
diff --git a/app/styles/components/search.scss b/app/styles/components/search.scss
index 6933c6006..ffc46b2b0 100644
--- a/app/styles/components/search.scss
+++ b/app/styles/components/search.scss
@@ -68,8 +68,6 @@
}
.search-results__section {
- margin-bottom: 5px;
-
h5 {
background: var(--accent-color--faint);
border-bottom: 1px solid var(--brand-color--faint);
@@ -86,8 +84,8 @@
}
}
- .account:last-child,
- & > div:last-child .status {
+ &:not(.has-more) .account:last-child,
+ &:not(.has-more) > div:last-child .status {
border-bottom: 0;
}
}
diff --git a/app/styles/ui.scss b/app/styles/ui.scss
index 5a6b3e554..86cf3361d 100644
--- a/app/styles/ui.scss
+++ b/app/styles/ui.scss
@@ -608,6 +608,7 @@
}
.notification__filter-bar,
+.search__filter-bar,
.account__section-headline {
border-bottom: 1px solid var(--brand-color--faint);
cursor: default;