sforkowany z mirror/soapbox
Use ScrollableList for search results
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>draftjs
rodzic
dde588faa8
commit
742b1f2b58
|
@ -95,7 +95,7 @@ export const expandSearch = type => (dispatch, getState) => {
|
||||||
const value = getState().getIn(['search', 'value']);
|
const value = getState().getIn(['search', 'value']);
|
||||||
const offset = getState().getIn(['search', 'results', type]).size;
|
const offset = getState().getIn(['search', 'results', type]).size;
|
||||||
|
|
||||||
dispatch(expandSearchRequest());
|
dispatch(expandSearchRequest(type));
|
||||||
|
|
||||||
api(getState).get('/api/v2/search', {
|
api(getState).get('/api/v2/search', {
|
||||||
params: {
|
params: {
|
||||||
|
@ -119,8 +119,9 @@ export const expandSearch = type => (dispatch, getState) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const expandSearchRequest = () => ({
|
export const expandSearchRequest = (searchType) => ({
|
||||||
type: SEARCH_EXPAND_REQUEST,
|
type: SEARCH_EXPAND_REQUEST,
|
||||||
|
searchType,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const expandSearchSuccess = (results, searchTerm, searchType) => ({
|
export const expandSearchSuccess = (results, searchTerm, searchType) => ({
|
||||||
|
|
|
@ -6,12 +6,13 @@ import AccountContainer from '../../../containers/account_container';
|
||||||
import StatusContainer from '../../../containers/status_container';
|
import StatusContainer from '../../../containers/status_container';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import Hashtag from '../../../components/hashtag';
|
import Hashtag from '../../../components/hashtag';
|
||||||
import LoadingIndicator from 'soapbox/components/loading_indicator';
|
|
||||||
import FilterBar from '../../search/components/filter_bar';
|
import FilterBar from '../../search/components/filter_bar';
|
||||||
import LoadMore from '../../../components/load_more';
|
|
||||||
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
|
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
|
||||||
import { WhoToFollowPanel } from 'soapbox/features/ui/util/async-components';
|
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 {
|
export default class SearchResults extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
@ -32,13 +33,7 @@ export default class SearchResults extends ImmutablePureComponent {
|
||||||
render() {
|
render() {
|
||||||
const { value, results, submitted, selectedFilter, features } = this.props;
|
const { value, results, submitted, selectedFilter, features } = this.props;
|
||||||
|
|
||||||
if (submitted && results.isEmpty()) {
|
if (!submitted && features.suggestions && results.isEmpty()) {
|
||||||
return (
|
|
||||||
<div className='search-results'>
|
|
||||||
<LoadingIndicator />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else if (features.suggestions && results.isEmpty()) {
|
|
||||||
return (
|
return (
|
||||||
<BundleContainer fetchComponent={WhoToFollowPanel}>
|
<BundleContainer fetchComponent={WhoToFollowPanel}>
|
||||||
{Component => <Component limit={5} />}
|
{Component => <Component limit={5} />}
|
||||||
|
@ -48,15 +43,19 @@ export default class SearchResults extends ImmutablePureComponent {
|
||||||
|
|
||||||
let searchResults;
|
let searchResults;
|
||||||
let hasMore = false;
|
let hasMore = false;
|
||||||
|
let loaded;
|
||||||
|
let noResultsMessage;
|
||||||
|
let placeholderComponent = PlaceholderStatus;
|
||||||
|
|
||||||
if (selectedFilter === 'accounts' && results.get('accounts')) {
|
if (selectedFilter === 'accounts' && results.get('accounts')) {
|
||||||
hasMore = results.get('accountsHasMore');
|
hasMore = results.get('accountsHasMore');
|
||||||
|
loaded = results.get('accountsLoaded');
|
||||||
|
placeholderComponent = PlaceholderAccount;
|
||||||
|
|
||||||
searchResults = results.get('accounts').size > 0 ? (
|
if (results.get('accounts').size > 0) {
|
||||||
<div className={classNames('search-results__section', { 'has-more': hasMore })}>
|
searchResults = results.get('accounts').map(accountId => <AccountContainer key={accountId} id={accountId} />);
|
||||||
{results.get('accounts').map(accountId => <AccountContainer key={accountId} id={accountId} />)}
|
} else {
|
||||||
</div>
|
noResultsMessage = (
|
||||||
) : (
|
|
||||||
<div className='empty-column-indicator'>
|
<div className='empty-column-indicator'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='empty_column.search.accounts'
|
id='empty_column.search.accounts'
|
||||||
|
@ -66,15 +65,16 @@ export default class SearchResults extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedFilter === 'statuses' && results.get('statuses')) {
|
if (selectedFilter === 'statuses' && results.get('statuses')) {
|
||||||
hasMore = results.get('statusesHasMore');
|
hasMore = results.get('statusesHasMore');
|
||||||
|
loaded = results.get('statusesLoaded');
|
||||||
|
|
||||||
searchResults = results.get('statuses').size > 0 ? (
|
if (results.get('statuses').size > 0) {
|
||||||
<div className={classNames('search-results__section', { 'has-more': hasMore })}>
|
searchResults = results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />);
|
||||||
{results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />)}
|
} else {
|
||||||
</div>
|
noResultsMessage = (
|
||||||
) : (
|
|
||||||
<div className='empty-column-indicator'>
|
<div className='empty-column-indicator'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='empty_column.search.statuses'
|
id='empty_column.search.statuses'
|
||||||
|
@ -84,15 +84,17 @@ export default class SearchResults extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedFilter === 'hashtags' && results.get('hashtags')) {
|
if (selectedFilter === 'hashtags' && results.get('hashtags')) {
|
||||||
hasMore = results.get('hashtagsHasMore');
|
hasMore = results.get('hashtagsHasMore');
|
||||||
|
loaded = results.get('hashtagsLoaded');
|
||||||
|
placeholderComponent = PlaceholderHashtag;
|
||||||
|
|
||||||
searchResults = results.get('hashtags').size > 0 ? (
|
if (results.get('hashtags').size > 0) {
|
||||||
<div className={classNames('search-results__section', { 'has-more': hasMore })}>
|
searchResults = results.get('hashtags').map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />);
|
||||||
{results.get('hashtags').map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
|
} else {
|
||||||
</div>
|
noResultsMessage = (
|
||||||
) : (
|
|
||||||
<div className='empty-column-indicator'>
|
<div className='empty-column-indicator'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='empty_column.search.hashtags'
|
id='empty_column.search.hashtags'
|
||||||
|
@ -102,14 +104,26 @@ export default class SearchResults extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{submitted && <FilterBar selectedFilter={submitted ? selectedFilter : null} selectFilter={this.handleSelectFilter} />}
|
{submitted && <FilterBar selectedFilter={submitted ? selectedFilter : null} selectFilter={this.handleSelectFilter} />}
|
||||||
|
|
||||||
|
{noResultsMessage || (
|
||||||
|
<ScrollableList
|
||||||
|
key={selectedFilter}
|
||||||
|
scrollKey={`${selectedFilter}:${value}`}
|
||||||
|
isLoading={submitted && !loaded}
|
||||||
|
showLoading={submitted && !loaded && results.isEmpty()}
|
||||||
|
hasMore={hasMore}
|
||||||
|
onLoadMore={this.handleLoadMore}
|
||||||
|
placeholderComponent={placeholderComponent}
|
||||||
|
placeholderCount={20}
|
||||||
|
>
|
||||||
{searchResults}
|
{searchResults}
|
||||||
|
</ScrollableList>
|
||||||
{hasMore && <LoadMore visible onClick={this.handleLoadMore} />}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 (
|
||||||
|
<div className='placeholder-hashtag'>
|
||||||
|
<div className='trends__item'>
|
||||||
|
<div className='trends__item__name'>
|
||||||
|
{generateText(length)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import {
|
||||||
SEARCH_FETCH_SUCCESS,
|
SEARCH_FETCH_SUCCESS,
|
||||||
SEARCH_SHOW,
|
SEARCH_SHOW,
|
||||||
SEARCH_FILTER_SET,
|
SEARCH_FILTER_SET,
|
||||||
|
SEARCH_EXPAND_REQUEST,
|
||||||
SEARCH_EXPAND_SUCCESS,
|
SEARCH_EXPAND_SUCCESS,
|
||||||
} from '../actions/search';
|
} from '../actions/search';
|
||||||
import {
|
import {
|
||||||
|
@ -28,7 +29,6 @@ export default function search(state = initialState, action) {
|
||||||
case SEARCH_CHANGE:
|
case SEARCH_CHANGE:
|
||||||
return state.withMutations(map => {
|
return state.withMutations(map => {
|
||||||
map.set('value', action.value);
|
map.set('value', action.value);
|
||||||
map.set('submitted', false);
|
|
||||||
});
|
});
|
||||||
case SEARCH_CLEAR:
|
case SEARCH_CLEAR:
|
||||||
return state.withMutations(map => {
|
return state.withMutations(map => {
|
||||||
|
@ -58,6 +58,9 @@ export default function search(state = initialState, action) {
|
||||||
accountsHasMore: action.results.accounts.length >= 20,
|
accountsHasMore: action.results.accounts.length >= 20,
|
||||||
statusesHasMore: action.results.statuses.length >= 20,
|
statusesHasMore: action.results.statuses.length >= 20,
|
||||||
hashtagsHasMore: action.results.hashtags.length >= 20,
|
hashtagsHasMore: action.results.hashtags.length >= 20,
|
||||||
|
accountsLoaded: true,
|
||||||
|
statusesLoaded: true,
|
||||||
|
hashtagsLoaded: true,
|
||||||
})).set('submitted', true).set('filter', action.results.accounts.length > 0
|
})).set('submitted', true).set('filter', action.results.accounts.length > 0
|
||||||
? 'accounts'
|
? 'accounts'
|
||||||
: action.results.statuses.length > 0
|
: action.results.statuses.length > 0
|
||||||
|
@ -67,9 +70,12 @@ export default function search(state = initialState, action) {
|
||||||
: 'accounts');
|
: 'accounts');
|
||||||
case SEARCH_FILTER_SET:
|
case SEARCH_FILTER_SET:
|
||||||
return state.set('filter', action.value);
|
return state.set('filter', action.value);
|
||||||
|
case SEARCH_EXPAND_REQUEST:
|
||||||
|
return state.setIn(['results', `${action.searchType}Loaded`], false);
|
||||||
case SEARCH_EXPAND_SUCCESS:
|
case SEARCH_EXPAND_SUCCESS:
|
||||||
return state.withMutations((state) => {
|
return state.withMutations((state) => {
|
||||||
state.setIn(['results', `${action.searchType}HasMore`], action.results[action.searchType].length >= 20);
|
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)));
|
state.updateIn(['results', action.searchType], list => list.concat(action.results[action.searchType].map(item => item.id)));
|
||||||
});
|
});
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
.placeholder-status,
|
.placeholder-status,
|
||||||
|
.placeholder-hashtag,
|
||||||
.notification--placeholder {
|
.notification--placeholder {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue