Admin: display latest accounts in dashboard

remove-account-counters-reducer
Alex Gleason 2021-07-14 12:27:51 -05:00
rodzic 778c90277b
commit b2ebdbcf80
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 7211D1F99744FBB7
6 zmienionych plików z 153 dodań i 11 usunięć

Wyświetl plik

@ -1,6 +1,7 @@
import api from '../api'; import api from '../api';
import { importFetchedAccount, importFetchedStatuses } from 'soapbox/actions/importer'; import { importFetchedAccount, importFetchedStatuses } from 'soapbox/actions/importer';
import { fetchRelationships } from 'soapbox/actions/accounts'; import { fetchRelationships } from 'soapbox/actions/accounts';
import { Set as ImmutableSet } from 'immutable';
export const ADMIN_CONFIG_FETCH_REQUEST = 'ADMIN_CONFIG_FETCH_REQUEST'; export const ADMIN_CONFIG_FETCH_REQUEST = 'ADMIN_CONFIG_FETCH_REQUEST';
export const ADMIN_CONFIG_FETCH_SUCCESS = 'ADMIN_CONFIG_FETCH_SUCCESS'; export const ADMIN_CONFIG_FETCH_SUCCESS = 'ADMIN_CONFIG_FETCH_SUCCESS';

Wyświetl plik

@ -0,0 +1,54 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, defineMessages } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import AccountListPanel from 'soapbox/features/ui/components/account_list_panel';
import { fetchUsers } from 'soapbox/actions/admin';
const messages = defineMessages({
title: { id: 'admin.latest_accounts_panel.title', defaultMessage: 'Latest Accounts' },
});
const mapStateToProps = state => ({
accountIds: state.getIn(['admin', 'latestUsers']),
});
export default @connect(mapStateToProps)
@injectIntl
class LatestAccountsPanel extends ImmutablePureComponent {
static propTypes = {
accountIds: ImmutablePropTypes.orderedSet.isRequired,
limit: PropTypes.number,
};
static defaultProps = {
limit: 5,
}
componentDidMount() {
const { dispatch, limit } = this.props;
dispatch(fetchUsers(['local', 'active'], 1, null, limit));
}
render() {
const { intl, accountIds, limit, ...props } = this.props;
if (!accountIds || accountIds.isEmpty()) {
return null;
}
return (
<AccountListPanel
icon='users'
title={intl.formatMessage(messages.title)}
accountIds={accountIds}
limit={limit}
{...props}
/>
);
};
};

Wyświetl plik

@ -0,0 +1,47 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Icon from 'soapbox/components/icon';
import AccountContainer from '../../../containers/account_container';
export default class AccountListPanel extends ImmutablePureComponent {
static propTypes = {
title: PropTypes.node.isRequired,
accountIds: ImmutablePropTypes.orderedSet.isRequired,
icon: PropTypes.string.isRequired,
limit: PropTypes.number,
};
static defaultProps = {
limit: Infinity,
}
render() {
const { title, icon, accountIds, limit, ...props } = this.props;
if (!accountIds || accountIds.isEmpty()) {
return null;
}
return (
<div className='wtf-panel'>
<div className='wtf-panel-header'>
<Icon id={icon} className='wtf-panel-header__icon' />
<span className='wtf-panel-header__label'>
{title}
</span>
</div>
<div className='wtf-panel__content'>
<div className='wtf-panel__list'>
{accountIds.take(limit).map(accountId => (
<AccountContainer key={accountId} id={accountId} {...props} />
))}
</div>
</div>
</div>
);
};
};

Wyświetl plik

