Merge branch 'admin-list-users' into 'develop'
Admin: add UserIndex to view a list of registered users See merge request soapbox-pub/soapbox-fe!613
This commit is contained in:
commit
c883613c67
|
@ -130,7 +130,9 @@ export function fetchAccount(id) {
|
|||
return (dispatch, getState) => {
|
||||
dispatch(fetchRelationships([id]));
|
||||
|
||||
if (getState().getIn(['accounts', id], null) !== null) {
|
||||
const account = getState().getIn(['accounts', id]);
|
||||
|
||||
if (account && !account.get('should_refetch')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -156,7 +158,15 @@ export function fetchAccount(id) {
|
|||
|
||||
export function fetchAccountByUsername(username) {
|
||||
return (dispatch, getState) => {
|
||||
const account = getState().get('accounts').find(account => account.get('acct') === username);
|
||||
|
||||
if (account) {
|
||||
dispatch(fetchAccount(account.get('id')));
|
||||
return;
|
||||
}
|
||||
|
||||
api(getState).get(`/api/v1/accounts/${username}`).then(response => {
|
||||
dispatch(fetchRelationships([response.data.id]));
|
||||
dispatch(importFetchedAccount(response.data));
|
||||
}).then(() => {
|
||||
dispatch(fetchAccountSuccess());
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import api from '../api';
|
||||
import { importFetchedAccount, importFetchedStatuses } from 'soapbox/actions/importer';
|
||||
import { fetchRelationships } from 'soapbox/actions/accounts';
|
||||
|
||||
export const ADMIN_CONFIG_FETCH_REQUEST = 'ADMIN_CONFIG_FETCH_REQUEST';
|
||||
export const ADMIN_CONFIG_FETCH_SUCCESS = 'ADMIN_CONFIG_FETCH_SUCCESS';
|
||||
|
@ -124,54 +125,61 @@ export function closeReports(ids) {
|
|||
return patchReports(ids, 'closed');
|
||||
}
|
||||
|
||||
export function fetchUsers(params) {
|
||||
export function fetchUsers(filters = [], page = 1, pageSize = 50) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: ADMIN_USERS_FETCH_REQUEST, params });
|
||||
const params = { filters: filters.join(), page, page_size: pageSize };
|
||||
|
||||
dispatch({ type: ADMIN_USERS_FETCH_REQUEST, filters, page, pageSize });
|
||||
return api(getState)
|
||||
.get('/api/pleroma/admin/users', { params })
|
||||
.then(({ data }) => {
|
||||
dispatch({ type: ADMIN_USERS_FETCH_SUCCESS, data, params });
|
||||
.then(({ data: { users, count, page_size: pageSize } }) => {
|
||||
dispatch(fetchRelationships(users.map(user => user.id)));
|
||||
dispatch({ type: ADMIN_USERS_FETCH_SUCCESS, users, count, pageSize, filters, page });
|
||||
return { users, count, pageSize };
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USERS_FETCH_FAIL, error, params });
|
||||
dispatch({ type: ADMIN_USERS_FETCH_FAIL, error, filters, page, pageSize });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function deactivateUsers(nicknames) {
|
||||
export function deactivateUsers(accountIds) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_REQUEST, nicknames });
|
||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_REQUEST, accountIds });
|
||||
return api(getState)
|
||||
.patch('/api/pleroma/admin/users/deactivate', { nicknames })
|
||||
.then(({ data: { users } }) => {
|
||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_SUCCESS, users, nicknames });
|
||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_SUCCESS, users, accountIds });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_FAIL, error, nicknames });
|
||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_FAIL, error, accountIds });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteUsers(nicknames) {
|
||||
export function deleteUsers(accountIds) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: ADMIN_USERS_DELETE_REQUEST, nicknames });
|
||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||
dispatch({ type: ADMIN_USERS_DELETE_REQUEST, accountIds });
|
||||
return api(getState)
|
||||
.delete('/api/pleroma/admin/users', { data: { nicknames } })
|
||||
.then(({ data: nicknames }) => {
|
||||
dispatch({ type: ADMIN_USERS_DELETE_SUCCESS, nicknames });
|
||||
dispatch({ type: ADMIN_USERS_DELETE_SUCCESS, nicknames, accountIds });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USERS_DELETE_FAIL, error, nicknames });
|
||||
dispatch({ type: ADMIN_USERS_DELETE_FAIL, error, accountIds });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function approveUsers(nicknames) {
|
||||
export function approveUsers(accountIds) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: ADMIN_USERS_APPROVE_REQUEST, nicknames });
|
||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||
dispatch({ type: ADMIN_USERS_APPROVE_REQUEST, accountIds });
|
||||
return api(getState)
|
||||
.patch('/api/pleroma/admin/users/approve', { nicknames })
|
||||
.then(({ data: { users } }) => {
|
||||
dispatch({ type: ADMIN_USERS_APPROVE_SUCCESS, users, nicknames });
|
||||
dispatch({ type: ADMIN_USERS_APPROVE_SUCCESS, users, accountIds });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USERS_APPROVE_FAIL, error, nicknames });
|
||||
dispatch({ type: ADMIN_USERS_APPROVE_FAIL, error, accountIds });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ export function deactivateUserModal(intl, accountId, afterConfirm = () => {}) {
|
|||
message: intl.formatMessage(messages.deactivateUserPrompt, { acct }),
|
||||
confirm: intl.formatMessage(messages.deactivateUserConfirm, { name }),
|
||||
onConfirm: () => {
|
||||
dispatch(deactivateUsers([acct])).then(() => {
|
||||
dispatch(deactivateUsers([accountId])).then(() => {
|
||||
const message = intl.formatMessage(messages.userDeactivated, { acct });
|
||||
dispatch(snackbar.success(message));
|
||||
afterConfirm();
|
||||
|
@ -74,7 +74,7 @@ export function deleteUserModal(intl, accountId, afterConfirm = () => {}) {
|
|||
confirm,
|
||||
checkbox,
|
||||
onConfirm: () => {
|
||||
dispatch(deleteUsers([acct])).then(() => {
|
||||
dispatch(deleteUsers([accountId])).then(() => {
|
||||
const message = intl.formatMessage(messages.userDeleted, { acct });
|
||||
dispatch(fetchAccountByUsername(acct));
|
||||
dispatch(snackbar.success(message));
|
||||
|
|
|
@ -5,24 +5,18 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import Column from '../ui/components/column';
|
||||
import IconButton from 'soapbox/components/icon_button';
|
||||
import ScrollableList from 'soapbox/components/scrollable_list';
|
||||
import { fetchUsers, deleteUsers, approveUsers } from 'soapbox/actions/admin';
|
||||
import snackbar from 'soapbox/actions/snackbar';
|
||||
import UnapprovedAccount from './components/unapproved_account';
|
||||
import { fetchUsers } from 'soapbox/actions/admin';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.admin.awaiting_approval', defaultMessage: 'Awaiting Approval' },
|
||||
emptyMessage: { id: 'admin.awaiting_approval.empty_message', defaultMessage: 'There is nobody waiting for approval. When a new user signs up, you can review them here.' },
|
||||
approved: { id: 'admin.awaiting_approval.approved_message', defaultMessage: '{acct} was approved!' },
|
||||
rejected: { id: 'admin.awaiting_approval.rejected_message', defaultMessage: '{acct} was rejected.' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const nicknames = state.getIn(['admin', 'awaitingApproval']);
|
||||
return {
|
||||
users: nicknames.toList().map(nickname => state.getIn(['admin', 'users', nickname])),
|
||||
};
|
||||
};
|
||||
const mapStateToProps = state => ({
|
||||
accountIds: state.getIn(['admin', 'awaitingApproval']),
|
||||
});
|
||||
|
||||
export default @connect(mapStateToProps)
|
||||
@injectIntl
|
||||
|
@ -30,7 +24,7 @@ class AwaitingApproval extends ImmutablePureComponent {
|
|||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
users: ImmutablePropTypes.list.isRequired,
|
||||
accountIds: ImmutablePropTypes.orderedSet.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -39,51 +33,26 @@ class AwaitingApproval extends ImmutablePureComponent {
|
|||
|
||||
componentDidMount() {
|
||||
const { dispatch } = this.props;
|
||||
const params = { page: 1, filters: 'local,need_approval' };
|
||||
dispatch(fetchUsers(params))
|
||||
dispatch(fetchUsers(['local', 'need_approval']))
|
||||
.then(() => this.setState({ isLoading: false }))
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
handleApprove = nickname => {
|
||||
const { dispatch, intl } = this.props;
|
||||
return e => {
|
||||
dispatch(approveUsers([nickname])).then(() => {
|
||||
const message = intl.formatMessage(messages.approved, { acct: `@${nickname}` });
|
||||
dispatch(snackbar.success(message));
|
||||
}).catch(() => {});
|
||||
};
|
||||
}
|
||||
|
||||
handleReject = nickname => {
|
||||
const { dispatch, intl } = this.props;
|
||||
return e => {
|
||||
dispatch(deleteUsers([nickname])).then(() => {
|
||||
const message = intl.formatMessage(messages.rejected, { acct: `@${nickname}` });
|
||||
dispatch(snackbar.info(message));
|
||||
}).catch(() => {});
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl, users } = this.props;
|
||||
const { intl, accountIds } = this.props;
|
||||
const { isLoading } = this.state;
|
||||
const showLoading = isLoading && users.count() === 0;
|
||||
const showLoading = isLoading && accountIds.count() === 0;
|
||||
|
||||
return (
|
||||
<Column icon='user' heading={intl.formatMessage(messages.heading)} backBtnSlim>
|
||||
<ScrollableList isLoading={isLoading} showLoading={showLoading} scrollKey='awaiting-approval' emptyMessage={intl.formatMessage(messages.emptyMessage)}>
|
||||
{users.map((user, i) => (
|
||||
<div className='unapproved-account' key={user.get('id')}>
|
||||
<div className='unapproved-account__bio'>
|
||||
<div className='unapproved-account__nickname'>@{user.get('nickname')}</div>
|
||||
<blockquote className='md'>{user.get('registration_reason')}</blockquote>
|
||||
</div>
|
||||
<div className='unapproved-account__actions'>
|
||||
<IconButton icon='check' size={22} onClick={this.handleApprove(user.get('nickname'))} />
|
||||
<IconButton icon='close' size={22} onClick={this.handleReject(user.get('nickname'))} />
|
||||
</div>
|
||||
</div>
|
||||
<ScrollableList
|
||||
isLoading={isLoading}
|
||||
showLoading={showLoading}
|
||||
scrollKey='awaiting-approval'
|
||||
emptyMessage={intl.formatMessage(messages.emptyMessage)}
|
||||
>
|
||||
{accountIds.map(id => (
|
||||
<UnapprovedAccount accountId={id} key={id} />
|
||||
))}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import React from 'react';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import IconButton from 'soapbox/components/icon_button';
|
||||
import { deleteUsers, approveUsers } from 'soapbox/actions/admin';
|
||||
import { makeGetAccount } from 'soapbox/selectors';
|
||||
import snackbar from 'soapbox/actions/snackbar';
|
||||
|
||||
const messages = defineMessages({
|
||||
approved: { id: 'admin.awaiting_approval.approved_message', defaultMessage: '{acct} was approved!' },
|
||||
rejected: { id: 'admin.awaiting_approval.rejected_message', defaultMessage: '{acct} was rejected.' },
|
||||
});
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
const getAccount = makeGetAccount();
|
||||
|
||||
const mapStateToProps = (state, { accountId }) => {
|
||||
return {
|
||||
account: getAccount(state, accountId),
|
||||
};
|
||||
};
|
||||
|
||||
return mapStateToProps;
|
||||
};
|
||||
|
||||
export default @connect(makeMapStateToProps)
|
||||
@injectIntl
|
||||
class UnapprovedAccount extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
};
|
||||
|
||||
handleApprove = () => {
|
||||
const { dispatch, intl, account } = this.props;
|
||||
|
||||
dispatch(approveUsers([account.get('id')]))
|
||||
.then(() => {
|
||||
const message = intl.formatMessage(messages.approved, { acct: `@${account.get('acct')}` });
|
||||
dispatch(snackbar.success(message));
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
handleReject = () => {
|
||||
const { dispatch, intl, account } = this.props;
|
||||
|
||||
dispatch(deleteUsers([account.get('id')]))
|
||||
.then(() => {
|
||||
const message = intl.formatMessage(messages.rejected, { acct: `@${account.get('acct')}` });
|
||||
dispatch(snackbar.info(message));
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { account } = this.props;
|
||||
|
||||
return (
|
||||
<div className='unapproved-account'>
|
||||
<div className='unapproved-account__bio'>
|
||||
<div className='unapproved-account__nickname'>@{account.get('acct')}</div>
|
||||
<blockquote className='md'>{account.getIn(['pleroma', 'admin', 'registration_reason'])}</blockquote>
|
||||
</div>
|
||||
<div className='unapproved-account__actions'>
|
||||
<IconButton icon='check' size={22} onClick={this.handleApprove} />
|
||||
<IconButton icon='close' size={22} onClick={this.handleReject} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import { defineMessages, injectIntl, FormattedMessage, FormattedNumber } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import PropTypes from 'prop-types';
|
||||
|
@ -92,14 +93,14 @@ class Dashboard extends ImmutablePureComponent {
|
|||
</div>
|
||||
</div>}
|
||||
<div className='dashcounter'>
|
||||
<a href='/pleroma/admin/#/users/index' target='_blank'>
|
||||
<Link to='/admin/users'>
|
||||
<div className='dashcounter__num'>
|
||||
<FormattedNumber value={userCount} />
|
||||
</div>
|
||||
<div className='dashcounter__label'>
|
||||
<FormattedMessage id='admin.dashcounters.user_count_label' defaultMessage='total users' />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
{retention && <div className='dashcounter'>
|
||||
<div>
|
||||
|
@ -112,14 +113,14 @@ class Dashboard extends ImmutablePureComponent {
|
|||
</div>
|
||||
</div>}
|
||||
<div className='dashcounter'>
|
||||
<a href='/pleroma/admin/#/statuses/index' target='_blank'>
|
||||
<Link to='/timeline/local'>
|
||||
<div className='dashcounter__num'>
|
||||
<FormattedNumber value={instance.getIn(['stats', 'status_count'])} />
|
||||
</div>
|
||||
<div className='dashcounter__label'>
|
||||
<FormattedMessage id='admin.dashcounters.status_count_label' defaultMessage='posts' />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
<div className='dashcounter'>
|
||||
<div>
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
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 AccountContainer from 'soapbox/containers/account_container';
|
||||
import Column from 'soapbox/features/ui/components/column';
|
||||
import ScrollableList from 'soapbox/components/scrollable_list';
|
||||
import { Set as ImmutableSet, OrderedSet as ImmutableOrderedSet, is } from 'immutable';
|
||||
|
||||
export default @connect()
|
||||
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,
|
||||
}
|
||||
|
||||
clearState = () => {
|
||||
this.setState({
|
||||
isLoading: true,
|
||||
page: 0,
|
||||
});
|
||||
}
|
||||
|
||||
fetchNextPage = () => {
|
||||
const { filters, page, pageSize } = this.state;
|
||||
const nextPage = page + 1;
|
||||
|
||||
this.props.dispatch(fetchUsers(filters, nextPage, pageSize))
|
||||
.then(({ users, count }) => {
|
||||
const newIds = users.map(user => user.id);
|
||||
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
accountIds: this.state.accountIds.union(newIds),
|
||||
total: count,
|
||||
page: nextPage,
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchNextPage();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const { filters, q } = this.state;
|
||||
|
||||
if (!is(filters, prevState.filters) || !is(q, prevState.q)) {
|
||||
this.clearState();
|
||||
this.fetchNextPage();
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = debounce(() => {
|
||||
this.fetchNextPage();
|
||||
}, 2000, { leading: true });
|
||||
|
||||
render() {
|
||||
const { accountIds, isLoading } = this.state;
|
||||
const hasMore = accountIds.count() < this.state.total;
|
||||
|
||||
const showLoading = isLoading && accountIds.isEmpty();
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<ScrollableList
|
||||
scrollKey='user-index'
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
showLoading={showLoading}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='admin.user_index.empty' defaultMessage='No users found.' />}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} withNote={false} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -95,6 +95,7 @@ import {
|
|||
ModerationLog,
|
||||
CryptoDonate,
|
||||
ScheduledStatuses,
|
||||
UserIndex,
|
||||
} from './util/async-components';
|
||||
|
||||
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
||||
|
@ -265,6 +266,7 @@ class SwitchingColumnsArea extends React.PureComponent {
|
|||
<WrappedRoute path='/admin/approval' page={AdminPage} component={AwaitingApproval} content={children} exact />
|
||||
<WrappedRoute path='/admin/reports' page={AdminPage} component={Reports} content={children} exact />
|
||||
<WrappedRoute path='/admin/log' page={AdminPage} component={ModerationLog} content={children} exact />
|
||||
<WrappedRoute path='/admin/users' page={AdminPage} component={UserIndex} content={children} exact />
|
||||
<WrappedRoute path='/info' page={EmptyPage} component={ServerInfo} content={children} />
|
||||
|
||||
<WrappedRoute path='/donate/crypto' publicRoute page={DefaultPage} component={CryptoDonate} content={children} />
|
||||
|
@ -428,7 +430,7 @@ class UI extends React.PureComponent {
|
|||
|
||||
if (isStaff(account)) {
|
||||
this.props.dispatch(fetchReports({ state: 'open' }));
|
||||
this.props.dispatch(fetchUsers({ page: 1, filters: 'local,need_approval' }));
|
||||
this.props.dispatch(fetchUsers(['local', 'need_approval']));
|
||||
}
|
||||
|
||||
if (isAdmin(account)) {
|
||||
|
|
|
@ -241,3 +241,7 @@ export function CryptoDonate() {
|
|||
export function ScheduledStatuses() {
|
||||
return import(/* webpackChunkName: "features/scheduled_statuses" */'../../scheduled_statuses');
|
||||
}
|
||||
|
||||
export function UserIndex() {
|
||||
return import(/* webpackChunkName: "features/admin/user_index" */'../../admin/user_index');
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
} from 'immutable';
|
||||
import { normalizePleromaUserFields } from 'soapbox/utils/pleroma';
|
||||
import {
|
||||
ADMIN_USERS_FETCH_SUCCESS,
|
||||
ADMIN_USERS_TAG_REQUEST,
|
||||
ADMIN_USERS_TAG_FAIL,
|
||||
ADMIN_USERS_UNTAG_REQUEST,
|
||||
|
@ -22,7 +23,12 @@ import {
|
|||
ADMIN_REMOVE_PERMISSION_GROUP_REQUEST,
|
||||
ADMIN_REMOVE_PERMISSION_GROUP_FAIL,
|
||||
} from 'soapbox/actions/admin';
|
||||
import { ADMIN_USERS_DELETE_REQUEST } from 'soapbox/actions/admin';
|
||||
import {
|
||||
ADMIN_USERS_DELETE_REQUEST,
|
||||
ADMIN_USERS_DELETE_FAIL,
|
||||
ADMIN_USERS_DEACTIVATE_REQUEST,
|
||||
ADMIN_USERS_DEACTIVATE_FAIL,
|
||||
} from 'soapbox/actions/admin';
|
||||
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
|
@ -79,17 +85,10 @@ const removeTags = (state, accountIds, tags) => {
|
|||
});
|
||||
};
|
||||
|
||||
const nicknamesToIds = (state, nicknames) => {
|
||||
return nicknames.map(nickname => {
|
||||
return state.find(account => account.get('acct') === nickname, null, ImmutableMap()).get('id');
|
||||
});
|
||||
};
|
||||
|
||||
const setDeactivated = (state, nicknames) => {
|
||||
const ids = nicknamesToIds(state, nicknames);
|
||||
const setActive = (state, accountIds, active) => {
|
||||
return state.withMutations(state => {
|
||||
ids.forEach(id => {
|
||||
state.setIn([id, 'pleroma', 'is_active'], false);
|
||||
accountIds.forEach(id => {
|
||||
state.setIn([id, 'pleroma', 'is_active'], active);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -121,6 +120,67 @@ const removePermission = (state, accountIds, permissionGroup) => {
|
|||
});
|
||||
};
|
||||
|
||||
const buildAccount = adminUser => fromJS({
|
||||
id: adminUser.get('id'),
|
||||
username: adminUser.get('nickname').split('@')[0],
|
||||
acct: adminUser.get('nickname'),
|
||||
display_name: adminUser.get('display_name'),
|
||||
display_name_html: adminUser.get('display_name'),
|
||||
note: '',
|
||||
url: adminUser.get('url'),
|
||||
avatar: adminUser.get('avatar'),
|
||||
avatar_static: adminUser.get('avatar'),
|
||||
header: '',
|
||||
header_static: '',
|
||||
emojis: [],
|
||||
fields: [],
|
||||
pleroma: {
|
||||
is_active: adminUser.get('is_active'),
|
||||
is_confirmed: adminUser.get('is_confirmed'),
|
||||
is_admin: adminUser.getIn(['roles', 'admin']),
|
||||
is_moderator: adminUser.getIn(['roles', 'moderator']),
|
||||
tags: adminUser.get('tags'),
|
||||
},
|
||||
source: {
|
||||
pleroma: {
|
||||
actor_type: adminUser.get('actor_type'),
|
||||
},
|
||||
},
|
||||
should_refetch: true,
|
||||
});
|
||||
|
||||
const mergeAdminUser = (account, adminUser) => {
|
||||
return account.withMutations(account => {
|
||||
account.set('display_name', adminUser.get('display_name'));
|
||||
account.set('avatar', adminUser.get('avatar'));
|
||||
account.set('avatar_static', adminUser.get('avatar'));
|
||||
account.setIn(['pleroma', 'is_active'], adminUser.get('is_active'));
|
||||
account.setIn(['pleroma', 'is_admin'], adminUser.getIn(['roles', 'admin']));
|
||||
account.setIn(['pleroma', 'is_moderator'], adminUser.getIn(['roles', 'moderator']));
|
||||
account.setIn(['pleroma', 'is_confirmed'], adminUser.get('is_confirmed'));
|
||||
account.setIn(['pleroma', 'tags'], adminUser.get('tags'));
|
||||
});
|
||||
};
|
||||
|
||||
const importAdminUser = (state, adminUser) => {
|
||||
const id = adminUser.get('id');
|
||||
const account = state.get(id);
|
||||
|
||||
if (!account) {
|
||||
return state.set(id, buildAccount(adminUser));
|
||||
} else {
|
||||
return state.set(id, mergeAdminUser(account, adminUser));
|
||||
}
|
||||
};
|
||||
|
||||
const importAdminUsers = (state, adminUsers) => {
|
||||
return state.withMutations(state => {
|
||||
fromJS(adminUsers).forEach(adminUser => {
|
||||
importAdminUser(state, adminUser);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default function accounts(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case ACCOUNT_IMPORT:
|
||||
|
@ -149,7 +209,13 @@ export default function accounts(state = initialState, action) {
|
|||
case ADMIN_ADD_PERMISSION_GROUP_FAIL:
|
||||
return removePermission(state, action.accountIds, action.permissionGroup);
|
||||
case ADMIN_USERS_DELETE_REQUEST:
|
||||
return setDeactivated(state, action.nicknames);
|
||||
case ADMIN_USERS_DEACTIVATE_REQUEST:
|
||||
return setActive(state, action.accountIds, false);
|
||||
case ADMIN_USERS_DELETE_FAIL:
|
||||
case ADMIN_USERS_DEACTIVATE_FAIL:
|
||||
return setActive(state, action.accountIds, true);
|
||||
case ADMIN_USERS_FETCH_SUCCESS:
|
||||
return importAdminUsers(state, action.users);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -31,18 +31,21 @@ function importUsers(state, users) {
|
|||
users.forEach(user => {
|
||||
user = normalizePleromaUserFields(user);
|
||||
if (!user.is_approved) {
|
||||
state.update('awaitingApproval', orderedSet => orderedSet.add(user.nickname));
|
||||
state.update('awaitingApproval', orderedSet => orderedSet.add(user.id));
|
||||
}
|
||||
state.setIn(['users', user.nickname], fromJS(user));
|
||||
state.setIn(['users', user.id], ImmutableMap({
|
||||
email: user.email,
|
||||
registration_reason: user.registration_reason,
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function deleteUsers(state, nicknames) {
|
||||
function deleteUsers(state, accountIds) {
|
||||
return state.withMutations(state => {
|
||||
nicknames.forEach(nickname => {
|
||||
state.update('awaitingApproval', orderedSet => orderedSet.delete(nickname));
|
||||
state.deleteIn(['users', nickname]);
|
||||
accountIds.forEach(id => {
|
||||
state.update('awaitingApproval', orderedSet => orderedSet.delete(id));
|
||||
state.deleteIn(['users', id]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -94,12 +97,12 @@ export default function admin(state = initialState, action) {
|
|||
case ADMIN_REPORTS_PATCH_SUCCESS:
|
||||
return handleReportDiffs(state, action.reports);
|
||||
case ADMIN_USERS_FETCH_SUCCESS:
|
||||
return importUsers(state, action.data.users);
|
||||
return importUsers(state, action.users);
|
||||
case ADMIN_USERS_DELETE_REQUEST:
|
||||
case ADMIN_USERS_DELETE_SUCCESS:
|
||||
return deleteUsers(state, action.nicknames);
|
||||
return deleteUsers(state, action.accountIds);
|
||||
case ADMIN_USERS_APPROVE_REQUEST:
|
||||
return state.update('awaitingApproval', set => set.subtract(action.nicknames));
|
||||
return state.update('awaitingApproval', set => set.subtract(action.accountIds));
|
||||
case ADMIN_USERS_APPROVE_SUCCESS:
|
||||
return approveUsers(state, action.users);
|
||||
default:
|
||||
|
|
|
@ -5,6 +5,7 @@ const getAccountBase = (state, id) => state.getIn(['accounts', id], null
|
|||
const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null);
|
||||
const getAccountRelationship = (state, id) => state.getIn(['relationships', id], null);
|
||||
const getAccountMoved = (state, id) => state.getIn(['accounts', state.getIn(['accounts', id, 'moved'])]);
|
||||
const getAccountAdminData = (state, id) => state.getIn(['admin', 'users', id]);
|
||||
const getAccountPatron = (state, id) => {
|
||||
const url = state.getIn(['accounts', id, 'url']);
|
||||
return state.getIn(['patron', 'accounts', url]);
|
||||
|
@ -16,8 +17,9 @@ export const makeGetAccount = () => {
|
|||
getAccountCounters,
|
||||
getAccountRelationship,
|
||||
getAccountMoved,
|
||||
getAccountAdminData,
|
||||
getAccountPatron,
|
||||
], (base, counters, relationship, moved, patron) => {
|
||||
], (base, counters, relationship, moved, admin, patron) => {
|
||||
if (base === null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -26,6 +28,7 @@ export const makeGetAccount = () => {
|
|||
map.set('relationship', relationship);
|
||||
map.set('moved', moved);
|
||||
map.set('patron', patron);
|
||||
map.setIn(['pleroma', 'admin'], admin);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue