Rework admin approve/reject actions

environments/review-main-yi2y9f/deployments/4692^2
Alex Gleason 3 months ago
parent cdc8c70078
commit 0e846784df
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7

@ -39,6 +39,10 @@ const ADMIN_USERS_APPROVE_REQUEST = 'ADMIN_USERS_APPROVE_REQUEST';
const ADMIN_USERS_APPROVE_SUCCESS = 'ADMIN_USERS_APPROVE_SUCCESS';
const ADMIN_USERS_APPROVE_FAIL = 'ADMIN_USERS_APPROVE_FAIL';
const ADMIN_USERS_REJECT_REQUEST = 'ADMIN_USERS_REJECT_REQUEST';
const ADMIN_USERS_REJECT_SUCCESS = 'ADMIN_USERS_REJECT_SUCCESS';
const ADMIN_USERS_REJECT_FAIL = 'ADMIN_USERS_REJECT_FAIL';
const ADMIN_USERS_DEACTIVATE_REQUEST = 'ADMIN_USERS_DEACTIVATE_REQUEST';
const ADMIN_USERS_DEACTIVATE_SUCCESS = 'ADMIN_USERS_DEACTIVATE_SUCCESS';
const ADMIN_USERS_DEACTIVATE_FAIL = 'ADMIN_USERS_DEACTIVATE_FAIL';
@ -309,56 +313,80 @@ const deactivateUsers = (accountIds: string[], reportId?: string) =>
}
};
const deleteUsers = (accountIds: string[]) =>
const deleteUser = (accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const nicknames = accountIdsToAccts(getState(), accountIds);
dispatch({ type: ADMIN_USERS_DELETE_REQUEST, accountIds });
const nicknames = accountIdsToAccts(getState(), [accountId]);
dispatch({ type: ADMIN_USERS_DELETE_REQUEST, accountId });
return api(getState)
.delete('/api/v1/pleroma/admin/users', { data: { nicknames } })
.then(({ data: nicknames }) => {
dispatch({ type: ADMIN_USERS_DELETE_SUCCESS, nicknames, accountIds });
dispatch({ type: ADMIN_USERS_DELETE_SUCCESS, nicknames, accountId });
}).catch(error => {
dispatch({ type: ADMIN_USERS_DELETE_FAIL, error, accountIds });
dispatch({ type: ADMIN_USERS_DELETE_FAIL, error, accountId });
});
};
const approveMastodonUsers = (accountIds: string[]) =>
const approveMastodonUser = (accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) =>
Promise.all(accountIds.map(accountId => {
api(getState)
.post(`/api/v1/admin/accounts/${accountId}/approve`)
.then(({ data: user }) => {
dispatch({ type: ADMIN_USERS_APPROVE_SUCCESS, users: [user], accountIds: [accountId] });
}).catch(error => {
dispatch({ type: ADMIN_USERS_APPROVE_FAIL, error, accountIds: [accountId] });
});
}));
api(getState)
.post(`/api/v1/admin/accounts/${accountId}/approve`)
.then(({ data: user }) => {
dispatch({ type: ADMIN_USERS_APPROVE_SUCCESS, user, accountId });
}).catch(error => {
dispatch({ type: ADMIN_USERS_APPROVE_FAIL, error, accountId });
});
const approvePleromaUsers = (accountIds: string[]) =>
const approvePleromaUser = (accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const nicknames = accountIdsToAccts(getState(), accountIds);
const nicknames = accountIdsToAccts(getState(), [accountId]);
return api(getState)
.patch('/api/v1/pleroma/admin/users/approve', { nicknames })
.then(({ data: { users } }) => {
dispatch({ type: ADMIN_USERS_APPROVE_SUCCESS, users, accountIds });
dispatch({ type: ADMIN_USERS_APPROVE_SUCCESS, user: users[0], accountId });
}).catch(error => {
dispatch({ type: ADMIN_USERS_APPROVE_FAIL, error, accountId });
});
};
const rejectMastodonUser = (accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) =>
api(getState)
.post(`/api/v1/admin/accounts/${accountId}/reject`)
.then(({ data: user }) => {
dispatch({ type: ADMIN_USERS_REJECT_SUCCESS, user, accountId });
}).catch(error => {
dispatch({ type: ADMIN_USERS_APPROVE_FAIL, error, accountIds });
dispatch({ type: ADMIN_USERS_REJECT_FAIL, error, accountId });
});
const approveUser = (accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const instance = state.instance;
const features = getFeatures(instance);
dispatch({ type: ADMIN_USERS_APPROVE_REQUEST, accountId });
if (features.mastodonAdmin) {
return dispatch(approveMastodonUser(accountId));
} else {
return dispatch(approvePleromaUser(accountId));
}
};
const approveUsers = (accountIds: string[]) =>
const rejectUser = (accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const instance = state.instance;
const features = getFeatures(instance);
dispatch({ type: ADMIN_USERS_APPROVE_REQUEST, accountIds });
dispatch({ type: ADMIN_USERS_REJECT_REQUEST, accountId });
if (features.mastodonAdmin) {
return dispatch(approveMastodonUsers(accountIds));
return dispatch(rejectMastodonUser(accountId));
} else {
return dispatch(approvePleromaUsers(accountIds));
return dispatch(deleteUser(accountId));
}
};
@ -562,6 +590,9 @@ export {
ADMIN_USERS_APPROVE_REQUEST,
ADMIN_USERS_APPROVE_SUCCESS,
ADMIN_USERS_APPROVE_FAIL,
ADMIN_USERS_REJECT_REQUEST,
ADMIN_USERS_REJECT_SUCCESS,
ADMIN_USERS_REJECT_FAIL,
ADMIN_USERS_DEACTIVATE_REQUEST,
ADMIN_USERS_DEACTIVATE_SUCCESS,
ADMIN_USERS_DEACTIVATE_FAIL,
@ -597,8 +628,9 @@ export {
closeReports,
fetchUsers,
deactivateUsers,
deleteUsers,
approveUsers,
deleteUser,
approveUser,
rejectUser,
deleteStatus,
toggleStatusSensitivity,
tagUsers,

@ -2,7 +2,7 @@ import React from 'react';
import { defineMessages, IntlShape } from 'react-intl';
import { fetchAccountByUsername } from 'soapbox/actions/accounts';
import { deactivateUsers, deleteUsers, deleteStatus, toggleStatusSensitivity } from 'soapbox/actions/admin';
import { deactivateUsers, deleteUser, deleteStatus, toggleStatusSensitivity } from 'soapbox/actions/admin';
import { openModal } from 'soapbox/actions/modals';
import OutlineBox from 'soapbox/components/outline-box';
import { Stack, Text } from 'soapbox/components/ui';
@ -102,7 +102,7 @@ const deleteUserModal = (intl: IntlShape, accountId: string, afterConfirm = () =
confirm,
checkbox,
onConfirm: () => {
dispatch(deleteUsers([accountId])).then(() => {
dispatch(deleteUser(accountId)).then(() => {
const message = intl.formatMessage(messages.userDeleted, { acct });
dispatch(fetchAccountByUsername(acct));
toast.success(message);

@ -71,6 +71,7 @@ const ProfilePopper: React.FC<IProfilePopper> = ({ condition, wrapper, children
};
export interface IAccount {
acct?: string;
account: AccountSchema;
action?: React.ReactElement;
actionAlignment?: 'center' | 'top';
@ -99,6 +100,7 @@ export interface IAccount {
}
const Account = ({
acct,
account,
actionType,
action,
@ -228,7 +230,7 @@ const Account = ({
<Stack space={withAccountNote || note ? 1 : 0}>
<HStack alignItems='center' space={1}>
<Text theme='muted' size='sm' direction='ltr' truncate>@{username}</Text>
<Text theme='muted' size='sm' direction='ltr' truncate>@{acct ?? username}</Text>
{account.pleroma?.favicon && (
<InstanceFavicon account={account} disabled={!withLinkToProfile} />

@ -1,6 +1,6 @@
import React from 'react';
import { approveUsers, deleteUsers } from 'soapbox/actions/admin';
import { approveUser, rejectUser } from 'soapbox/actions/admin';
import { useAccount } from 'soapbox/api/hooks';
import Account from 'soapbox/components/account';
import { AuthorizeRejectButtons } from 'soapbox/components/authorize-reject-buttons';
@ -14,18 +14,19 @@ interface IUnapprovedAccount {
const UnapprovedAccount: React.FC<IUnapprovedAccount> = ({ accountId }) => {
const dispatch = useAppDispatch();
const { account } = useAccount(accountId);
const adminAccount = useAppSelector(state => state.admin.users.get(accountId));
const { account } = useAccount(adminAccount?.account || undefined);
if (!account) return null;
if (!adminAccount || !account) return null;
const handleApprove = () => dispatch(approveUsers([account.id]));
const handleReject = () => dispatch(deleteUsers([account.id]));
const handleApprove = () => dispatch(approveUser(adminAccount.id));
const handleReject = () => dispatch(rejectUser(adminAccount.id));
return (
<Account
key={account.id}
key={adminAccount.id}
account={account}
acct={`${adminAccount.username}@${adminAccount.domain}`}
note={adminAccount?.invite_request || ''}
action={(
<AuthorizeRejectButtons

@ -19,6 +19,8 @@ import {
ADMIN_USERS_DELETE_SUCCESS,
ADMIN_USERS_APPROVE_REQUEST,
ADMIN_USERS_APPROVE_SUCCESS,
ADMIN_USERS_REJECT_REQUEST,
ADMIN_USERS_REJECT_SUCCESS,
} from 'soapbox/actions/admin';
import { normalizeAdminReport, normalizeAdminAccount } from 'soapbox/normalizers';
import { normalizeId } from 'soapbox/utils/normalizers';
@ -120,22 +122,18 @@ function importUsers(state: State, users: APIUser[], filters: Filter[], page: nu
});
}
function deleteUsers(state: State, accountIds: string[]): State {
function deleteUser(state: State, accountId: string): State {
return state.withMutations(state => {
accountIds.forEach(id => {
state.update('awaitingApproval', orderedSet => orderedSet.delete(id));
state.deleteIn(['users', id]);
});
state.update('awaitingApproval', orderedSet => orderedSet.delete(accountId));
state.deleteIn(['users', accountId]);
});
}
function approveUsers(state: State, users: APIUser[]): State {
function approveUser(state: State, user: APIUser): State {
const normalizedUser = fixUser(user);
return state.withMutations(state => {
users.forEach(user => {
const normalizedUser = fixUser(user);
state.update('awaitingApproval', orderedSet => orderedSet.delete(user.id));
state.setIn(['users', user.id], normalizedUser);
});
state.update('awaitingApproval', orderedSet => orderedSet.delete(user.id));
state.setIn(['users', user.id], normalizedUser);
});
}
@ -207,11 +205,13 @@ export default function admin(state: State = ReducerRecord(), action: AnyAction)
return importUsers(state, action.users, action.filters, action.page);
case ADMIN_USERS_DELETE_REQUEST:
case ADMIN_USERS_DELETE_SUCCESS:
return deleteUsers(state, action.accountIds);
case ADMIN_USERS_REJECT_REQUEST:
case ADMIN_USERS_REJECT_SUCCESS:
return deleteUser(state, action.accountId);
case ADMIN_USERS_APPROVE_REQUEST:
return state.update('awaitingApproval', set => set.subtract(action.accountIds));
return state.update('awaitingApproval', set => set.remove(action.accountId));
case ADMIN_USERS_APPROVE_SUCCESS:
return approveUsers(state, action.users);
return approveUser(state, action.user);
default:
return state;
}

Loading…
Cancel
Save