pl-fe: account moderation interface updates

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2025-08-01 07:25:47 +02:00
parent 48b714e52a
commit 8aa61a6d0e
15 changed files with 276 additions and 198 deletions

View File

@@ -777,11 +777,6 @@ const MenuButton: React.FC<IMenuButton> = ({
copy(uri);
};
const onModerate: React.MouseEventHandler = (e) => {
const account = status.account;
openModal('ACCOUNT_MODERATION', { accountId: account.id });
};
const handleDeleteStatus: React.EventHandler<React.MouseEvent> = (e) => {
dispatch(deleteStatusModal(intl, status.id));
};
@@ -1073,7 +1068,7 @@ const MenuButton: React.FC<IMenuButton> = ({
menu.push({
text: intl.formatMessage(messages.adminAccount, { name: username }),
action: onModerate,
to: `/pl-fe/admin/accounts/${status.account_id}`,
icon: require('@tabler/icons/outline/gavel.svg'),
});

View File

@@ -272,10 +272,6 @@ const Header: React.FC<IHeader> = ({ account }) => {
});
};
const onModerate = () => {
openModal('ACCOUNT_MODERATION', { accountId: account.id });
};
const onRemoveFromFollowers = () => {
const unfollowModal = settings.unfollowModal;
if (unfollowModal) {
@@ -549,7 +545,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
menu.push({
text: intl.formatMessage(messages.adminAccount, { name: account.username }),
action: onModerate,
to: `/pl-fe/admin/accounts/${account.id}`,
icon: require('@tabler/icons/outline/gavel.svg'),
});
}

View File

@@ -64,7 +64,9 @@ const Report: React.FC<IReport> = ({ id }) => {
/>
</Text>
<HoverAccountWrapper accountId={account.id} element='span'>
@{reporterAcct}
<Link to={`/pl-fe/admin/accounts/${account.id}`}>
@{reporterAcct}
</Link>
</HoverAccountWrapper>
</HStack>
)}

View File

@@ -204,10 +204,6 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
dispatch(initReport(ReportableEntities.STATUS, account, { status }));
};
const handleModerate = () => {
openModal('ACCOUNT_MODERATION', { accountId: account.id });
};
const handleModerateStatus = () => {
window.open(`/pleroma/admin/#/statuses/${status.id}/`, '_blank');
};
@@ -357,8 +353,8 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
menu.push(null);
menu.push({
text: intl.formatMessage(messages.adminAccount, { name: account.username }),
action: handleModerate,
text: intl.formatMessage(messages.adminAccount, { name: username }),
to: `/pl-fe/admin/accounts/${account.id}`,
icon: require('@tabler/icons/outline/gavel.svg'),
});

View File

