diff --git a/app/soapbox/actions/search.js b/app/soapbox/actions/search.js
index 194f54e05..6f1621ad4 100644
--- a/app/soapbox/actions/search.js
+++ b/app/soapbox/actions/search.js
@@ -95,7 +95,7 @@ export const expandSearch = type => (dispatch, getState) => {
const value = getState().getIn(['search', 'value']);
const offset = getState().getIn(['search', 'results', type]).size;
- dispatch(expandSearchRequest());
+ dispatch(expandSearchRequest(type));
api(getState).get('/api/v2/search', {
params: {
@@ -119,8 +119,9 @@ export const expandSearch = type => (dispatch, getState) => {
});
};
-export const expandSearchRequest = () => ({
+export const expandSearchRequest = (searchType) => ({
type: SEARCH_EXPAND_REQUEST,
+ searchType,
});
export const expandSearchSuccess = (results, searchTerm, searchType) => ({
diff --git a/app/soapbox/features/compose/components/search_results.js b/app/soapbox/features/compose/components/search_results.js
index 25dd43c7b..6e144d55d 100644
--- a/app/soapbox/features/compose/components/search_results.js
+++ b/app/soapbox/features/compose/components/search_results.js
@@ -6,12 +6,13 @@ import AccountContainer from '../../../containers/account_container';
import StatusContainer from '../../../containers/status_container';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Hashtag from '../../../components/hashtag';
-import LoadingIndicator from 'soapbox/components/loading_indicator';
import FilterBar from '../../search/components/filter_bar';
-import LoadMore from '../../../components/load_more';
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
import { WhoToFollowPanel } from 'soapbox/features/ui/util/async-components';
-import classNames from 'classnames';
+import ScrollableList from 'soapbox/components/scrollable_list';
+import PlaceholderAccount from 'soapbox/features/placeholder/components/placeholder_account';
+import PlaceholderHashtag from 'soapbox/features/placeholder/components/placeholder_hashtag';
+import PlaceholderStatus from 'soapbox/features/placeholder/components/placeholder_status';
export default class SearchResults extends ImmutablePureComponent {
@@ -32,13 +33,7 @@ export default class SearchResults extends ImmutablePureComponent {
render() {
const { value, results, submitted, selectedFilter, features } = this.props;
- if (submitted && results.isEmpty()) {
- return (
-
-
-
- );
- } else if (features.suggestions && results.isEmpty()) {
+ if (!submitted && features.suggestions && results.isEmpty()) {
return (
{Component => }
@@ -48,68 +43,87 @@ export default class SearchResults extends ImmutablePureComponent {
let searchResults;
let hasMore = false;
+ let loaded;
+ let noResultsMessage;
+ let placeholderComponent = PlaceholderStatus;
if (selectedFilter === 'accounts' && results.get('accounts')) {
hasMore = results.get('accountsHasMore');
+ loaded = results.get('accountsLoaded');
+ placeholderComponent = PlaceholderAccount;
- searchResults = results.get('accounts').size > 0 ? (
-
- {results.get('accounts').map(accountId =>
)}
-
- ) : (
-
-
-
- );
+ if (results.get('accounts').size > 0) {
+ searchResults = results.get('accounts').map(accountId => );
+ } else {
+ noResultsMessage = (
+
+
+
+ );
+ }
}
if (selectedFilter === 'statuses' && results.get('statuses')) {
hasMore = results.get('statusesHasMore');
+ loaded = results.get('statusesLoaded');
- searchResults = results.get('statuses').size > 0 ? (
-
- {results.get('statuses').map(statusId => )}
-
- ) : (
-
-
-
- );
+ if (results.get('statuses').size > 0) {
+ searchResults = results.get('statuses').map(statusId => );
+ } else {
+ noResultsMessage = (
+
+
+
+ );
+ }
}
if (selectedFilter === 'hashtags' && results.get('hashtags')) {
hasMore = results.get('hashtagsHasMore');
+ loaded = results.get('hashtagsLoaded');
+ placeholderComponent = PlaceholderHashtag;
- searchResults = results.get('hashtags').size > 0 ? (
-
- {results.get('hashtags').map(hashtag => )}
-
- ) : (
-
-
-
- );
+ if (results.get('hashtags').size > 0) {
+ searchResults = results.get('hashtags').map(hashtag => );
+ } else {
+ noResultsMessage = (
+
+
+
+ );
+ }
}
return (
<>
{submitted && }
- {searchResults}
-
- {hasMore && }
+ {noResultsMessage || (
+
+ {searchResults}
+
+ )}
>
);
}
diff --git a/app/soapbox/features/placeholder/components/placeholder_hashtag.js b/app/soapbox/features/placeholder/components/placeholder_hashtag.js
new file mode 100644
index 000000000..66f11a479
--- /dev/null
+++ b/app/soapbox/features/placeholder/components/placeholder_hashtag.js
@@ -0,0 +1,20 @@
+import React from 'react';
+import { generateText, randomIntFromInterval } from '../utils';
+
+export default class PlaceholderHashtag extends React.Component {
+
+ render() {
+ const length = randomIntFromInterval(15, 30);
+
+ return (
+
+
+
+ {generateText(length)}
+
+
+
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/app/soapbox/reducers/search.js b/app/soapbox/reducers/search.js
index 104f7dc2d..d4c3f1a59 100644
--- a/app/soapbox/reducers/search.js
+++ b/app/soapbox/reducers/search.js
@@ -5,6 +5,7 @@ import {
SEARCH_FETCH_SUCCESS,
SEARCH_SHOW,
SEARCH_FILTER_SET,
+ SEARCH_EXPAND_REQUEST,
SEARCH_EXPAND_SUCCESS,
} from '../actions/search';
import {
@@ -28,7 +29,6 @@ export default function search(state = initialState, action) {
case SEARCH_CHANGE:
return state.withMutations(map => {
map.set('value', action.value);
- map.set('submitted', false);
});
case SEARCH_CLEAR:
return state.withMutations(map => {
@@ -58,6 +58,9 @@ export default function search(state = initialState, action) {
accountsHasMore: action.results.accounts.length >= 20,
statusesHasMore: action.results.statuses.length >= 20,
hashtagsHasMore: action.results.hashtags.length >= 20,
+ accountsLoaded: true,
+ statusesLoaded: true,
+ hashtagsLoaded: true,
})).set('submitted', true).set('filter', action.results.accounts.length > 0
? 'accounts'
: action.results.statuses.length > 0
@@ -67,9 +70,12 @@ export default function search(state = initialState, action) {
: 'accounts');
case SEARCH_FILTER_SET:
return state.set('filter', action.value);
+ case SEARCH_EXPAND_REQUEST:
+ return state.setIn(['results', `${action.searchType}Loaded`], false);
case SEARCH_EXPAND_SUCCESS:
return state.withMutations((state) => {
state.setIn(['results', `${action.searchType}HasMore`], action.results[action.searchType].length >= 20);
+ state.setIn(['results', `${action.searchType}Loaded`], true);
state.updateIn(['results', action.searchType], list => list.concat(action.results[action.searchType].map(item => item.id)));
});
default:
diff --git a/app/styles/placeholder.scss b/app/styles/placeholder.scss
index 3b5a3a4aa..0427e9463 100644
--- a/app/styles/placeholder.scss
+++ b/app/styles/placeholder.scss
@@ -1,4 +1,5 @@
.placeholder-status,
+.placeholder-hashtag,
.notification--placeholder {
position: relative;