Add hook to delete Group
This commit is contained in:
parent
be7a462fc4
commit
ad98bf45cc
|
@ -30,7 +30,7 @@ interface EntityActionEndpoints {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EntityCallbacks<TEntity extends Entity = Entity> {
|
interface EntityCallbacks<TEntity extends Entity = Entity> {
|
||||||
onSuccess?(entity: TEntity): void
|
onSuccess?(entity?: TEntity): void
|
||||||
}
|
}
|
||||||
|
|
||||||
function useEntityActions<TEntity extends Entity = Entity, P = any>(
|
function useEntityActions<TEntity extends Entity = Entity, P = any>(
|
||||||
|
@ -70,14 +70,20 @@ function useEntityActions<TEntity extends Entity = Entity, P = any>(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteEntity(entityId: string): Promise<DeleteEntityResult> {
|
function deleteEntity(entityId: string, callbacks: EntityCallbacks = {}): Promise<DeleteEntityResult> {
|
||||||
if (!endpoints.delete) return Promise.reject(endpoints);
|
if (!endpoints.delete) return Promise.reject(endpoints);
|
||||||
// Get the entity before deleting, so we can reverse the action if the API request fails.
|
// Get the entity before deleting, so we can reverse the action if the API request fails.
|
||||||
const entity = getState().entities[entityType]?.store[entityId];
|
const entity = getState().entities[entityType]?.store[entityId];
|
||||||
// Optimistically delete the entity from the _store_ but keep the lists in tact.
|
// Optimistically delete the entity from the _store_ but keep the lists in tact.
|
||||||
dispatch(deleteEntities([entityId], entityType, { preserveLists: true }));
|
dispatch(deleteEntities([entityId], entityType, { preserveLists: true }));
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
return api.delete(endpoints.delete.replaceAll(':id', entityId)).then((response) => {
|
return api.delete(endpoints.delete.replaceAll(':id', entityId)).then((response) => {
|
||||||
|
if (callbacks.onSuccess) {
|
||||||
|
callbacks.onSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
// Success - finish deleting entity from the state.
|
// Success - finish deleting entity from the state.
|
||||||
dispatch(deleteEntities([entityId], entityType));
|
dispatch(deleteEntities([entityId], entityType));
|
||||||
|
|
||||||
|
@ -90,12 +96,14 @@ function useEntityActions<TEntity extends Entity = Entity, P = any>(
|
||||||
dispatch(importEntities([entity], entityType));
|
dispatch(importEntities([entity], entityType));
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
|
}).finally(() => {
|
||||||
|
setIsLoading(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createEntity: createEntity,
|
createEntity,
|
||||||
deleteEntity: endpoints.delete ? deleteEntity : undefined,
|
deleteEntity,
|
||||||
isLoading,
|
isLoading,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,10 @@ import { deleteEntities } from 'soapbox/entity-store/actions';
|
||||||
import { Entities } from 'soapbox/entity-store/entities';
|
import { Entities } from 'soapbox/entity-store/entities';
|
||||||
import { useAppDispatch } from 'soapbox/hooks';
|
import { useAppDispatch } from 'soapbox/hooks';
|
||||||
import { useCancelMembershipRequest, useJoinGroup, useLeaveGroup } from 'soapbox/hooks/api';
|
import { useCancelMembershipRequest, useJoinGroup, useLeaveGroup } from 'soapbox/hooks/api';
|
||||||
|
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||||
import toast from 'soapbox/toast';
|
import toast from 'soapbox/toast';
|
||||||
import { Group } from 'soapbox/types/entities';
|
|
||||||
|
import type { Group } from 'soapbox/types/entities';
|
||||||
|
|
||||||
interface IGroupActionButton {
|
interface IGroupActionButton {
|
||||||
group: Group
|
group: Group
|
||||||
|
@ -33,7 +35,7 @@ const GroupActionButton = ({ group }: IGroupActionButton) => {
|
||||||
|
|
||||||
const isRequested = group.relationship?.requested;
|
const isRequested = group.relationship?.requested;
|
||||||
const isNonMember = !group.relationship?.member && !isRequested;
|
const isNonMember = !group.relationship?.member && !isRequested;
|
||||||
const isAdmin = group.relationship?.role === 'owner';
|
const isOwner = group.relationship?.role === GroupRoles.OWNER;
|
||||||
const isBlocked = group.relationship?.blocked_by;
|
const isBlocked = group.relationship?.blocked_by;
|
||||||
|
|
||||||
const onJoinGroup = () => joinGroup.mutate({}, {
|
const onJoinGroup = () => joinGroup.mutate({}, {
|
||||||
|
@ -68,6 +70,17 @@ const GroupActionButton = ({ group }: IGroupActionButton) => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isOwner) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
theme='secondary'
|
||||||
|
to={`/groups/${group.id}/manage`}
|
||||||
|
>
|
||||||
|
<FormattedMessage id='group.manage' defaultMessage='Manage Group' />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isNonMember) {
|
if (isNonMember) {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
@ -94,17 +107,6 @@ const GroupActionButton = ({ group }: IGroupActionButton) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAdmin) {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
theme='secondary'
|
|
||||||
to={`/groups/${group.id}/manage`}
|
|
||||||
>
|
|
||||||
<FormattedMessage id='group.manage' defaultMessage='Manage Group' />
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
theme='secondary'
|
theme='secondary'
|
||||||
|
|
|
@ -2,11 +2,14 @@ import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { deleteGroup, editGroup } from 'soapbox/actions/groups';
|
import { editGroup } from 'soapbox/actions/groups';
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
import List, { ListItem } from 'soapbox/components/list';
|
import List, { ListItem } from 'soapbox/components/list';
|
||||||
import { CardBody, CardHeader, CardTitle, Column, Spinner, Text } from 'soapbox/components/ui';
|
import { CardBody, CardHeader, CardTitle, Column, Spinner, Text } from 'soapbox/components/ui';
|
||||||
import { useAppDispatch, useGroup } from 'soapbox/hooks';
|
import { useAppDispatch, useGroup, useGroupsPath } from 'soapbox/hooks';
|
||||||
|
import { useDeleteGroup } from 'soapbox/hooks/api';
|
||||||
|
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||||
|
import toast from 'soapbox/toast';
|
||||||
|
|
||||||
import ColumnForbidden from '../ui/components/column-forbidden';
|
import ColumnForbidden from '../ui/components/column-forbidden';
|
||||||
|
|
||||||
|
@ -23,6 +26,7 @@ const messages = defineMessages({
|
||||||
deleteMessage: { id: 'confirmations.delete_group.message', defaultMessage: 'Are you sure you want to delete this group? This is a permanent action that cannot be undone.' },
|
deleteMessage: { id: 'confirmations.delete_group.message', defaultMessage: 'Are you sure you want to delete this group? This is a permanent action that cannot be undone.' },
|
||||||
members: { id: 'group.tabs.members', defaultMessage: 'Members' },
|
members: { id: 'group.tabs.members', defaultMessage: 'Members' },
|
||||||
other: { id: 'settings.other', defaultMessage: 'Other options' },
|
other: { id: 'settings.other', defaultMessage: 'Other options' },
|
||||||
|
deleteSuccess: { id: 'group.delete.success', defaultMessage: 'Group successfully deleted' },
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IManageGroup {
|
interface IManageGroup {
|
||||||
|
@ -34,9 +38,14 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const groupsPath = useGroupsPath();
|
||||||
|
|
||||||
const { group } = useGroup(id);
|
const { group } = useGroup(id);
|
||||||
|
|
||||||
|
const deleteGroup = useDeleteGroup();
|
||||||
|
|
||||||
|
const isOwner = group?.relationship?.role === GroupRoles.OWNER;
|
||||||
|
|
||||||
if (!group || !group.relationship) {
|
if (!group || !group.relationship) {
|
||||||
return (
|
return (
|
||||||
<Column label={intl.formatMessage(messages.heading)}>
|
<Column label={intl.formatMessage(messages.heading)}>
|
||||||
|
@ -58,7 +67,14 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
||||||
heading: intl.formatMessage(messages.deleteHeading),
|
heading: intl.formatMessage(messages.deleteHeading),
|
||||||
message: intl.formatMessage(messages.deleteMessage),
|
message: intl.formatMessage(messages.deleteMessage),
|
||||||
confirm: intl.formatMessage(messages.deleteConfirm),
|
confirm: intl.formatMessage(messages.deleteConfirm),
|
||||||
onConfirm: () => dispatch(deleteGroup(id)),
|
onConfirm: () => {
|
||||||
|
deleteGroup.mutate(group.id, {
|
||||||
|
onSuccess() {
|
||||||
|
toast.success(intl.formatMessage(messages.deleteSuccess));
|
||||||
|
history.push(groupsPath);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const navigateToPending = () => history.push(`/groups/${id}/manage/requests`);
|
const navigateToPending = () => history.push(`/groups/${id}/manage/requests`);
|
||||||
|
@ -67,7 +83,7 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
||||||
return (
|
return (
|
||||||
<Column label={intl.formatMessage(messages.heading)} backHref={`/groups/${id}`}>
|
<Column label={intl.formatMessage(messages.heading)} backHref={`/groups/${id}`}>
|
||||||
<CardBody className='space-y-4'>
|
<CardBody className='space-y-4'>
|
||||||
{group.relationship.role === 'owner' && (
|
{isOwner && (
|
||||||
<>
|
<>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle title={intl.formatMessage(messages.editGroup)} />
|
<CardTitle title={intl.formatMessage(messages.editGroup)} />
|
||||||
|
@ -90,7 +106,7 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
||||||
<ListItem label={intl.formatMessage(messages.blockedMembers)} onClick={navigateToBlocks} />
|
<ListItem label={intl.formatMessage(messages.blockedMembers)} onClick={navigateToBlocks} />
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
{group.relationship.role === 'owner' && (
|
{isOwner && (
|
||||||
<>
|
<>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle title={intl.formatMessage(messages.other)} />
|
<CardTitle title={intl.formatMessage(messages.other)} />
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Entities } from 'soapbox/entity-store/entities';
|
||||||
|
import { useEntityActions } from 'soapbox/entity-store/hooks';
|
||||||
|
|
||||||
|
import type { Group } from 'soapbox/schemas';
|
||||||
|
|
||||||
|
function useDeleteGroup() {
|
||||||
|
const { deleteEntity, isLoading } = useEntityActions<Group>(
|
||||||
|
[Entities.GROUPS],
|
||||||
|
{ delete: '/api/v1/groups/:id' },
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
mutate: deleteEntity,
|
||||||
|
isLoading,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useDeleteGroup };
|
|
@ -3,6 +3,7 @@
|
||||||
*/
|
*/
|
||||||
export { useBlockGroupMember } from './groups/useBlockGroupMember';
|
export { useBlockGroupMember } from './groups/useBlockGroupMember';
|
||||||
export { useCancelMembershipRequest } from './groups/useCancelMembershipRequest';
|
export { useCancelMembershipRequest } from './groups/useCancelMembershipRequest';
|
||||||
|
export { useDeleteGroup } from './groups/useDeleteGroup';
|
||||||
export { useDemoteGroupMember } from './groups/useDemoteGroupMember';
|
export { useDemoteGroupMember } from './groups/useDemoteGroupMember';
|
||||||
export { useJoinGroup } from './groups/useJoinGroup';
|
export { useJoinGroup } from './groups/useJoinGroup';
|
||||||
export { useLeaveGroup } from './groups/useLeaveGroup';
|
export { useLeaveGroup } from './groups/useLeaveGroup';
|
||||||
|
|
Loading…
Reference in New Issue