@ -2,6 +2,7 @@ import React from 'react';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import LinkFooter from '../features/ui/components/link_footer'; import LinkFooter from '../features/ui/components/link_footer';
import AdminNav from 'soapbox/features/admin/components/admin_nav'; import AdminNav from 'soapbox/features/admin/components/admin_nav';
import LatestAccountsPanel from 'soapbox/features/admin/components/latest_accounts_panel';
export default export default
class AdminPage extends ImmutablePureComponent { class AdminPage extends ImmutablePureComponent {
@ -28,6 +29,7 @@ class AdminPage extends ImmutablePureComponent {
<div className='columns-area__panels__pane columns-area__panels__pane--right'> <div className='columns-area__panels__pane columns-area__panels__pane--right'>
<div className='columns-area__panels__pane__inner'> <div className='columns-area__panels__pane__inner'>
<LatestAccountsPanel limit={5} withDate />
<LinkFooter /> <LinkFooter />
</div> </div>
</div> </div>

Wyświetl plik

@ -11,6 +11,7 @@ describe('admin reducer', () => {
reports: ImmutableMap(), reports: ImmutableMap(),
openReports: ImmutableOrderedSet(), openReports: ImmutableOrderedSet(),
users: ImmutableMap(), users: ImmutableMap(),
latestUsers: ImmutableOrderedSet(),
awaitingApproval: ImmutableOrderedSet(), awaitingApproval: ImmutableOrderedSet(),
configs: ImmutableList(), configs: ImmutableList(),
needsReboot: false, needsReboot: false,

Wyświetl plik

@ -12,31 +12,68 @@ import {
import { import {
Map as ImmutableMap, Map as ImmutableMap,
List as ImmutableList, List as ImmutableList,
Set as ImmutableSet,
OrderedSet as ImmutableOrderedSet, OrderedSet as ImmutableOrderedSet,
fromJS, fromJS,
is,
} from 'immutable'; } from 'immutable';
import { normalizePleromaUserFields } from 'soapbox/utils/pleroma';
const initialState = ImmutableMap({ const initialState = ImmutableMap({
reports: ImmutableMap(), reports: ImmutableMap(),
openReports: ImmutableOrderedSet(), openReports: ImmutableOrderedSet(),
users: ImmutableMap(), users: ImmutableMap(),
latestUsers: ImmutableOrderedSet(),
awaitingApproval: ImmutableOrderedSet(), awaitingApproval: ImmutableOrderedSet(),
configs: ImmutableList(), configs: ImmutableList(),
needsReboot: false, needsReboot: false,
}); });
function importUsers(state, users) { const FILTER_UNAPPROVED = ['local', 'need_approval'];
const FILTER_LATEST = ['local', 'active'];
const filtersMatch = (f1, f2) => is(ImmutableSet(f1), ImmutableSet(f2));
const toIds = items => items.map(item => item.id);
const mergeSet = (state, key, users) => {
const newIds = toIds(users);
return state.update(key, ImmutableOrderedSet(), ids => ids.union(newIds));
};
const replaceSet = (state, key, users) => {
const newIds = toIds(users);
return state.set(key, ImmutableOrderedSet(newIds));
};
const maybeImportUnapproved = (state, users, filters) => {
if (filtersMatch(FILTER_UNAPPROVED, filters)) {
return mergeSet(state, 'awaitingApproval', users);
} else {
return state;
}
};
const maybeImportLatest = (state, users, filters, page) => {
if (page === 1 && filtersMatch(FILTER_LATEST, filters)) {
return replaceSet(state, 'latestUsers', users);
} else {
return state;
}
};
const importUser = (state, user) => (
state.setIn(['users', user.id], ImmutableMap({
email: user.email,
registration_reason: user.registration_reason,
}))
);
function importUsers(state, users, filters, page) {
return state.withMutations(state => { return state.withMutations(state => {
maybeImportUnapproved(state, users, filters);
maybeImportLatest(state, users, filters, page);
users.forEach(user => { users.forEach(user => {
user = normalizePleromaUserFields(user); importUser(state, user);
if (!user.is_approved) {
state.update('awaitingApproval', orderedSet => orderedSet.add(user.id));
}
state.setIn(['users', user.id], ImmutableMap({
email: user.email,
registration_reason: user.registration_reason,
}));
}); });
}); });
} }
@ -97,7 +134,7 @@ export default function admin(state = initialState, action) {
case ADMIN_REPORTS_PATCH_SUCCESS: case ADMIN_REPORTS_PATCH_SUCCESS:
return handleReportDiffs(state, action.reports); return handleReportDiffs(state, action.reports);
case ADMIN_USERS_FETCH_SUCCESS: case ADMIN_USERS_FETCH_SUCCESS:
return importUsers(state, action.users); return importUsers(state, action.users, action.filters, action.page);
case ADMIN_USERS_DELETE_REQUEST: case ADMIN_USERS_DELETE_REQUEST:
case ADMIN_USERS_DELETE_SUCCESS: case ADMIN_USERS_DELETE_SUCCESS:
return deleteUsers(state, action.accountIds); return deleteUsers(state, action.accountIds);