bigbuffet-rw/app/soapbox/features/group/components/group-member-list-item.tsx

208 lines
8.1 KiB
TypeScript
Raw Normal View History

2023-03-13 06:47:23 -07:00
import clsx from 'clsx';
import React, { useMemo } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { groupKick } from 'soapbox/actions/groups';
2023-03-13 06:47:23 -07:00
import { openModal } from 'soapbox/actions/modals';
import Account from 'soapbox/components/account';
import DropdownMenu from 'soapbox/components/dropdown-menu/dropdown-menu';
import { HStack } from 'soapbox/components/ui';
import { deleteEntities } from 'soapbox/entity-store/actions';
import { Entities } from 'soapbox/entity-store/entities';
import PlaceholderAccount from 'soapbox/features/placeholder/components/placeholder-account';
import { useAppDispatch, useFeatures } from 'soapbox/hooks';
import { useAccount, useBlockGroupMember, useDemoteGroupMember, usePromoteGroupMember } from 'soapbox/hooks/api';
2023-03-20 05:53:38 -07:00
import { GroupRoles } from 'soapbox/schemas/group-member';
2023-03-13 06:47:23 -07:00
import toast from 'soapbox/toast';
2023-04-04 11:05:08 -07:00
import { MAX_ADMIN_COUNT } from '../group-members';
2023-03-13 06:47:23 -07:00
import type { Menu as IMenu } from 'soapbox/components/dropdown-menu';
import type { Group, GroupMember } from 'soapbox/types/entities';
2023-03-13 06:47:23 -07:00
const messages = defineMessages({
2023-04-04 11:05:08 -07:00
adminLimitTitle: { id: 'group.member.admin.limit.title', defaultMessage: 'Admin limit reached' },
adminLimitSummary: { id: 'group.member.admin.limit.summary', defaultMessage: 'You can assign up to {count} admins for the group at this time.' },
2023-03-20 05:53:38 -07:00
blockConfirm: { id: 'confirmations.block_from_group.confirm', defaultMessage: 'Ban' },
blockFromGroupHeading: { id: 'confirmations.block_from_group.heading', defaultMessage: 'Ban From Group' },
blockFromGroupMessage: { id: 'confirmations.block_from_group.message', defaultMessage: 'Are you sure you want to ban @{name} from the group?' },
2023-03-20 05:53:38 -07:00
blocked: { id: 'group.group_mod_block.success', defaultMessage: '@{name} is banned' },
demotedToUser: { id: 'group.demote.user.success', defaultMessage: '@{name} is now a member' },
groupModBlock: { id: 'group.group_mod_block', defaultMessage: 'Ban from group' },
groupModDemote: { id: 'group.group_mod_demote', defaultMessage: 'Remove {role} role' },
2023-03-13 06:47:23 -07:00
groupModKick: { id: 'group.group_mod_kick', defaultMessage: 'Kick @{name} from group' },
groupModPromoteMod: { id: 'group.group_mod_promote_mod', defaultMessage: 'Assign {role} role' },
2023-03-13 06:47:23 -07:00
kickConfirm: { id: 'confirmations.kick_from_group.confirm', defaultMessage: 'Kick' },
kickFromGroupMessage: { id: 'confirmations.kick_from_group.message', defaultMessage: 'Are you sure you want to kick @{name} from this group?' },
kicked: { id: 'group.group_mod_kick.success', defaultMessage: 'Kicked @{name} from group' },
2023-03-20 05:53:38 -07:00
promoteConfirm: { id: 'group.promote.admin.confirmation.title', defaultMessage: 'Assign Admin Role' },
promoteConfirmMessage: { id: 'group.promote.admin.confirmation.message', defaultMessage: 'Are you sure you want to assign the admin role to @{name}?' },
promotedToAdmin: { id: 'group.promote.admin.success', defaultMessage: '@{name} is now an admin' },
2023-03-13 06:47:23 -07:00
});
interface IGroupMemberListItem {
member: GroupMember
group: Group
2023-04-04 11:05:08 -07:00
canPromoteToAdmin: boolean
2023-03-13 06:47:23 -07:00
}
const GroupMemberListItem = (props: IGroupMemberListItem) => {
2023-04-04 11:05:08 -07:00
const { canPromoteToAdmin, member, group } = props;
2023-03-13 06:47:23 -07:00
const dispatch = useAppDispatch();
const features = useFeatures();
2023-03-13 06:47:23 -07:00
const intl = useIntl();
const blockGroupMember = useBlockGroupMember(group, member);
const promoteGroupMember = usePromoteGroupMember(group, member);
const demoteGroupMember = useDemoteGroupMember(group, member);
2023-03-13 06:47:23 -07:00
const { account, isLoading } = useAccount(member.account.id);
2023-03-13 06:47:23 -07:00
// Current user role
2023-03-20 05:53:38 -07:00
const isCurrentUserOwner = group.relationship?.role === GroupRoles.OWNER;
const isCurrentUserAdmin = group.relationship?.role === GroupRoles.ADMIN;
2023-03-13 06:47:23 -07:00
// Member role
2023-03-20 05:53:38 -07:00
const isMemberOwner = member.role === GroupRoles.OWNER;
const isMemberAdmin = member.role === GroupRoles.ADMIN;
const isMemberUser = member.role === GroupRoles.USER;
2023-03-13 06:47:23 -07:00
const handleKickFromGroup = () => {
dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.kickFromGroupMessage, { name: account?.username }),
2023-03-13 06:47:23 -07:00
confirm: intl.formatMessage(messages.kickConfirm),
onConfirm: () => dispatch(groupKick(group.id, account?.id as string)).then(() =>
toast.success(intl.formatMessage(messages.kicked, { name: account?.acct })),
2023-03-13 06:47:23 -07:00
),
}));
};
const handleBlockFromGroup = () => {
dispatch(openModal('CONFIRM', {
heading: intl.formatMessage(messages.blockFromGroupHeading),
message: intl.formatMessage(messages.blockFromGroupMessage, { name: account?.username }),
2023-03-13 06:47:23 -07:00
confirm: intl.formatMessage(messages.blockConfirm),
onConfirm: () => {
blockGroupMember({ account_ids: [member.account.id] }, {
onSuccess() {
dispatch(deleteEntities([member.id], Entities.GROUP_MEMBERSHIPS));
toast.success(intl.formatMessage(messages.blocked, { name: account?.acct }));
},
});
},
2023-03-13 06:47:23 -07:00
}));
};
2023-03-20 05:53:38 -07:00
const handleAdminAssignment = () => {
2023-04-04 11:05:08 -07:00
if (!canPromoteToAdmin) {
toast.error(intl.formatMessage(messages.adminLimitTitle), {
summary: intl.formatMessage(messages.adminLimitSummary, { count: MAX_ADMIN_COUNT }),
});
return;
}
2023-03-20 05:53:38 -07:00
dispatch(openModal('CONFIRM', {
heading: intl.formatMessage(messages.promoteConfirm),
message: intl.formatMessage(messages.promoteConfirmMessage, { name: account?.username }),
2023-03-20 05:53:38 -07:00
confirm: intl.formatMessage(messages.promoteConfirm),
confirmationTheme: 'primary',
onConfirm: () => {
promoteGroupMember({ role: GroupRoles.ADMIN, account_ids: [account?.id] }, {
2023-03-20 05:53:38 -07:00
onSuccess() {
toast.success(
intl.formatMessage(messages.promotedToAdmin, { name: account?.acct }),
2023-03-20 05:53:38 -07:00
);
},
});
},
}));
2023-03-13 06:47:23 -07:00
};
2023-03-20 05:53:38 -07:00
const handleUserAssignment = () => {
demoteGroupMember({ role: GroupRoles.USER, account_ids: [account?.id] }, {
onSuccess() {
toast.success(intl.formatMessage(messages.demotedToUser, { name: account?.acct }));
},
});
2023-03-13 06:47:23 -07:00
};
const menu: IMenu = useMemo(() => {
const items: IMenu = [];
if (!group || !account || !group.relationship?.role) {
return items;
}
2023-03-20 05:53:38 -07:00
if (isCurrentUserOwner) {
if (isMemberUser) {
items.push({
2023-03-20 05:53:38 -07:00
text: intl.formatMessage(messages.groupModPromoteMod, { role: GroupRoles.ADMIN }),
icon: require('@tabler/icons/briefcase.svg'),
2023-03-20 05:53:38 -07:00
action: handleAdminAssignment,
});
2023-03-20 05:53:38 -07:00
} else if (isMemberAdmin) {
items.push({
2023-03-20 05:53:38 -07:00
text: intl.formatMessage(messages.groupModDemote, { role: GroupRoles.ADMIN, name: account.username }),
icon: require('@tabler/icons/briefcase.svg'),
2023-03-20 05:53:38 -07:00
action: handleUserAssignment,
destructive: true,
});
}
}
2023-03-13 06:47:23 -07:00
if (
2023-03-20 05:53:38 -07:00
(isCurrentUserOwner || isCurrentUserAdmin) &&
(isMemberAdmin || isMemberUser) &&
2023-03-13 06:47:23 -07:00
member.role !== group.relationship.role
) {
if (features.groupsKick) {
items.push({
text: intl.formatMessage(messages.groupModKick, { name: account.username }),
icon: require('@tabler/icons/user-minus.svg'),
action: handleKickFromGroup,
});
}
2023-03-13 06:47:23 -07:00
items.push({
text: intl.formatMessage(messages.groupModBlock, { name: account.username }),
icon: require('@tabler/icons/ban.svg'),
action: handleBlockFromGroup,
destructive: true,
2023-03-13 06:47:23 -07:00
});
}
return items;
}, [group, account?.id]);
if (isLoading) {
return <PlaceholderAccount />;
}
2023-03-13 06:47:23 -07:00
return (
<HStack alignItems='center' justifyContent='between'>
<div className='w-full'>
<Account account={member.account} withRelationship={false} />
</div>
<HStack alignItems='center' space={2}>
2023-03-20 05:53:38 -07:00
{(isMemberOwner || isMemberAdmin) ? (
2023-03-13 06:47:23 -07:00
<span
className={
clsx('inline-flex items-center rounded px-2 py-1 text-xs font-medium capitalize', {
2023-03-20 05:53:38 -07:00
'bg-primary-200 text-primary-500': isMemberOwner,
'bg-gray-200 text-gray-900': isMemberAdmin,
2023-03-13 06:47:23 -07:00
})
}
>
{member.role}
</span>
) : null}
<DropdownMenu items={menu} />
2023-03-13 06:47:23 -07:00
</HStack>
</HStack>
);
};
export default GroupMemberListItem;