@@ -9,7 +9,6 @@ import ModalLoading from './modal-loading';
/* eslint sort-keys: "error" */
const MODAL_COMPONENTS = {
ACCOUNT_MODERATION: lazy(() => import('pl-fe/modals/account-moderation-modal')),
ALT_TEXT: lazy(() => import('pl-fe/modals/alt-text-modal')),
BIRTHDAYS: lazy(() => import('pl-fe/modals/birthdays-modal')),
BOOST: lazy(() => import('pl-fe/modals/boost-modal')),

View File

@@ -56,6 +56,7 @@ import {
AccountGallery,
AccountHoverCard,
AccountTimeline,
AdminAccount,
Aliases,
Announcements,
AuthTokenList,
@@ -329,6 +330,7 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = React.memo(({ chil
<WrappedRoute path='/pl-fe/config' adminOnly layout={DefaultLayout} component={PlFeConfig} content={children} />
<WrappedRoute path='/pl-fe/admin' staffOnly layout={AdminLayout} component={Dashboard} content={children} exact />
<WrappedRoute path='/pl-fe/admin/accounts/:accountId' staffOnly layout={AdminLayout} component={AdminAccount} content={children} exact />
<WrappedRoute path='/pl-fe/admin/approval' staffOnly layout={AdminLayout} component={Dashboard} content={children} exact />
<WrappedRoute path='/pl-fe/admin/reports' staffOnly layout={AdminLayout} component={Dashboard} content={children} exact />
<WrappedRoute path='/pl-fe/admin/reports/:reportId' staffOnly layout={AdminLayout} component={Report} content={children} exact />

View File

@@ -1,9 +1,10 @@
import { lazy } from 'react';
// Pages
export const AboutPage = lazy(() => import('pl-fe/pages/utils/about'));
export const AccountGallery = lazy(() => import('pl-fe/pages/accounts/account-gallery'));
export const AccountTimeline = lazy(() => import('pl-fe/pages/accounts/account-timeline'));
export const AboutPage = lazy(() => import('pl-fe/pages/utils/about'));
export const AdminAccount = lazy(() => import('pl-fe/pages/dashboard/account'));
export const Aliases = lazy(() => import('pl-fe/pages/settings/aliases'));
export const Announcements = lazy(() => import('pl-fe/pages/dashboard/announcements'));
export const AuthTokenList = lazy(() => import('pl-fe/pages/settings/auth-token-list'));

View File

@@ -97,7 +97,6 @@
"account_moderation_modal.roles.admin": "Admin",
"account_moderation_modal.roles.moderator": "Moderator",
"account_moderation_modal.roles.user": "User",
"account_moderation_modal.title": "Moderate @{acct}",
"account_note.header": "Note",
"account_note.placeholder": "Click to add a note",
"account_search.placeholder": "Search for an account",
@@ -193,7 +192,7 @@
"admin.report.action_taken.true": "Resolved",
"admin.report.assigned_account": "Assigned moderator",
"admin.report.created_at": "Reported",
"admin.report.moderate": "Open account in moderation interface",
"admin.report.moderate": "Moderate account",
"admin.report.reopen": "Reopen report",
"admin.report.reported_by": "Reported by",
"admin.report.resolve": "Mark as resolved",
@@ -358,6 +357,7 @@
"circles.new.create_title": "Add circle",
"circles.new.title_placeholder": "New circle title",
"circles.subheading": "Your circles",
"column.admin.account": "Moderate @{acct}",
"column.admin.announcements": "Announcements",
"column.admin.awaiting_approval": "Awaiting Approval",
"column.admin.create_announcement": "Create announcement",

View File

@@ -1,37 +0,0 @@
import React from 'react';
import { useIntl, defineMessages } from 'react-intl';
import TagInput from 'pl-fe/components/ui/tag-input';
import { badgeToTag, tagToBadge } from 'pl-fe/utils/badges';
const messages = defineMessages({
placeholder: { id: 'badge_input.placeholder', defaultMessage: 'Enter a badge…' },
});
interface IBadgeInput {
/** A badge is a tag that begins with `badge:` */
badges: string[];
/** Callback when badges change. */
onChange: (badges: string[]) => void;
}
/** Manages user badges. */
const BadgeInput: React.FC<IBadgeInput> = ({ badges, onChange }) => {
const intl = useIntl();
const tags = badges.map(badgeToTag);
const handleTagsChange = (tags: string[]) => {
const badges = tags.map(tagToBadge);
onChange(badges);
};
return (
<TagInput
tags={tags}
onChange={handleTagsChange}
placeholder={intl.formatMessage(messages.placeholder)}
/>
);
};
export { BadgeInput as default };

View File

@@ -1,86 +0,0 @@
import React, { useMemo } from 'react';
import { defineMessages, MessageDescriptor, useIntl } from 'react-intl';
import { setRole } from 'pl-fe/actions/admin';
import { SelectDropdown } from 'pl-fe/features/forms';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import toast from 'pl-fe/toast';
import type { Account as AccountEntity } from 'pl-fe/normalizers/account';
/** Staff role. */
type AccountRole = 'user' | 'moderator' | 'admin';
/** Get the highest staff role associated with the account. */
const getRole = (account: Pick<AccountEntity, 'is_admin' | 'is_moderator'>): AccountRole => {
if (account.is_admin) {
return 'admin';
} else if (account.is_moderator) {
return 'moderator';
} else {
return 'user';
}
};
const messages = defineMessages({
roleUser: { id: 'account_moderation_modal.roles.user', defaultMessage: 'User' },
roleModerator: { id: 'account_moderation_modal.roles.moderator', defaultMessage: 'Moderator' },
roleAdmin: { id: 'account_moderation_modal.roles.admin', defaultMessage: 'Admin' },
promotedToAdmin: { id: 'admin.users.actions.promote_to_admin_message', defaultMessage: '@{acct} was promoted to an admin' },
promotedToModerator: { id: 'admin.users.actions.promote_to_moderator_message', defaultMessage: '@{acct} was promoted to a moderator' },
demotedToModerator: { id: 'admin.users.actions.demote_to_moderator_message', defaultMessage: '@{acct} was demoted to a moderator' },
demotedToUser: { id: 'admin.users.actions.demote_to_user_message', defaultMessage: '@{acct} was demoted to a regular user' },
});
interface IStaffRolePicker {
/** Account whose role to change. */
account: Pick<AccountEntity, 'id' | 'acct' | 'is_admin' | 'is_moderator'>;
}
/** Picker for setting the staff role of an account. */
const StaffRolePicker: React.FC<IStaffRolePicker> = ({ account }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const roles: Record<AccountRole, string> = useMemo(() => ({
user: intl.formatMessage(messages.roleUser),
moderator: intl.formatMessage(messages.roleModerator),
admin: intl.formatMessage(messages.roleAdmin),
}), []);
const handleRoleChange: React.ChangeEventHandler<HTMLSelectElement> = (e) => {
const role = e.target.value as AccountRole;
dispatch(setRole(account.id, role))
.then(() => {
let message: MessageDescriptor | undefined;
if (role === 'admin') {
message = messages.promotedToAdmin;
} else if (role === 'moderator' && account.is_admin) {
message = messages.demotedToModerator;
} else if (role === 'moderator') {
message = messages.promotedToModerator;
} else if (role === 'user') {
message = messages.demotedToUser;
}
if (message) {
toast.success(intl.formatMessage(message, { acct: account.acct }));
}
})
.catch(() => {});
};
const accountRole = getRole(account);
return (
<SelectDropdown
items={roles}
defaultValue={accountRole}
onChange={handleRoleChange}
/>
);
};
export { StaffRolePicker as default };

View File

@@ -1,8 +1,8 @@
import { PLEROMA } from 'pl-api';
import React, { ChangeEventHandler, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import React, { ChangeEventHandler, useMemo, useState } from 'react';
import { defineMessages, FormattedMessage, type MessageDescriptor, useIntl } from 'react-intl';
import { setBadges as saveBadges } from 'pl-fe/actions/admin';
import { setBadges as saveBadges, setRole } from 'pl-fe/actions/admin';
import { deactivateUserModal, deleteUserModal } from 'pl-fe/actions/moderation';
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
import { useSuggest } from 'pl-fe/api/hooks/admin/use-suggest';
@@ -12,37 +12,139 @@ import List, { ListItem } from 'pl-fe/components/list';
import MissingIndicator from 'pl-fe/components/missing-indicator';
import OutlineBox from 'pl-fe/components/outline-box';
import Button from 'pl-fe/components/ui/button';
import Column from 'pl-fe/components/ui/column';
import HStack from 'pl-fe/components/ui/hstack';
import Modal from 'pl-fe/components/ui/modal';
import Stack from 'pl-fe/components/ui/stack';
import TagInput from 'pl-fe/components/ui/tag-input';
import Text from 'pl-fe/components/ui/text';
import Toggle from 'pl-fe/components/ui/toggle';
import { SelectDropdown } from 'pl-fe/features/forms';
import ColumnLoading from 'pl-fe/features/ui/components/column-loading';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useFeatures } from 'pl-fe/hooks/use-features';
import { useOwnAccount } from 'pl-fe/hooks/use-own-account';
import toast from 'pl-fe/toast';
import { getBadges } from 'pl-fe/utils/badges';
import { badgeToTag, tagToBadge, getBadges } from 'pl-fe/utils/badges';
import BadgeInput from './badge-input';
import StaffRolePicker from './staff-role-picker';
import type { BaseModalProps } from 'pl-fe/features/ui/components/modal-root';
import type { Account as AccountEntity } from 'pl-fe/normalizers/account';
const messages = defineMessages({
columnHeading: { id: 'column.admin.account', defaultMessage: 'Moderate @{acct}' },
userVerified: { id: 'admin.users.user_verified_message', defaultMessage: '@{acct} was verified' },
userUnverified: { id: 'admin.users.user_unverified_message', defaultMessage: '@{acct} was unverified' },
userSuggested: { id: 'admin.users.user_suggested_message', defaultMessage: '@{acct} was suggested' },
userUnsuggested: { id: 'admin.users.user_unsuggested_message', defaultMessage: '@{acct} was unsuggested' },
badgesSaved: { id: 'admin.users.badges_saved_message', defaultMessage: 'Custom badges updated.' },
badgePlaceholder: { id: 'badge_input.placeholder', defaultMessage: 'Enter a badge…' },
roleUser: { id: 'account_moderation_modal.roles.user', defaultMessage: 'User' },
roleModerator: { id: 'account_moderation_modal.roles.moderator', defaultMessage: 'Moderator' },
roleAdmin: { id: 'account_moderation_modal.roles.admin', defaultMessage: 'Admin' },
promotedToAdmin: { id: 'admin.users.actions.promote_to_admin_message', defaultMessage: '@{acct} was promoted to an admin' },
promotedToModerator: { id: 'admin.users.actions.promote_to_moderator_message', defaultMessage: '@{acct} was promoted to a moderator' },
demotedToModerator: { id: 'admin.users.actions.demote_to_moderator_message', defaultMessage: '@{acct} was demoted to a moderator' },
demotedToUser: { id: 'admin.users.actions.demote_to_user_message', defaultMessage: '@{acct} was demoted to a regular user' },
});
interface AccountModerationModalProps {
/** ID of the account to moderate. */
accountId: string;
/** Staff role. */
type AccountRole = 'user' | 'moderator' | 'admin';
/** Get the highest staff role associated with the account. */
const getRole = (account: Pick<AccountEntity, 'is_admin' | 'is_moderator'>): AccountRole => {
if (account.is_admin) {
return 'admin';
} else if (account.is_moderator) {
return 'moderator';
} else {
return 'user';
}
};
interface IStaffRolePicker {
/** Account whose role to change. */
account: Pick<AccountEntity, 'id' | 'acct' | 'is_admin' | 'is_moderator'>;
}
/** Moderator actions against accounts. */
const AccountModerationModal: React.FC<AccountModerationModalProps & BaseModalProps> = ({ onClose, accountId }) => {
/** Picker for setting the staff role of an account. */
const StaffRolePicker: React.FC<IStaffRolePicker> = ({ account }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const roles: Record<AccountRole, string> = useMemo(() => ({
user: intl.formatMessage(messages.roleUser),
moderator: intl.formatMessage(messages.roleModerator),
admin: intl.formatMessage(messages.roleAdmin),
}), []);
const handleRoleChange: React.ChangeEventHandler<HTMLSelectElement> = (e) => {
const role = e.target.value as AccountRole;
dispatch(setRole(account.id, role))
.then(() => {
let message: MessageDescriptor | undefined;
if (role === 'admin') {
message = messages.promotedToAdmin;
} else if (role === 'moderator' && account.is_admin) {
message = messages.demotedToModerator;
} else if (role === 'moderator') {
message = messages.promotedToModerator;
} else if (role === 'user') {
message = messages.demotedToUser;
}
if (message) {
toast.success(intl.formatMessage(message, { acct: account.acct }));
}
})
.catch(() => {});
};
const accountRole = getRole(account);
return (
<SelectDropdown
items={roles}
defaultValue={accountRole}
onChange={handleRoleChange}
/>
);
};
interface IBadgeInput {
/** A badge is a tag that begins with `badge:` */
badges: string[];
/** Callback when badges change. */
onChange: (badges: string[]) => void;
}
/** Manages user badges. */
const BadgeInput: React.FC<IBadgeInput> = ({ badges, onChange }) => {
const intl = useIntl();
const tags = badges.map(badgeToTag);
const handleTagsChange = (tags: string[]) => {
const badges = tags.map(tagToBadge);
onChange(badges);
};
return (
<TagInput
tags={tags}
onChange={handleTagsChange}
placeholder={intl.formatMessage(messages.badgePlaceholder)}
/>
);
};
type RouteParams = { accountId: string };
interface IAdminAccountPage {
params: RouteParams;
}
const AdminAccountPage: React.FC<IAdminAccountPage> = (props) => {
const { accountId } = props.params;
const intl = useIntl();
const dispatch = useAppDispatch();
@@ -50,18 +152,20 @@ const AccountModerationModal: React.FC<AccountModerationModalProps & BaseModalPr
const { verify, unverify } = useVerify();
const { account: ownAccount } = useOwnAccount();
const features = useFeatures();
const { account } = useAccount(accountId);
const { account, isLoading } = useAccount(accountId);
const accountBadges = account ? getBadges(account) : [];
const [badges, setBadges] = useState<string[]>(accountBadges);
const handleClose = () => onClose('ACCOUNT_MODERATION');
if (isLoading) {
return <ColumnLoading />;
}
if (!account || !ownAccount) {
return (
<Modal onClose={handleClose}>
<Column>
<MissingIndicator />
</Modal>
</Column>
);
}
@@ -106,10 +210,7 @@ const AccountModerationModal: React.FC<AccountModerationModalProps & BaseModalPr
};
return (
<Modal
title={<FormattedMessage id='account_moderation_modal.title' defaultMessage='Moderate @{acct}' values={{ acct: account.acct }} />}
onClose={handleClose}
>
<Column label={intl.formatMessage(messages.columnHeading, { acct: account.acct })}>
<Stack space={4}>
<OutlineBox>
<Account
@@ -189,8 +290,8 @@ const AccountModerationModal: React.FC<AccountModerationModalProps & BaseModalPr
</HStack>
)}
</Stack>
</Modal>
</Column>
);
};
export { type AccountModerationModalProps, AccountModerationModal as default };
export { AdminAccountPage as default };

View File

@@ -34,8 +34,6 @@ const messages = defineMessages({
reportCommentConfirm: { id: 'report.resolve.comment.confirm', defaultMessage: 'Resolve report' },
});
type RouteParams = { reportId: string };
interface IReportStatuses {
statusIds: Array<string>;
}
@@ -76,6 +74,8 @@ const ReportStatuses: React.FC<IReportStatuses> = ({ statusIds }) => {
);
};
type RouteParams = { reportId: string };
interface IReportPage {
params: RouteParams;
}
@@ -175,19 +175,23 @@ const ReportPage: React.FC<IReportPage> = (props) => {
</Text>
</td>
</tr>
<tr className='border-b border-primary-200 last:border-none dark:border-gray-800'>
<td className='p-2.5'>
<Text weight='medium' size='sm' tag='span'>
<FormattedMessage id='admin.report.reported_by' defaultMessage='Reported by' />
</Text>
</td>
{report.account && (
<tr className='border-b border-primary-200 last:border-none dark:border-gray-800'>
<td className='p-2.5'>
<Text weight='medium' size='sm' tag='span'>
<FormattedMessage id='admin.report.reported_by' defaultMessage='Reported by' />
</Text>
</td>
<td className='p-2.5 text-end'>
<Text size='sm'>
{report.account?.acct}
</Text>
</td>
</tr>
<td className='p-2.5 text-end'>
<Text size='sm' className='hover:underline'>
<Link to={`/pl-fe/admin/accounts/${report.account_id}`}>
@{report.account.acct}
</Link>
</Text>
</td>
</tr>
)}
<tr className='border-b border-primary-200 last:border-none dark:border-gray-800'>
<td className='p-2.5'>
<Text weight='medium' size='sm' tag='span'>
@@ -212,8 +216,8 @@ const ReportPage: React.FC<IReportPage> = (props) => {
<td className='p-2.5 text-end'>
{report.assigned_account ? (
<HStack space={2} alignItems='center' justifyContent='end'>
<Text size='sm'>
<Link to={`/@${report.assigned_account.acct}`}>
<Text size='sm' className='hover:underline'>
<Link to={`/pl-fe/admin/accounts/${report.assigned_account.id}`}>
@{report.assigned_account.acct}
</Link>
</Text>
@@ -263,8 +267,8 @@ const ReportPage: React.FC<IReportPage> = (props) => {
/>
)}
<ListItem
label={<FormattedMessage id='admin.report.moderate' defaultMessage='Open account in moderation interface' />}
onClick={() => openModal('ACCOUNT_MODERATION', { accountId: report.target_account_id })}
label={<FormattedMessage id='admin.report.moderate' defaultMessage='Moderate account' />}
to={`/pl-fe/admin/accounts/${report.target_account_id}`}
/>
</List>
</Column>

View File

@@ -1,5 +1,5 @@
import { InfiniteData, useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { PaginatedResponse, type AdminAccount, type AdminGetAccountsParams, type PaginationParams } from 'pl-api';
import { importEntities } from 'pl-fe/actions/importer';
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
@@ -10,7 +10,9 @@ import { useOwnAccount } from 'pl-fe/hooks/use-own-account';
import { filterById } from '../utils/filter-id';
import { makePaginatedResponseQuery } from '../utils/make-paginated-response-query';
import { makePaginatedResponseQueryOptions } from '../utils/make-paginated-response-query-options';
import { minifyAdminAccountList } from '../utils/minify-list';
import { minifyAdminAccount, minifyAdminAccountList } from '../utils/minify-list';
import type { AdminPerformAccountActionParams, PaginatedResponse, AdminAccount, AdminGetAccountsParams, PaginationParams, AdminAccountAction } from 'pl-api';
const useAdminAccounts = makePaginatedResponseQuery(
@@ -93,4 +95,109 @@ const useAdminRejectAccountMutation = (accountId: string) => {
});
};
export { useAdminAccount, useAdminAccounts, pendingUsersQuery, usePendingUsersCount, useAdminApproveAccountMutation, useAdminRejectAccountMutation };
const useAdminDeleteAccountMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['admin', 'acounts', accountId],
mutationFn: () => client.admin.accounts.deleteAccount(accountId),
onSuccess: () => {
queryClient.setQueriesData<InfiniteData<PaginatedResponse<string>>>({
queryKey: ['admin', 'accountLists'],
exact: false,
}, filterById(accountId));
},
});
};
const useAdminPerformAccountActionMutation = (accountId: string, type: AdminAccountAction) => {
const client = useClient();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['admin', 'acounts', accountId],
mutationFn: (params?: AdminPerformAccountActionParams) => client.admin.accounts.performAccountAction(accountId, type, params),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['admin', 'accountLists'], exact: false });
},
});
};
const useAdminEnableAccountMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['admin', 'acounts', accountId],
mutationFn: () => client.admin.accounts.enableAccount(accountId),
onSuccess: (account) => {
queryClient.setQueriesData<InfiniteData<PaginatedResponse<string>>>({
queryKey: ['admin', 'accountLists', { status: 'disabled' }],
exact: false,
}, filterById(accountId));
minifyAdminAccount(account);
},
});
};
const useAdminUnsilenceAccountMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['admin', 'acounts', accountId],
mutationFn: () => client.admin.accounts.unsilenceAccount(accountId),
onSuccess: (account) => {
queryClient.setQueriesData<InfiniteData<PaginatedResponse<string>>>({
queryKey: ['admin', 'accountLists', { status: 'silenced' }],
exact: false,
}, filterById(accountId));
minifyAdminAccount(account);
},
});
};
const useAdminUnsuspendAccountMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['admin', 'acounts', accountId],
mutationFn: () => client.admin.accounts.unsuspendAccount(accountId),
onSuccess: (account) => {
queryClient.setQueriesData<InfiniteData<PaginatedResponse<string>>>({
queryKey: ['admin', 'accountLists', { status: 'suspended' }],
exact: false,
}, filterById(accountId));
minifyAdminAccount(account);
},
});
};
const useAdminUnsensitiveAccountMutation = (accountId: string) => {
const client = useClient();
return useMutation({
mutationKey: ['admin', 'acounts', accountId],
mutationFn: () => client.admin.accounts.unsensitiveAccount(accountId),
onSuccess: (account) => {
minifyAdminAccount(account);
},
});
};
export {
useAdminAccount,
useAdminAccounts,
pendingUsersQuery,
usePendingUsersCount,
useAdminApproveAccountMutation,
useAdminRejectAccountMutation,
useAdminDeleteAccountMutation,
useAdminPerformAccountActionMutation,
useAdminEnableAccountMutation,
useAdminUnsilenceAccountMutation,
useAdminUnsuspendAccountMutation,
useAdminUnsensitiveAccountMutation,
};

View File

@@ -26,12 +26,12 @@ const minifyAccountList = (response: PaginatedResponse<Account>): PaginatedRespo
store.dispatch(importEntities({ accounts }) as any);
});
// const minifyAdminAccount = ({ account, ...adminAccount }: AdminAccount) => {
// store.dispatch(importEntities({ accounts: [account] }) as any);
// queryClient.setQueryData(['admin', 'accounts', adminAccount.id], adminAccount);
const minifyAdminAccount = ({ account, ...adminAccount }: AdminAccount) => {
store.dispatch(importEntities({ accounts: [account] }) as any);
queryClient.setQueryData(['admin', 'accounts', adminAccount.id], adminAccount);
// return adminAccount;
// };
return adminAccount;
};
const minifyAdminAccountList = (response: PaginatedResponse<AdminAccount>) =>
minifyList(response, (account) => account.id, (accounts) => {
@@ -63,4 +63,4 @@ const minifyAdminReportList = (response: PaginatedResponse<AdminReport>) =>
}
});
export { minifyList, minifyAccountList, minifyStatusList, minifyAdminAccountList, minifyAdminReport, minifyAdminReportList };
export { minifyList, minifyAccountList, minifyStatusList, minifyAdminAccount, minifyAdminAccountList, minifyAdminReport, minifyAdminReportList };

View File

@@ -5,7 +5,6 @@ import { MuteModalProps } from 'pl-fe/modals/mute-modal';
import type { ICryptoAddress } from 'pl-fe/features/crypto-donate/components/crypto-address';
import type { ModalType } from 'pl-fe/features/ui/components/modal-root';
import type { AccountModerationModalProps } from 'pl-fe/modals/account-moderation-modal';
import type { AltTextModalProps } from 'pl-fe/modals/alt-text-modal';
import type { BoostModalProps } from 'pl-fe/modals/boost-modal';
import type { CompareHistoryModalProps } from 'pl-fe/modals/compare-history-modal';
@@ -41,7 +40,6 @@ import type { UnauthorizedModalProps } from 'pl-fe/modals/unauthorized-modal';
import type { VideoModalProps } from 'pl-fe/modals/video-modal';
type OpenModalProps =
| [type: 'ACCOUNT_MODERATION', props: AccountModerationModalProps]
| [type: 'ALT_TEXT', props: AltTextModalProps]
| [type: 'BIRTHDAYS' | 'CREATE_GROUP' | 'HOTKEYS']
| [type: 'BOOST', props: BoostModalProps]