diff --git a/app/soapbox/actions/groups.ts b/app/soapbox/actions/groups.ts
index 690b74540..ad760d916 100644
--- a/app/soapbox/actions/groups.ts
+++ b/app/soapbox/actions/groups.ts
@@ -4,7 +4,6 @@ import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts';
import { importFetchedGroups, importFetchedAccounts } from './importer';
-import { deleteFromTimelines } from './timelines';
import type { AxiosError } from 'axios';
import type { GroupRole } from 'soapbox/reducers/group-memberships';
@@ -35,10 +34,6 @@ const GROUP_RELATIONSHIPS_FETCH_REQUEST = 'GROUP_RELATIONSHIPS_FETCH_REQUEST';
const GROUP_RELATIONSHIPS_FETCH_SUCCESS = 'GROUP_RELATIONSHIPS_FETCH_SUCCESS';
const GROUP_RELATIONSHIPS_FETCH_FAIL = 'GROUP_RELATIONSHIPS_FETCH_FAIL';
-const GROUP_DELETE_STATUS_REQUEST = 'GROUP_DELETE_STATUS_REQUEST';
-const GROUP_DELETE_STATUS_SUCCESS = 'GROUP_DELETE_STATUS_SUCCESS';
-const GROUP_DELETE_STATUS_FAIL = 'GROUP_DELETE_STATUS_FAIL';
-
const GROUP_KICK_REQUEST = 'GROUP_KICK_REQUEST';
const GROUP_KICK_SUCCESS = 'GROUP_KICK_SUCCESS';
const GROUP_KICK_FAIL = 'GROUP_KICK_FAIL';
@@ -206,36 +201,6 @@ const fetchGroupRelationshipsFail = (error: AxiosError) => ({
skipNotFound: true,
});
-const groupDeleteStatus = (groupId: string, statusId: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- dispatch(groupDeleteStatusRequest(groupId, statusId));
-
- return api(getState).delete(`/api/v1/groups/${groupId}/statuses/${statusId}`)
- .then(() => {
- dispatch(deleteFromTimelines(statusId));
- dispatch(groupDeleteStatusSuccess(groupId, statusId));
- }).catch(err => dispatch(groupDeleteStatusFail(groupId, statusId, err)));
- };
-
-const groupDeleteStatusRequest = (groupId: string, statusId: string) => ({
- type: GROUP_DELETE_STATUS_REQUEST,
- groupId,
- statusId,
-});
-
-const groupDeleteStatusSuccess = (groupId: string, statusId: string) => ({
- type: GROUP_DELETE_STATUS_SUCCESS,
- groupId,
- statusId,
-});
-
-const groupDeleteStatusFail = (groupId: string, statusId: string, error: AxiosError) => ({
- type: GROUP_DELETE_STATUS_SUCCESS,
- groupId,
- statusId,
- error,
-});
-
const groupKick = (groupId: string, accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(groupKickRequest(groupId, accountId));
@@ -677,9 +642,6 @@ export {
GROUP_RELATIONSHIPS_FETCH_REQUEST,
GROUP_RELATIONSHIPS_FETCH_SUCCESS,
GROUP_RELATIONSHIPS_FETCH_FAIL,
- GROUP_DELETE_STATUS_REQUEST,
- GROUP_DELETE_STATUS_SUCCESS,
- GROUP_DELETE_STATUS_FAIL,
GROUP_KICK_REQUEST,
GROUP_KICK_SUCCESS,
GROUP_KICK_FAIL,
@@ -735,10 +697,6 @@ export {
fetchGroupRelationshipsRequest,
fetchGroupRelationshipsSuccess,
fetchGroupRelationshipsFail,
- groupDeleteStatus,
- groupDeleteStatusRequest,
- groupDeleteStatusSuccess,
- groupDeleteStatusFail,
groupKick,
groupKickRequest,
groupKickSuccess,
diff --git a/app/soapbox/api/hooks/groups/useDeleteGroupStatus.ts b/app/soapbox/api/hooks/groups/useDeleteGroupStatus.ts
new file mode 100644
index 000000000..55a6f9459
--- /dev/null
+++ b/app/soapbox/api/hooks/groups/useDeleteGroupStatus.ts
@@ -0,0 +1,20 @@
+import { Entities } from 'soapbox/entity-store/entities';
+import { useDeleteEntity } from 'soapbox/entity-store/hooks';
+import { useApi } from 'soapbox/hooks';
+
+import type { Group } from 'soapbox/schemas';
+
+function useDeleteGroupStatus(group: Group, statusId: string) {
+ const api = useApi();
+ const { deleteEntity, isSubmitting } = useDeleteEntity(
+ Entities.STATUSES,
+ () => api.delete(`/api/v1/groups/${group.id}/statuses/${statusId}`),
+ );
+
+ return {
+ mutate: deleteEntity,
+ isSubmitting,
+ };
+}
+
+export { useDeleteGroupStatus };
\ No newline at end of file
diff --git a/app/soapbox/components/dropdown-menu/dropdown-menu-item.tsx b/app/soapbox/components/dropdown-menu/dropdown-menu-item.tsx
index 8b0ca7755..8a6c8f531 100644
--- a/app/soapbox/components/dropdown-menu/dropdown-menu-item.tsx
+++ b/app/soapbox/components/dropdown-menu/dropdown-menu-item.tsx
@@ -94,7 +94,7 @@ const DropdownMenuItem = ({ index, item, onClick }: IDropdownMenuItem) => {
>
{item.icon && }
- {item.text}
+ {item.text}
{item.count ? (
diff --git a/app/soapbox/components/status-action-bar.tsx b/app/soapbox/components/status-action-bar.tsx
index 0f4adb552..13e1a4a3c 100644
--- a/app/soapbox/components/status-action-bar.tsx
+++ b/app/soapbox/components/status-action-bar.tsx
@@ -7,18 +7,20 @@ import { blockAccount } from 'soapbox/actions/accounts';
import { launchChat } from 'soapbox/actions/chats';
import { directCompose, mentionCompose, quoteCompose, replyCompose } from 'soapbox/actions/compose';
import { editEvent } from 'soapbox/actions/events';
-import { groupBlock, groupDeleteStatus, groupKick } from 'soapbox/actions/groups';
import { toggleBookmark, toggleDislike, toggleFavourite, togglePin, toggleReblog } from 'soapbox/actions/interactions';
import { openModal } from 'soapbox/actions/modals';
import { deleteStatusModal, toggleStatusSensitivityModal } from 'soapbox/actions/moderation';
import { initMuteModal } from 'soapbox/actions/mutes';
import { initReport, ReportableEntities } from 'soapbox/actions/reports';
import { deleteStatus, editStatus, toggleMuteStatus } from 'soapbox/actions/statuses';
+import { deleteFromTimelines } from 'soapbox/actions/timelines';
+import { useDeleteGroupStatus } from 'soapbox/api/hooks/groups/useDeleteGroupStatus';
import DropdownMenu from 'soapbox/components/dropdown-menu';
import StatusActionButton from 'soapbox/components/status-action-button';
import StatusReactionWrapper from 'soapbox/components/status-reaction-wrapper';
import { HStack } from 'soapbox/components/ui';
import { useAppDispatch, useAppSelector, useFeatures, useOwnAccount, useSettings, useSoapboxConfig } from 'soapbox/hooks';
+import { GroupRoles } from 'soapbox/schemas/group-member';
import toast from 'soapbox/toast';
import { isLocal, isRemote } from 'soapbox/utils/accounts';
import copy from 'soapbox/utils/copy';
@@ -87,16 +89,7 @@ const messages = defineMessages({
blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' },
replies_disabled_group: { id: 'status.disabled_replies.group_membership', defaultMessage: 'Only group members can reply' },
groupModDelete: { id: 'status.group_mod_delete', defaultMessage: 'Delete post from group' },
- groupModKick: { id: 'status.group_mod_kick', defaultMessage: 'Kick @{name} from group' },
- groupModBlock: { id: 'status.group_mod_block', defaultMessage: 'Block @{name} from group' },
- deleteFromGroupHeading: { id: 'confirmations.delete_from_group.heading', defaultMessage: 'Delete from group' },
deleteFromGroupMessage: { id: 'confirmations.delete_from_group.message', defaultMessage: 'Are you sure you want to delete @{name}\'s post?' },
- kickFromGroupHeading: { id: 'confirmations.kick_from_group.heading', defaultMessage: 'Kick group member' },
- kickFromGroupMessage: { id: 'confirmations.kick_from_group.message', defaultMessage: 'Are you sure you want to kick @{name} from this group?' },
- kickFromGroupConfirm: { id: 'confirmations.kick_from_group.confirm', defaultMessage: 'Kick' },
- blockFromGroupHeading: { id: 'confirmations.block_from_group.heading', defaultMessage: 'Block group member' },
- blockFromGroupMessage: { id: 'confirmations.block_from_group.message', defaultMessage: 'Are you sure you want to block @{name} from interacting with this group?' },
- blockFromGroupConfirm: { id: 'confirmations.block_from_group.confirm', defaultMessage: 'Block' },
});
interface IStatusActionBar {
@@ -121,6 +114,7 @@ const StatusActionBar: React.FC = ({
const features = useFeatures();
const settings = useSettings();
const soapboxConfig = useSoapboxConfig();
+ const deleteGroupStatus = useDeleteGroupStatus(status?.group as Group, status.id);
const { allowedEmoji } = soapboxConfig;
@@ -315,29 +309,13 @@ const StatusActionBar: React.FC = ({
heading: intl.formatMessage(messages.deleteHeading),
message: intl.formatMessage(messages.deleteFromGroupMessage, { name: {account.username} }),
confirm: intl.formatMessage(messages.deleteConfirm),
- onConfirm: () => dispatch(groupDeleteStatus((status.group as Group).id, status.id)),
- }));
- };
-
- const handleKickFromGroup: React.EventHandler = () => {
- const account = status.account as Account;
-
- dispatch(openModal('CONFIRM', {
- heading: intl.formatMessage(messages.kickFromGroupHeading),
- message: intl.formatMessage(messages.kickFromGroupMessage, { name: {account.username} }),
- confirm: intl.formatMessage(messages.kickFromGroupConfirm),
- onConfirm: () => dispatch(groupKick((status.group as Group).id, account.id)),
- }));
- };
-
- const handleBlockFromGroup: React.EventHandler = () => {
- const account = status.account as Account;
-
- dispatch(openModal('CONFIRM', {
- heading: intl.formatMessage(messages.blockFromGroupHeading),
- message: intl.formatMessage(messages.blockFromGroupMessage, { name: {account.username} }),
- confirm: intl.formatMessage(messages.blockFromGroupConfirm),
- onConfirm: () => dispatch(groupBlock((status.group as Group).id, account.id)),
+ onConfirm: () => {
+ deleteGroupStatus.mutate(status.id, {
+ onSuccess() {
+ dispatch(deleteFromTimelines(status.id));
+ },
+ });
+ },
}));
};
@@ -362,7 +340,7 @@ const StatusActionBar: React.FC = ({
menu.push({
text: intl.formatMessage(messages.copy),
action: handleCopy,
- icon: require('@tabler/icons/link.svg'),
+ icon: require('@tabler/icons/clipboard-copy.svg'),
});
if (features.embeds && isLocal(account)) {
@@ -466,7 +444,7 @@ const StatusActionBar: React.FC = ({
menu.push({
text: intl.formatMessage(messages.mute, { name: username }),
action: handleMuteClick,
- icon: require('@tabler/icons/circle-x.svg'),
+ icon: require('@tabler/icons/volume-3.svg'),
});
menu.push({
text: intl.formatMessage(messages.block, { name: username }),
@@ -480,23 +458,17 @@ const StatusActionBar: React.FC = ({
});
}
- if (status.group && groupRelationship?.role && ['admin', 'moderator'].includes(groupRelationship.role)) {
+ if (status.group &&
+ groupRelationship?.role &&
+ [GroupRoles.OWNER].includes(groupRelationship.role) &&
+ !ownAccount
+ ) {
menu.push(null);
menu.push({
text: intl.formatMessage(messages.groupModDelete),
action: handleDeleteFromGroup,
icon: require('@tabler/icons/trash.svg'),
- });
- // TODO: figure out when an account is not in the group anymore
- menu.push({
- text: intl.formatMessage(messages.groupModKick, { name: account.get('username') }),
- action: handleKickFromGroup,
- icon: require('@tabler/icons/user-minus.svg'),
- });
- menu.push({
- text: intl.formatMessage(messages.groupModBlock, { name: account.get('username') }),
- action: handleBlockFromGroup,
- icon: require('@tabler/icons/ban.svg'),
+ destructive: true,
});
}
diff --git a/app/soapbox/locales/en.json b/app/soapbox/locales/en.json
index 91fad97e0..a07a6457d 100644
--- a/app/soapbox/locales/en.json
+++ b/app/soapbox/locales/en.json
@@ -488,7 +488,6 @@
"confirmations.delete_event.confirm": "Delete",
"confirmations.delete_event.heading": "Delete event",
"confirmations.delete_event.message": "Are you sure you want to delete this event?",
- "confirmations.delete_from_group.heading": "Delete from group",
"confirmations.delete_from_group.message": "Are you sure you want to delete @{name}'s post?",
"confirmations.delete_group.confirm": "Delete",
"confirmations.delete_group.heading": "Delete Group",
@@ -500,7 +499,6 @@
"confirmations.domain_block.heading": "Block {domain}",
"confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications.",
"confirmations.kick_from_group.confirm": "Kick",
- "confirmations.kick_from_group.heading": "Kick group member",
"confirmations.kick_from_group.message": "Are you sure you want to kick @{name} from this group?",
"confirmations.leave_event.confirm": "Leave event",
"confirmations.leave_event.message": "If you want to rejoin the event, the request will be manually reviewed again. Are you sure you want to proceed?",
@@ -1440,7 +1438,7 @@
"status.cancel_reblog_private": "Un-repost",
"status.cannot_reblog": "This post cannot be reposted",
"status.chat": "Chat with @{name}",
- "status.copy": "Copy link to post",
+ "status.copy": "Copy Link to Post",
"status.delete": "Delete",
"status.detailed_status": "Detailed conversation view",
"status.direct": "Direct message @{name}",
@@ -1452,9 +1450,7 @@
"status.favourite": "Like",
"status.filtered": "Filtered",
"status.group": "Posted in {group}",
- "status.group_mod_block": "Block @{name} from group",
"status.group_mod_delete": "Delete post from group",
- "status.group_mod_kick": "Kick @{name} from group",
"status.interactions.dislikes": "{count, plural, one {Dislike} other {Dislikes}}",
"status.interactions.favourites": "{count, plural, one {Like} other {Likes}}",
"status.interactions.quotes": "{count, plural, one {Quote} other {Quotes}}",
@@ -1462,8 +1458,8 @@
"status.load_more": "Load more",
"status.mention": "Mention @{name}",
"status.more": "More",
- "status.mute_conversation": "Mute conversation",
- "status.open": "Expand this post",
+ "status.mute_conversation": "Mute Conversation",
+ "status.open": "Show Post Details",
"status.pin": "Pin on profile",
"status.pinned": "Pinned post",
"status.quote": "Quote post",
@@ -1499,7 +1495,7 @@
"status.translated_from_with": "Translated from {lang} using {provider}",
"status.unbookmark": "Remove bookmark",
"status.unbookmarked": "Bookmark removed.",
- "status.unmute_conversation": "Unmute conversation",
+ "status.unmute_conversation": "Unmute Conversation",
"status.unpin": "Unpin from profile",
"status_list.queue_label": "Click to see {count} new {count, plural, one {post} other {posts}}",
"statuses.quote_tombstone": "Post is unavailable.",