diff --git a/app/soapbox/actions/admin.js b/app/soapbox/actions/admin.js index 43119708d..ee957948d 100644 --- a/app/soapbox/actions/admin.js +++ b/app/soapbox/actions/admin.js @@ -125,9 +125,10 @@ export function closeReports(ids) { return patchReports(ids, 'closed'); } -export function fetchUsers(filters = [], page = 1, pageSize = 50) { +export function fetchUsers(filters = [], page = 1, query, pageSize = 50) { return (dispatch, getState) => { const params = { filters: filters.join(), page, page_size: pageSize }; + if (query) params.query = query; dispatch({ type: ADMIN_USERS_FETCH_REQUEST, filters, page, pageSize }); return api(getState) diff --git a/app/soapbox/features/admin/user_index.js b/app/soapbox/features/admin/user_index.js index 71a173822..8ae051080 100644 --- a/app/soapbox/features/admin/user_index.js +++ b/app/soapbox/features/admin/user_index.js @@ -4,13 +4,20 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import PropTypes from 'prop-types'; import { debounce } from 'lodash'; import { fetchUsers } from 'soapbox/actions/admin'; -import { FormattedMessage } from 'react-intl'; +import { injectIntl, defineMessages } from 'react-intl'; import AccountContainer from 'soapbox/containers/account_container'; import Column from 'soapbox/features/ui/components/column'; import ScrollableList from 'soapbox/components/scrollable_list'; +import { SimpleForm, TextInput } from 'soapbox/features/forms'; import { Set as ImmutableSet, OrderedSet as ImmutableOrderedSet, is } from 'immutable'; +const messages = defineMessages({ + empty: { id: 'admin.user_index.empty', defaultMessage: 'No users found.' }, + searchPlaceholder: { id: 'admin.user_index.search_input_placeholder', defaultMessage: 'Who are you looking for?' }, +}); + export default @connect() +@injectIntl class UserIndex extends ImmutablePureComponent { static propTypes = { @@ -24,20 +31,22 @@ class UserIndex extends ImmutablePureComponent { total: Infinity, pageSize: 50, page: 0, + query: '', } - clearState = () => { + clearState = callback => { this.setState({ isLoading: true, + accountIds: ImmutableOrderedSet(), page: 0, - }); + }, callback); } fetchNextPage = () => { - const { filters, page, pageSize } = this.state; + const { filters, page, query, pageSize } = this.state; const nextPage = page + 1; - this.props.dispatch(fetchUsers(filters, nextPage, pageSize)) + this.props.dispatch(fetchUsers(filters, nextPage, query, pageSize)) .then(({ users, count }) => { const newIds = users.map(user => user.id); @@ -55,12 +64,19 @@ class UserIndex extends ImmutablePureComponent { this.fetchNextPage(); } + refresh = () => { + this.clearState(() => { + this.fetchNextPage(); + }); + } + componentDidUpdate(prevProps, prevState) { - const { filters, q } = this.state; + const { filters, query } = this.state; + const filtersChanged = !is(filters, prevState.filters); + const queryChanged = query !== prevState.query; - if (!is(filters, prevState.filters) || !is(q, prevState.q)) { - this.clearState(); - this.fetchNextPage(); + if (filtersChanged || queryChanged) { + this.refresh(); } } @@ -68,7 +84,16 @@ class UserIndex extends ImmutablePureComponent { this.fetchNextPage(); }, 2000, { leading: true }); + updateQuery = debounce(query => { + this.setState({ query }); + }, 900) + + handleQueryChange = e => { + this.updateQuery(e.target.value); + }; + render() { + const { intl } = this.props; const { accountIds, isLoading } = this.state; const hasMore = accountIds.count() < this.state.total; @@ -76,13 +101,20 @@ class UserIndex extends ImmutablePureComponent { return ( + + + } + emptyMessage={intl.formatMessage(messages.empty)} > {accountIds.map(id => ,