Signed-off-by: marcin mikołajczak <git@mkljczk.pl>environments/review-cleanup-4am6ej/deployments/1865
parent
c692265249
commit
4b3f03353d
@ -1,132 +0,0 @@
|
||||
import { Set as ImmutableSet, OrderedSet as ImmutableOrderedSet, is } from 'immutable';
|
||||
import debounce from 'lodash/debounce';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { fetchUsers } from 'soapbox/actions/admin';
|
||||
import ScrollableList from 'soapbox/components/scrollable-list';
|
||||
import { Column } from 'soapbox/components/ui';
|
||||
import AccountContainer from 'soapbox/containers/account-container';
|
||||
import { SimpleForm, TextInput } from 'soapbox/features/forms';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.admin.users', defaultMessage: 'Users' },
|
||||
empty: { id: 'admin.user_index.empty', defaultMessage: 'No users found.' },
|
||||
searchPlaceholder: { id: 'admin.user_index.search_input_placeholder', defaultMessage: 'Who are you looking for?' },
|
||||
});
|
||||
|
||||
class UserIndex extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
isLoading: true,
|
||||
filters: ImmutableSet(['local', 'active']),
|
||||
accountIds: ImmutableOrderedSet(),
|
||||
total: Infinity,
|
||||
pageSize: 50,
|
||||
page: 0,
|
||||
query: '',
|
||||
nextLink: undefined,
|
||||
}
|
||||
|
||||
clearState = callback => {
|
||||
this.setState({
|
||||
isLoading: true,
|
||||
accountIds: ImmutableOrderedSet(),
|
||||
page: 0,
|
||||
}, callback);
|
||||
}
|
||||
|
||||
fetchNextPage = () => {
|
||||
const { filters, page, query, pageSize, nextLink } = this.state;
|
||||
const nextPage = page + 1;
|
||||
|
||||
this.props.dispatch(fetchUsers(filters, nextPage, query, pageSize, nextLink))
|
||||
.then(({ users, count, next }) => {
|
||||
const newIds = users.map(user => user.id);
|
||||
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
accountIds: this.state.accountIds.union(newIds),
|
||||
total: count,
|
||||
page: nextPage,
|
||||
nextLink: next,
|
||||
});
|
||||
})
|
||||
.catch(() => { });
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchNextPage();
|
||||
}
|
||||
|
||||
refresh = () => {
|
||||
this.clearState(() => {
|
||||
this.fetchNextPage();
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const { filters, query } = this.state;
|
||||
const filtersChanged = !is(filters, prevState.filters);
|
||||
const queryChanged = query !== prevState.query;
|
||||
|
||||
if (filtersChanged || queryChanged) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = debounce(() => {
|
||||
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 && this.state.nextLink !== false;
|
||||
|
||||
const showLoading = isLoading && accountIds.isEmpty();
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.heading)}>
|
||||
<SimpleForm style={{ paddingBottom: 0 }}>
|
||||
<TextInput
|
||||
onChange={this.handleQueryChange}
|
||||
placeholder={intl.formatMessage(messages.searchPlaceholder)}
|
||||
/>
|
||||
</SimpleForm>
|
||||
<ScrollableList
|
||||
scrollKey='user-index'
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
showLoading={showLoading}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={intl.formatMessage(messages.empty)}
|
||||
className='mt-4'
|
||||
itemClassName='pb-4'
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} withDate />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(connect()(UserIndex));
|
@ -0,0 +1,71 @@
|
||||
import debounce from 'lodash/debounce';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { expandUserIndex, fetchUserIndex, setUserIndexQuery } from 'soapbox/actions/admin';
|
||||
import ScrollableList from 'soapbox/components/scrollable-list';
|
||||
import { Column } from 'soapbox/components/ui';
|
||||
import AccountContainer from 'soapbox/containers/account-container';
|
||||
import { SimpleForm, TextInput } from 'soapbox/features/forms';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.admin.users', defaultMessage: 'Users' },
|
||||
empty: { id: 'admin.user_index.empty', defaultMessage: 'No users found.' },
|
||||
searchPlaceholder: { id: 'admin.user_index.search_input_placeholder', defaultMessage: 'Who are you looking for?' },
|
||||
});
|
||||
|
||||
const UserIndex: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const { isLoading, items, total, query, next } = useAppSelector((state) => state.admin_user_index);
|
||||
|
||||
const handleLoadMore = () => {
|
||||
dispatch(expandUserIndex());
|
||||
};
|
||||
|
||||
const updateQuery = useCallback(debounce(() => {
|
||||
dispatch(fetchUserIndex());
|
||||
}, 900, { leading: true }), []);
|
||||
|
||||
const handleQueryChange: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
dispatch(setUserIndexQuery(e.target.value));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
updateQuery();
|
||||
}, [query]);
|
||||
|
||||
const hasMore = items.count() < total && next !== null;
|
||||
|
||||
const showLoading = isLoading && items.isEmpty();
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.heading)}>
|
||||
<SimpleForm style={{ paddingBottom: 0 }}>
|
||||
<TextInput
|
||||
value={query}
|
||||
onChange={handleQueryChange}
|
||||
placeholder={intl.formatMessage(messages.searchPlaceholder)}
|
||||
/>
|
||||
</SimpleForm>
|
||||
<ScrollableList
|
||||
scrollKey='user-index'
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
showLoading={showLoading}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={intl.formatMessage(messages.empty)}
|
||||
className='mt-4'
|
||||
itemClassName='pb-4'
|
||||
>
|
||||
{items.map(id =>
|
||||
<AccountContainer key={id} id={id} withDate />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserIndex;
|
@ -0,0 +1,68 @@
|
||||
import { Set as ImmutableSet, OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord } from 'immutable';
|
||||
|
||||
import {
|
||||
ADMIN_USER_INDEX_EXPAND_FAIL,
|
||||
ADMIN_USER_INDEX_EXPAND_REQUEST,
|
||||
ADMIN_USER_INDEX_EXPAND_SUCCESS,
|
||||
ADMIN_USER_INDEX_FETCH_FAIL,
|
||||
ADMIN_USER_INDEX_FETCH_REQUEST,
|
||||
ADMIN_USER_INDEX_FETCH_SUCCESS,
|
||||
ADMIN_USER_INDEX_QUERY_SET,
|
||||
} from 'soapbox/actions/admin';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
import type { APIEntity } from 'soapbox/types/entities';
|
||||
|
||||
const ReducerRecord = ImmutableRecord({
|
||||
isLoading: false,
|
||||
loaded: false,
|
||||
items: ImmutableOrderedSet<string>(),
|
||||
filters: ImmutableSet(['local', 'active']),
|
||||
total: Infinity,
|
||||
pageSize: 50,
|
||||
page: -1,
|
||||
query: '',
|
||||
next: null as string | null,
|
||||
});
|
||||
|
||||
type State = ReturnType<typeof ReducerRecord>;
|
||||
|
||||
export default function admin_user_index(state: State = ReducerRecord(), action: AnyAction): State {
|
||||
switch (action.type) {
|
||||
case ADMIN_USER_INDEX_QUERY_SET:
|
||||
return state.set('query', action.query);
|
||||
case ADMIN_USER_INDEX_FETCH_REQUEST:
|
||||
return state
|
||||
.set('isLoading', true)
|
||||
.set('loaded', true)
|
||||
.set('items', ImmutableOrderedSet())
|
||||
.set('total', action.count)
|
||||
.set('page', 0)
|
||||
.set('next', null);
|
||||
case ADMIN_USER_INDEX_FETCH_SUCCESS:
|
||||
return state
|
||||
.set('isLoading', false)
|
||||
.set('loaded', true)
|
||||
.set('items', ImmutableOrderedSet(action.users.map((user: APIEntity) => user.id)))
|
||||
.set('total', action.count)
|
||||
.set('page', 1)
|
||||
.set('next', action.next);
|
||||
case ADMIN_USER_INDEX_FETCH_FAIL:
|
||||
case ADMIN_USER_INDEX_EXPAND_FAIL:
|
||||
return state
|
||||
.set('isLoading', false);
|
||||
case ADMIN_USER_INDEX_EXPAND_REQUEST:
|
||||
return state
|
||||
.set('isLoading', true);
|
||||
case ADMIN_USER_INDEX_EXPAND_SUCCESS:
|
||||
return state
|
||||
.set('isLoading', false)
|
||||
.set('loaded', true)
|
||||
.set('items', state.items.union(action.users.map((user: APIEntity) => user.id)))
|
||||
.set('total', action.count)
|
||||
.set('page', 1)
|
||||
.set('next', action.next);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
Loading…
Reference in new issue