sforkowany z mirror/soapbox
Merge branch 'promote-to-admin' into 'develop'
Move Promote/Demote admin into entity store See merge request soapbox-pub/soapbox!2359develop^2
commit
e46a7e8f4a
|
@ -1,7 +1,7 @@
|
|||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
import { useGroupRoles } from 'soapbox/hooks/useGroupRoles';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||
|
||||
import { Avatar } from '../ui';
|
||||
|
||||
|
@ -16,17 +16,15 @@ interface IGroupAvatar {
|
|||
const GroupAvatar = (props: IGroupAvatar) => {
|
||||
const { group, size, withRing = false } = props;
|
||||
|
||||
const { normalizeRole } = useGroupRoles();
|
||||
|
||||
const isAdmin = normalizeRole(group.relationship?.role as any) === 'admin';
|
||||
const isOwner = group.relationship?.role === GroupRoles.OWNER;
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
className={
|
||||
clsx('relative rounded-full', {
|
||||
'shadow-[0_0_0_2px_theme(colors.primary.600),0_0_0_4px_theme(colors.white)]': isAdmin && withRing,
|
||||
'shadow-[0_0_0_2px_theme(colors.primary.600)]': isAdmin && !withRing,
|
||||
'shadow-[0_0_0_2px_theme(colors.white)]': !isAdmin && withRing,
|
||||
'shadow-[0_0_0_2px_theme(colors.primary.600),0_0_0_4px_theme(colors.white)]': isOwner && withRing,
|
||||
'shadow-[0_0_0_2px_theme(colors.primary.600)]': isOwner && !withRing,
|
||||
'shadow-[0_0_0_2px_theme(colors.white)]': !isOwner && withRing,
|
||||
})
|
||||
}
|
||||
src={group.avatar}
|
||||
|
|
|
@ -28,6 +28,10 @@ interface EntityActionEndpoints {
|
|||
delete?: string
|
||||
}
|
||||
|
||||
interface EntityCallbacks<TEntity extends Entity = Entity> {
|
||||
onSuccess?(entity: TEntity): void
|
||||
}
|
||||
|
||||
function useEntityActions<TEntity extends Entity = Entity, P = any>(
|
||||
path: EntityPath,
|
||||
endpoints: EntityActionEndpoints,
|
||||
|
@ -38,7 +42,7 @@ function useEntityActions<TEntity extends Entity = Entity, P = any>(
|
|||
const getState = useGetState();
|
||||
const [entityType, listKey] = path;
|
||||
|
||||
function createEntity(params: P): Promise<CreateEntityResult<TEntity>> {
|
||||
function createEntity(params: P, callbacks: EntityCallbacks = {}): Promise<CreateEntityResult<TEntity>> {
|
||||
if (!endpoints.post) return Promise.reject(endpoints);
|
||||
|
||||
return api.post(endpoints.post, params).then((response) => {
|
||||
|
@ -48,6 +52,10 @@ function useEntityActions<TEntity extends Entity = Entity, P = any>(
|
|||
// TODO: optimistic updating
|
||||
dispatch(importEntities([entity], entityType, listKey));
|
||||
|
||||
if (callbacks.onSuccess) {
|
||||
callbacks.onSuccess(entity);
|
||||
}
|
||||
|
||||
return {
|
||||
response,
|
||||
entity,
|
||||
|
|
|
@ -2,7 +2,7 @@ import clsx from 'clsx';
|
|||
import React, { useMemo } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { groupDemoteAccount, groupKick, groupPromoteAccount } from 'soapbox/actions/groups';
|
||||
import { groupKick } from 'soapbox/actions/groups';
|
||||
import { openModal } from 'soapbox/actions/modals';
|
||||
import Account from 'soapbox/components/account';
|
||||
import DropdownMenu from 'soapbox/components/dropdown-menu/dropdown-menu';
|
||||
|
@ -10,31 +10,29 @@ import { HStack } from 'soapbox/components/ui';
|
|||
import { deleteEntities } from 'soapbox/entity-store/actions';
|
||||
import { Entities } from 'soapbox/entity-store/entities';
|
||||
import { useAccount, useAppDispatch, useFeatures } from 'soapbox/hooks';
|
||||
import { useBlockGroupMember } from 'soapbox/hooks/api/groups/useBlockGroupMember';
|
||||
import { BaseGroupRoles, useGroupRoles } from 'soapbox/hooks/useGroupRoles';
|
||||
import { useBlockGroupMember, useDemoteGroupMember, usePromoteGroupMember } from 'soapbox/hooks/api';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||
import toast from 'soapbox/toast';
|
||||
|
||||
import type { Menu as IMenu } from 'soapbox/components/dropdown-menu';
|
||||
import type { Account as AccountEntity, Group, GroupMember } from 'soapbox/types/entities';
|
||||
|
||||
const messages = defineMessages({
|
||||
blockConfirm: { id: 'confirmations.block_from_group.confirm', defaultMessage: 'Block' },
|
||||
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?' },
|
||||
blocked: { id: 'group.group_mod_block.success', defaultMessage: 'You have successfully blocked @{name} from the group' },
|
||||
demotedToUser: { id: 'group.group_mod_demote.success', defaultMessage: 'Demoted @{name} to group user' },
|
||||
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: 'Demote @{name}' },
|
||||
groupModDemote: { id: 'group.group_mod_demote', defaultMessage: 'Remove {role} role' },
|
||||
groupModKick: { id: 'group.group_mod_kick', defaultMessage: 'Kick @{name} from group' },
|
||||
groupModPromoteAdmin: { id: 'group.group_mod_promote_admin', defaultMessage: 'Promote @{name} to group administrator' },
|
||||
groupModPromoteMod: { id: 'group.group_mod_promote_mod', defaultMessage: 'Promote @{name} to group moderator' },
|
||||
groupModPromoteMod: { id: 'group.group_mod_promote_mod', defaultMessage: 'Assign {role} role' },
|
||||
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' },
|
||||
promoteConfirm: { id: 'confirmations.promote_in_group.confirm', defaultMessage: 'Promote' },
|
||||
promoteConfirmMessage: { id: 'confirmations.promote_in_group.message', defaultMessage: 'Are you sure you want to promote @{name}? You will not be able to demote them.' },
|
||||
promotedToAdmin: { id: 'group.group_mod_promote_admin.success', defaultMessage: 'Promoted @{name} to group administrator' },
|
||||
promotedToMod: { id: 'group.group_mod_promote_mod.success', defaultMessage: 'Promoted @{name} to group moderator' },
|
||||
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' },
|
||||
});
|
||||
|
||||
interface IGroupMemberListItem {
|
||||
|
@ -49,19 +47,20 @@ const GroupMemberListItem = (props: IGroupMemberListItem) => {
|
|||
const features = useFeatures();
|
||||
const intl = useIntl();
|
||||
|
||||
const { normalizeRole } = useGroupRoles();
|
||||
const blockGroupMember = useBlockGroupMember(group, member);
|
||||
const promoteGroupMember = usePromoteGroupMember(group, member);
|
||||
const demoteGroupMember = useDemoteGroupMember(group, member);
|
||||
|
||||
const account = useAccount(member.account.id) as AccountEntity;
|
||||
|
||||
// Current user role
|
||||
const isCurrentUserAdmin = normalizeRole(group.relationship?.role as any) === BaseGroupRoles.ADMIN;
|
||||
const isCurrentUserModerator = normalizeRole(group.relationship?.role as any) === BaseGroupRoles.MODERATOR;
|
||||
const isCurrentUserOwner = group.relationship?.role === GroupRoles.OWNER;
|
||||
const isCurrentUserAdmin = group.relationship?.role === GroupRoles.ADMIN;
|
||||
|
||||
// Member role
|
||||
const isMemberAdmin = normalizeRole(member.role as any) === BaseGroupRoles.ADMIN;
|
||||
const isMemberModerator = normalizeRole(member.role as any) === BaseGroupRoles.MODERATOR;
|
||||
const isMemberUser = normalizeRole(member.role as any) === BaseGroupRoles.USER;
|
||||
const isMemberOwner = member.role === GroupRoles.OWNER;
|
||||
const isMemberAdmin = member.role === GroupRoles.ADMIN;
|
||||
const isMemberUser = member.role === GroupRoles.USER;
|
||||
|
||||
const handleKickFromGroup = () => {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
|
@ -78,39 +77,41 @@ const GroupMemberListItem = (props: IGroupMemberListItem) => {
|
|||
heading: intl.formatMessage(messages.blockFromGroupHeading),
|
||||
message: intl.formatMessage(messages.blockFromGroupMessage, { name: account.username }),
|
||||
confirm: intl.formatMessage(messages.blockConfirm),
|
||||
onConfirm: () => blockGroupMember({ account_ids: [member.account.id] }).then(() => {
|
||||
dispatch(deleteEntities([member.id], Entities.GROUP_MEMBERSHIPS));
|
||||
toast.success(intl.formatMessage(messages.blocked, { name: account.acct }));
|
||||
}),
|
||||
onConfirm: () => {
|
||||
blockGroupMember({ account_ids: [member.account.id] }, {
|
||||
onSuccess() {
|
||||
dispatch(deleteEntities([member.id], Entities.GROUP_MEMBERSHIPS));
|
||||
toast.success(intl.formatMessage(messages.blocked, { name: account.acct }));
|
||||
},
|
||||
});
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
const onPromote = (role: 'admin' | 'moderator', warning?: boolean) => {
|
||||
if (warning) {
|
||||
return dispatch(openModal('CONFIRM', {
|
||||
message: intl.formatMessage(messages.promoteConfirmMessage, { name: account.username }),
|
||||
confirm: intl.formatMessage(messages.promoteConfirm),
|
||||
onConfirm: () => dispatch(groupPromoteAccount(group.id, account.id, role)).then(() =>
|
||||
toast.success(intl.formatMessage(role === 'admin' ? messages.promotedToAdmin : messages.promotedToMod, { name: account.acct })),
|
||||
),
|
||||
}));
|
||||
} else {
|
||||
return dispatch(groupPromoteAccount(group.id, account.id, role)).then(() =>
|
||||
toast.success(intl.formatMessage(role === 'admin' ? messages.promotedToAdmin : messages.promotedToMod, { name: account.acct })),
|
||||
);
|
||||
}
|
||||
const handleAdminAssignment = () => {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
heading: intl.formatMessage(messages.promoteConfirm),
|
||||
message: intl.formatMessage(messages.promoteConfirmMessage, { name: account.username }),
|
||||
confirm: intl.formatMessage(messages.promoteConfirm),
|
||||
confirmationTheme: 'primary',
|
||||
onConfirm: () => {
|
||||
promoteGroupMember({ role: GroupRoles.ADMIN, account_ids: [account.id] }, {
|
||||
onSuccess() {
|
||||
toast.success(
|
||||
intl.formatMessage(messages.promotedToAdmin, { name: account.acct }),
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
const handlePromoteToGroupAdmin = () => onPromote('admin', true);
|
||||
|
||||
const handlePromoteToGroupMod = () => {
|
||||
onPromote('moderator', group.relationship!.role === 'moderator');
|
||||
};
|
||||
|
||||
const handleDemote = () => {
|
||||
dispatch(groupDemoteAccount(group.id, account.id, 'user')).then(() =>
|
||||
toast.success(intl.formatMessage(messages.demotedToUser, { name: account.acct })),
|
||||
).catch(() => {});
|
||||
const handleUserAssignment = () => {
|
||||
demoteGroupMember({ role: GroupRoles.USER, account_ids: [account.id] }, {
|
||||
onSuccess() {
|
||||
toast.success(intl.formatMessage(messages.demotedToUser, { name: account.acct }));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const menu: IMenu = useMemo(() => {
|
||||
|
@ -120,9 +121,26 @@ const GroupMemberListItem = (props: IGroupMemberListItem) => {
|
|||
return items;
|
||||
}
|
||||
|
||||
if (isCurrentUserOwner) {
|
||||
if (isMemberUser) {
|
||||
items.push({
|
||||
text: intl.formatMessage(messages.groupModPromoteMod, { role: GroupRoles.ADMIN }),
|
||||
icon: require('@tabler/icons/briefcase.svg'),
|
||||
action: handleAdminAssignment,
|
||||
});
|
||||
} else if (isMemberAdmin) {
|
||||
items.push({
|
||||
text: intl.formatMessage(messages.groupModDemote, { role: GroupRoles.ADMIN, name: account.username }),
|
||||
icon: require('@tabler/icons/briefcase.svg'),
|
||||
action: handleUserAssignment,
|
||||
destructive: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(isCurrentUserAdmin || isCurrentUserModerator) &&
|
||||
(isMemberModerator || isMemberUser) &&
|
||||
(isCurrentUserOwner || isCurrentUserAdmin) &&
|
||||
(isMemberAdmin || isMemberUser) &&
|
||||
member.role !== group.relationship.role
|
||||
) {
|
||||
if (features.groupsKick) {
|
||||
|
@ -141,29 +159,6 @@ const GroupMemberListItem = (props: IGroupMemberListItem) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (isCurrentUserAdmin && !isMemberAdmin && account.acct === account.username) {
|
||||
items.push(null);
|
||||
|
||||
if (isMemberModerator) {
|
||||
items.push({
|
||||
text: intl.formatMessage(messages.groupModPromoteAdmin, { name: account.username }),
|
||||
icon: require('@tabler/icons/arrow-up-circle.svg'),
|
||||
action: handlePromoteToGroupAdmin,
|
||||
});
|
||||
items.push({
|
||||
text: intl.formatMessage(messages.groupModDemote, { name: account.username }),
|
||||
icon: require('@tabler/icons/arrow-down-circle.svg'),
|
||||
action: handleDemote,
|
||||
});
|
||||
} else if (isMemberUser) {
|
||||
items.push({
|
||||
text: intl.formatMessage(messages.groupModPromoteMod, { name: account.username }),
|
||||
icon: require('@tabler/icons/arrow-up-circle.svg'),
|
||||
action: handlePromoteToGroupMod,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}, [group, account]);
|
||||
|
||||
|
@ -174,12 +169,12 @@ const GroupMemberListItem = (props: IGroupMemberListItem) => {
|
|||
</div>
|
||||
|
||||
<HStack alignItems='center' space={2}>
|
||||
{(isMemberAdmin || isMemberModerator) ? (
|
||||
{(isMemberOwner || isMemberAdmin) ? (
|
||||
<span
|
||||
className={
|
||||
clsx('inline-flex items-center rounded px-2 py-1 text-xs font-medium capitalize', {
|
||||
'bg-primary-200 text-primary-500': isMemberAdmin,
|
||||
'bg-gray-200 text-gray-900': isMemberModerator,
|
||||
'bg-primary-200 text-primary-500': isMemberOwner,
|
||||
'bg-gray-200 text-gray-900': isMemberAdmin,
|
||||
})
|
||||
}
|
||||
>
|
||||
|
|
|
@ -2,8 +2,8 @@ import React, { useMemo } from 'react';
|
|||
|
||||
import ScrollableList from 'soapbox/components/scrollable-list';
|
||||
import { useGroupMembers } from 'soapbox/hooks/api/useGroupMembers';
|
||||
import { useGroupRoles } from 'soapbox/hooks/useGroupRoles';
|
||||
import { useGroup } from 'soapbox/queries/groups';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||
|
||||
import PlaceholderAccount from '../placeholder/components/placeholder-account';
|
||||
|
||||
|
@ -16,22 +16,20 @@ interface IGroupMembers {
|
|||
}
|
||||
|
||||
const GroupMembers: React.FC<IGroupMembers> = (props) => {
|
||||
const { roles: { admin, moderator, user } } = useGroupRoles();
|
||||
|
||||
const groupId = props.params.id;
|
||||
|
||||
const { group, isFetching: isFetchingGroup } = useGroup(groupId);
|
||||
const { groupMembers: admins, isFetching: isFetchingAdmins } = useGroupMembers(groupId, admin);
|
||||
const { groupMembers: moderators, isFetching: isFetchingModerators } = useGroupMembers(groupId, moderator);
|
||||
const { groupMembers: users, isFetching: isFetchingUsers, fetchNextPage, hasNextPage } = useGroupMembers(groupId, user);
|
||||
const { groupMembers: owners, isFetching: isFetchingOwners } = useGroupMembers(groupId, GroupRoles.OWNER);
|
||||
const { groupMembers: admins, isFetching: isFetchingAdmins } = useGroupMembers(groupId, GroupRoles.ADMIN);
|
||||
const { groupMembers: users, isFetching: isFetchingUsers, fetchNextPage, hasNextPage } = useGroupMembers(groupId, GroupRoles.USER);
|
||||
|
||||
const isLoading = isFetchingGroup || isFetchingAdmins || isFetchingModerators || isFetchingUsers;
|
||||
const isLoading = isFetchingGroup || isFetchingOwners || isFetchingAdmins || isFetchingUsers;
|
||||
|
||||
const members = useMemo(() => [
|
||||
...owners,
|
||||
...admins,
|
||||
...moderators,
|
||||
...users,
|
||||
], [admins, moderators, users]);
|
||||
], [owners, admins, users]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useEntityActions } from 'soapbox/entity-store/hooks';
|
|||
import type { Group, GroupMember } from 'soapbox/schemas';
|
||||
|
||||
function useBlockGroupMember(group: Group, groupMember: GroupMember) {
|
||||
const { createEntity } = useEntityActions(
|
||||
const { createEntity } = useEntityActions<GroupMember>(
|
||||
[Entities.GROUP_MEMBERSHIPS, groupMember.id],
|
||||
{ post: `/api/v1/groups/${group.id}/blocks` },
|
||||
);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import { Entities } from 'soapbox/entity-store/entities';
|
||||
import { useEntityActions } from 'soapbox/entity-store/hooks';
|
||||
import { groupMemberSchema } from 'soapbox/schemas';
|
||||
|
||||
import type { Group, GroupMember } from 'soapbox/schemas';
|
||||
|
||||
function useDemoteGroupMember(group: Group, groupMember: GroupMember) {
|
||||
const { createEntity } = useEntityActions<GroupMember>(
|
||||
[Entities.GROUP_MEMBERSHIPS, groupMember.id],
|
||||
{ post: `/api/v1/groups/${group.id}/demote` },
|
||||
{ schema: z.array(groupMemberSchema).transform((arr) => arr[0]) },
|
||||
);
|
||||
|
||||
return createEntity;
|
||||
}
|
||||
|
||||
export { useDemoteGroupMember };
|
|
@ -0,0 +1,19 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import { Entities } from 'soapbox/entity-store/entities';
|
||||
import { useEntityActions } from 'soapbox/entity-store/hooks';
|
||||
import { groupMemberSchema } from 'soapbox/schemas';
|
||||
|
||||
import type { Group, GroupMember } from 'soapbox/schemas';
|
||||
|
||||
function usePromoteGroupMember(group: Group, groupMember: GroupMember) {
|
||||
const { createEntity } = useEntityActions<GroupMember>(
|
||||
[Entities.GROUP_MEMBERSHIPS, groupMember.id],
|
||||
{ post: `/api/v1/groups/${group.id}/promote` },
|
||||
{ schema: z.array(groupMemberSchema).transform((arr) => arr[0]) },
|
||||
);
|
||||
|
||||
return createEntity;
|
||||
}
|
||||
|
||||
export { usePromoteGroupMember };
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* Groups
|
||||
*/
|
||||
export { useBlockGroupMember } from './groups/useBlockGroupMember';
|
||||
export { useDemoteGroupMember } from './groups/useDemoteGroupMember';
|
||||
export { usePromoteGroupMember } from './groups/usePromoteGroupMember';
|
|
@ -1,51 +0,0 @@
|
|||
import { TRUTHSOCIAL } from 'soapbox/utils/features';
|
||||
|
||||
import { useBackend } from './useBackend';
|
||||
|
||||
enum TruthSocialGroupRoles {
|
||||
ADMIN = 'owner',
|
||||
MODERATOR = 'admin',
|
||||
USER = 'user'
|
||||
}
|
||||
|
||||
enum BaseGroupRoles {
|
||||
ADMIN = 'admin',
|
||||
MODERATOR = 'moderator',
|
||||
USER = 'user'
|
||||
}
|
||||
|
||||
const roleMap = {
|
||||
[TruthSocialGroupRoles.ADMIN]: BaseGroupRoles.ADMIN,
|
||||
[TruthSocialGroupRoles.MODERATOR]: BaseGroupRoles.MODERATOR,
|
||||
[TruthSocialGroupRoles.USER]: BaseGroupRoles.USER,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the correct role name depending on the used backend.
|
||||
*
|
||||
* @returns Object
|
||||
*/
|
||||
const useGroupRoles = () => {
|
||||
const version = useBackend();
|
||||
const isTruthSocial = version.software === TRUTHSOCIAL;
|
||||
const selectedRoles = isTruthSocial ? TruthSocialGroupRoles : BaseGroupRoles;
|
||||
|
||||
const normalizeRole = (role: TruthSocialGroupRoles) => {
|
||||
if (isTruthSocial) {
|
||||
return roleMap[role];
|
||||
}
|
||||
|
||||
return role;
|
||||
};
|
||||
|
||||
return {
|
||||
normalizeRole,
|
||||
roles: {
|
||||
admin: selectedRoles.ADMIN,
|
||||
moderator: selectedRoles.MODERATOR,
|
||||
user: selectedRoles.USER,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export { useGroupRoles, TruthSocialGroupRoles, BaseGroupRoles };
|
|
@ -470,8 +470,8 @@
|
|||
"confirmations.block.confirm": "Block",
|
||||
"confirmations.block.heading": "Block @{name}",
|
||||
"confirmations.block.message": "Are you sure you want to block {name}?",
|
||||
"confirmations.block_from_group.confirm": "Block",
|
||||
"confirmations.block_from_group.heading": "Block group member",
|
||||
"confirmations.block_from_group.confirm": "Ban User",
|
||||
"confirmations.block_from_group.heading": "Ban From Group",
|
||||
"confirmations.block_from_group.message": "Are you sure you want to ban @{name} from the group?",
|
||||
"confirmations.cancel.confirm": "Discard",
|
||||
"confirmations.cancel.heading": "Discard post",
|
||||
|
@ -509,8 +509,6 @@
|
|||
"confirmations.mute.confirm": "Mute",
|
||||
"confirmations.mute.heading": "Mute @{name}",
|
||||
"confirmations.mute.message": "Are you sure you want to mute {name}?",
|
||||
"confirmations.promote_in_group.confirm": "Promote",
|
||||
"confirmations.promote_in_group.message": "Are you sure you want to promote @{name}? You will not be able to demote them.",
|
||||
"confirmations.redraft.confirm": "Delete & redraft",
|
||||
"confirmations.redraft.heading": "Delete & redraft",
|
||||
"confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favorites and reposts will be lost, and replies to the original post will be orphaned.",
|
||||
|
@ -768,18 +766,15 @@
|
|||
"gdpr.title": "{siteTitle} uses cookies",
|
||||
"getting_started.open_source_notice": "{code_name} is open source software. You can contribute or report issues at {code_link} (v{code_version}).",
|
||||
"group.cancel_request": "Cancel Request",
|
||||
"group.demote.user.success": "@{name} is now a member",
|
||||
"group.group_mod_authorize": "Accept",
|
||||
"group.group_mod_authorize.success": "Accepted @{name} to group",
|
||||
"group.group_mod_block": "Ban from group",
|
||||
"group.group_mod_block.success": "You have successfully blocked @{name} from the group",
|
||||
"group.group_mod_demote": "Demote @{name}",
|
||||
"group.group_mod_demote.success": "Demoted @{name} to group user",
|
||||
"group.group_mod_block.success": "@{name} is banned",
|
||||
"group.group_mod_demote": "Remove {role} role",
|
||||
"group.group_mod_kick": "Kick @{name} from group",
|
||||
"group.group_mod_kick.success": "Kicked @{name} from group",
|
||||
"group.group_mod_promote_admin": "Promote @{name} to group administrator",
|
||||
"group.group_mod_promote_admin.success": "Promoted @{name} to group administrator",
|
||||
"group.group_mod_promote_mod": "Promote @{name} to group moderator",
|
||||
"group.group_mod_promote_mod.success": "Promoted @{name} to group moderator",
|
||||
"group.group_mod_promote_mod": "Assign {role} role",
|
||||
"group.group_mod_reject": "Reject",
|
||||
"group.group_mod_reject.success": "Rejected @{name} from group",
|
||||
"group.group_mod_unblock": "Unblock",
|
||||
|
@ -798,6 +793,9 @@
|
|||
"group.privacy.public": "Public",
|
||||
"group.privacy.public.full": "Public Group",
|
||||
"group.privacy.public.info": "Discoverable. Anyone can join.",
|
||||
"group.promote.admin.confirmation.message": "Are you sure you want to assign the admin role to @{name}?",
|
||||
"group.promote.admin.confirmation.title": "Assign Admin Role",
|
||||
"group.promote.admin.success": "@{name} is now an admin",
|
||||
"group.role.admin": "Admin",
|
||||
"group.role.moderator": "Moderator",
|
||||
"group.tabs.all": "All",
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { useApi } from 'soapbox/hooks';
|
||||
import { useGroupRoles } from 'soapbox/hooks/useGroupRoles';
|
||||
import { normalizeAccount } from 'soapbox/normalizers';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||
|
||||
const GroupMemberKeys = {
|
||||
members: (id: string, role: string) => ['group', id, role] as const,
|
||||
};
|
||||
|
||||
const useGroupMembers = (groupId: string, role: ReturnType<typeof useGroupRoles>['roles']['admin']) => {
|
||||
const useGroupMembers = (groupId: string, role: GroupRoles) => {
|
||||
const api = useApi();
|
||||
|
||||
const getQuery = async () => {
|
||||
|
|
|
@ -2,27 +2,18 @@ import z from 'zod';
|
|||
|
||||
import { accountSchema } from './account';
|
||||
|
||||
enum TruthSocialGroupRoles {
|
||||
ADMIN = 'owner',
|
||||
MODERATOR = 'admin',
|
||||
USER = 'user'
|
||||
}
|
||||
|
||||
enum BaseGroupRoles {
|
||||
enum GroupRoles {
|
||||
OWNER = 'owner',
|
||||
ADMIN = 'admin',
|
||||
MODERATOR = 'moderator',
|
||||
USER = 'user'
|
||||
}
|
||||
|
||||
const groupMemberSchema = z.object({
|
||||
id: z.string(),
|
||||
account: accountSchema,
|
||||
role: z.union([
|
||||
z.nativeEnum(TruthSocialGroupRoles),
|
||||
z.nativeEnum(BaseGroupRoles),
|
||||
]),
|
||||
role: z.nativeEnum(GroupRoles),
|
||||
});
|
||||
|
||||
type GroupMember = z.infer<typeof groupMemberSchema>;
|
||||
|
||||
export { groupMemberSchema, GroupMember };
|
||||
export { groupMemberSchema, GroupMember, GroupRoles };
|
|
@ -532,9 +532,14 @@ const getInstanceFeatures = (instance: Instance) => {
|
|||
|
||||
/**
|
||||
* Can query pending Group requests.
|
||||
*/
|
||||
*/
|
||||
groupsPending: v.software === TRUTHSOCIAL,
|
||||
|
||||
/**
|
||||
* Can promote members to Admins.
|
||||
*/
|
||||
groupsPromoteToAdmin: v.software !== TRUTHSOCIAL,
|
||||
|
||||
/**
|
||||
* Can hide follows/followers lists and counts.
|
||||
* @see PATCH /api/v1/accounts/update_credentials
|
||||
|
|
Ładowanie…
Reference in New Issue