pl-fe: account moderation interface updates
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@@ -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'),
|
||||
});
|
||||
|
||||
|
||||
@@ -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'),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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'),
|
||||
});
|
||||
|
||||
|
||||
@@ -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')),
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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'));
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user