Remove Truth Social-specific features
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
@ -3,7 +3,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { fetchGroupRelationshipsSuccess } from 'soapbox/actions/groups';
|
||||
import { openModal } from 'soapbox/actions/modals';
|
||||
import { useCancelMembershipRequest, useJoinGroup, useLeaveGroup, usePendingGroups } from 'soapbox/api/hooks';
|
||||
import { useCancelMembershipRequest, useJoinGroup, useLeaveGroup } from 'soapbox/api/hooks';
|
||||
import { Button } from 'soapbox/components/ui';
|
||||
import { importEntities } from 'soapbox/entity-store/actions';
|
||||
import { Entities } from 'soapbox/entity-store/entities';
|
||||
@ -33,7 +33,6 @@ const GroupActionButton = ({ group }: IGroupActionButton) => {
|
||||
const joinGroup = useJoinGroup(group);
|
||||
const leaveGroup = useLeaveGroup(group);
|
||||
const cancelRequest = useCancelMembershipRequest(group);
|
||||
const { invalidate: invalidatePendingGroups } = usePendingGroups();
|
||||
|
||||
const isRequested = group.relationship?.requested;
|
||||
const isNonMember = !group.relationship?.member && !isRequested;
|
||||
@ -44,7 +43,6 @@ const GroupActionButton = ({ group }: IGroupActionButton) => {
|
||||
const onJoinGroup = () => joinGroup.mutate({}, {
|
||||
onSuccess(entity) {
|
||||
joinGroup.invalidate();
|
||||
invalidatePendingGroups();
|
||||
dispatch(fetchGroupRelationshipsSuccess([entity]));
|
||||
|
||||
toast.success(
|
||||
@ -82,7 +80,6 @@ const GroupActionButton = ({ group }: IGroupActionButton) => {
|
||||
requested: false,
|
||||
};
|
||||
dispatch(importEntities([entity], Entities.GROUP_RELATIONSHIPS));
|
||||
invalidatePendingGroups();
|
||||
},
|
||||
});
|
||||
|
||||
@ -94,7 +91,7 @@ const GroupActionButton = ({ group }: IGroupActionButton) => {
|
||||
return (
|
||||
<Button
|
||||
theme='secondary'
|
||||
to={`/group/${group.slug}/manage`}
|
||||
to={`/group/${group.id}/manage`}
|
||||
>
|
||||
<FormattedMessage id='group.manage' defaultMessage='Manage Group' />
|
||||
</Button>
|
||||
|
||||
@ -12,7 +12,7 @@ interface IGroupMemberCount {
|
||||
|
||||
const GroupMemberCount = ({ group }: IGroupMemberCount) => {
|
||||
return (
|
||||
<Link to={`/group/${group.slug}/members`} className='hover:underline'>
|
||||
<Link to={`/group/${group.id}/members`} className='hover:underline'>
|
||||
<Text theme='inherit' tag='span' size='sm' weight='medium' data-testid='group-member-count'>
|
||||
{shortNumberFormat(group.members_count)}
|
||||
{' '}
|
||||
|
||||
@ -27,7 +27,7 @@ describe('<GroupMemberListItem />', () => {
|
||||
relationship: buildGroupRelationship(),
|
||||
});
|
||||
|
||||
render(<GroupMemberListItem group={group} member={groupMember} canPromoteToAdmin />);
|
||||
render(<GroupMemberListItem group={group} member={groupMember} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('group-member-list-item')).toHaveTextContent(groupMember.account.display_name);
|
||||
@ -52,7 +52,7 @@ describe('<GroupMemberListItem />', () => {
|
||||
});
|
||||
|
||||
it('should render the correct badge', async () => {
|
||||
render(<GroupMemberListItem group={group} member={groupMember} canPromoteToAdmin />);
|
||||
render(<GroupMemberListItem group={group} member={groupMember} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('role-badge')).toHaveTextContent('owner');
|
||||
@ -73,7 +73,7 @@ describe('<GroupMemberListItem />', () => {
|
||||
});
|
||||
|
||||
it('should render the correct badge', async () => {
|
||||
render(<GroupMemberListItem group={group} member={groupMember} canPromoteToAdmin />);
|
||||
render(<GroupMemberListItem group={group} member={groupMember} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('role-badge')).toHaveTextContent('admin');
|
||||
@ -94,7 +94,7 @@ describe('<GroupMemberListItem />', () => {
|
||||
});
|
||||
|
||||
it('should render no correct badge', async () => {
|
||||
render(<GroupMemberListItem group={group} member={groupMember} canPromoteToAdmin />);
|
||||
render(<GroupMemberListItem group={group} member={groupMember} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryAllByTestId('role-badge')).toHaveLength(0);
|
||||
@ -125,36 +125,19 @@ describe('<GroupMemberListItem />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when "canPromoteToAdmin is true', () => {
|
||||
it('should render dropdown with correct Owner actions', async () => {
|
||||
const user = userEvent.setup();
|
||||
it('should render dropdown with correct Owner actions', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<GroupMemberListItem group={group} member={groupMember} canPromoteToAdmin />);
|
||||
render(<GroupMemberListItem group={group} member={groupMember} />);
|
||||
|
||||
await waitFor(async() => {
|
||||
await user.click(screen.getByTestId('icon-button'));
|
||||
});
|
||||
|
||||
const dropdownMenu = screen.getByTestId('dropdown-menu');
|
||||
expect(dropdownMenu).toHaveTextContent('Assign admin role');
|
||||
expect(dropdownMenu).toHaveTextContent('Kick @tiger from group');
|
||||
expect(dropdownMenu).toHaveTextContent('Ban from group');
|
||||
await waitFor(async() => {
|
||||
await user.click(screen.getByTestId('icon-button'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('when "canPromoteToAdmin is false', () => {
|
||||
it('should prevent promoting user to Admin', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<GroupMemberListItem group={group} member={groupMember} canPromoteToAdmin={false} />);
|
||||
|
||||
await waitFor(async() => {
|
||||
await user.click(screen.getByTestId('icon-button'));
|
||||
await user.click(screen.getByTitle('Assign admin role'));
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('toast')).toHaveTextContent('Admin limit reached');
|
||||
});
|
||||
const dropdownMenu = screen.getByTestId('dropdown-menu');
|
||||
expect(dropdownMenu).toHaveTextContent('Assign admin role');
|
||||
expect(dropdownMenu).toHaveTextContent('Kick @tiger from group');
|
||||
expect(dropdownMenu).toHaveTextContent('Ban from group');
|
||||
});
|
||||
});
|
||||
|
||||
@ -180,7 +163,7 @@ describe('<GroupMemberListItem />', () => {
|
||||
it('should render dropdown with correct Owner actions', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<GroupMemberListItem group={group} member={groupMember} canPromoteToAdmin />);
|
||||
render(<GroupMemberListItem group={group} member={groupMember} />);
|
||||
|
||||
await waitFor(async() => {
|
||||
await user.click(screen.getByTestId('icon-button'));
|
||||
@ -219,7 +202,7 @@ describe('<GroupMemberListItem />', () => {
|
||||
it('should render dropdown with correct Admin actions', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<GroupMemberListItem group={group} member={groupMember} canPromoteToAdmin />);
|
||||
render(<GroupMemberListItem group={group} member={groupMember} />);
|
||||
|
||||
await waitFor(async() => {
|
||||
await user.click(screen.getByTestId('icon-button'));
|
||||
@ -252,7 +235,7 @@ describe('<GroupMemberListItem />', () => {
|
||||
});
|
||||
|
||||
it('should not render the dropdown', async () => {
|
||||
render(<GroupMemberListItem group={group} member={groupMember} canPromoteToAdmin />);
|
||||
render(<GroupMemberListItem group={group} member={groupMember} />);
|
||||
|
||||
await waitFor(async() => {
|
||||
expect(screen.queryAllByTestId('icon-button')).toHaveLength(0);
|
||||
@ -280,7 +263,7 @@ describe('<GroupMemberListItem />', () => {
|
||||
});
|
||||
|
||||
it('should not render the dropdown', async () => {
|
||||
render(<GroupMemberListItem group={group} member={groupMember} canPromoteToAdmin />);
|
||||
render(<GroupMemberListItem group={group} member={groupMember} />);
|
||||
|
||||
await waitFor(async() => {
|
||||
expect(screen.queryAllByTestId('icon-button')).toHaveLength(0);
|
||||
@ -310,7 +293,7 @@ describe('<GroupMemberListItem />', () => {
|
||||
});
|
||||
|
||||
it('should not render the dropdown', async () => {
|
||||
render(<GroupMemberListItem group={group} member={groupMember} canPromoteToAdmin />);
|
||||
render(<GroupMemberListItem group={group} member={groupMember} />);
|
||||
|
||||
await waitFor(async() => {
|
||||
expect(screen.queryAllByTestId('icon-button')).toHaveLength(0);
|
||||
|
||||
@ -11,12 +11,10 @@ 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 { useAppDispatch } from 'soapbox/hooks';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||
import toast from 'soapbox/toast';
|
||||
|
||||
import { MAX_ADMIN_COUNT } from '../group-members';
|
||||
|
||||
import type { Menu as IMenu } from 'soapbox/components/dropdown-menu';
|
||||
import type { Group, GroupMember } from 'soapbox/types/entities';
|
||||
|
||||
@ -43,14 +41,12 @@ const messages = defineMessages({
|
||||
interface IGroupMemberListItem {
|
||||
member: GroupMember;
|
||||
group: Group;
|
||||
canPromoteToAdmin: boolean;
|
||||
}
|
||||
|
||||
const GroupMemberListItem = (props: IGroupMemberListItem) => {
|
||||
const { canPromoteToAdmin, member, group } = props;
|
||||
const { member, group } = props;
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const features = useFeatures();
|
||||
const intl = useIntl();
|
||||
|
||||
const blockGroupMember = useBlockGroupMember(group, member.account);
|
||||
@ -95,13 +91,6 @@ const GroupMemberListItem = (props: IGroupMemberListItem) => {
|
||||
};
|
||||
|
||||
const handleAdminAssignment = () => {
|
||||
if (!canPromoteToAdmin) {
|
||||
toast.error(intl.formatMessage(messages.adminLimitTitle), {
|
||||
summary: intl.formatMessage(messages.adminLimitSummary, { count: MAX_ADMIN_COUNT }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(openModal('CONFIRM', {
|
||||
heading: intl.formatMessage(messages.promoteConfirm),
|
||||
message: intl.formatMessage(messages.promoteConfirmMessage, { name: account?.username }),
|
||||
@ -156,13 +145,11 @@ const GroupMemberListItem = (props: IGroupMemberListItem) => {
|
||||
(isMemberAdmin || isMemberUser) &&
|
||||
member.role !== group.relationship.role
|
||||
) {
|
||||
if (features.groupsKick) {
|
||||
items.push({
|
||||
text: intl.formatMessage(messages.groupModKick, { name: account.username }),
|
||||
icon: require('@tabler/icons/outline/user-minus.svg'),
|
||||
action: handleKickFromGroup,
|
||||
});
|
||||
}
|
||||
items.push({
|
||||
text: intl.formatMessage(messages.groupModKick, { name: account.username }),
|
||||
icon: require('@tabler/icons/outline/user-minus.svg'),
|
||||
action: handleKickFromGroup,
|
||||
});
|
||||
|
||||
items.push({
|
||||
text: intl.formatMessage(messages.groupModBlock, { name: account.username }),
|
||||
|
||||
@ -2,29 +2,21 @@ import React, { useMemo } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { openModal } from 'soapbox/actions/modals';
|
||||
import { initReport, ReportableEntities } from 'soapbox/actions/reports';
|
||||
import { useLeaveGroup, useMuteGroup, useUnmuteGroup } from 'soapbox/api/hooks';
|
||||
import { useLeaveGroup } from 'soapbox/api/hooks';
|
||||
import DropdownMenu, { Menu } from 'soapbox/components/dropdown-menu';
|
||||
import { IconButton } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useOwnAccount } from 'soapbox/hooks';
|
||||
import { useAppDispatch } from 'soapbox/hooks';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||
import toast from 'soapbox/toast';
|
||||
|
||||
import type { Account, Group } from 'soapbox/types/entities';
|
||||
import type { Group } from 'soapbox/types/entities';
|
||||
|
||||
const messages = defineMessages({
|
||||
confirmationConfirm: { id: 'confirmations.leave_group.confirm', defaultMessage: 'Leave' },
|
||||
confirmationHeading: { id: 'confirmations.leave_group.heading', defaultMessage: 'Leave group' },
|
||||
confirmationMessage: { id: 'confirmations.leave_group.message', defaultMessage: 'You are about to leave the group. Do you want to continue?' },
|
||||
muteConfirm: { id: 'confirmations.mute_group.confirm', defaultMessage: 'Mute' },
|
||||
muteHeading: { id: 'confirmations.mute_group.heading', defaultMessage: 'Mute Group' },
|
||||
muteMessage: { id: 'confirmations.mute_group.message', defaultMessage: 'You are about to mute the group. Do you want to continue?' },
|
||||
muteSuccess: { id: 'group.mute.success', defaultMessage: 'Muted the group' },
|
||||
unmuteSuccess: { id: 'group.unmute.success', defaultMessage: 'Unmuted the group' },
|
||||
leave: { id: 'group.leave.label', defaultMessage: 'Leave' },
|
||||
leaveSuccess: { id: 'group.leave.success', defaultMessage: 'Left the group' },
|
||||
mute: { id: 'group.mute.label', defaultMessage: 'Mute' },
|
||||
unmute: { id: 'group.unmute.label', defaultMessage: 'Unmute' },
|
||||
report: { id: 'group.report.label', defaultMessage: 'Report' },
|
||||
share: { id: 'group.share.label', defaultMessage: 'Share' },
|
||||
});
|
||||
@ -34,12 +26,9 @@ interface IGroupActionButton {
|
||||
}
|
||||
|
||||
const GroupOptionsButton = ({ group }: IGroupActionButton) => {
|
||||
const { account } = useOwnAccount();
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const muteGroup = useMuteGroup(group);
|
||||
const unmuteGroup = useUnmuteGroup(group);
|
||||
const leaveGroup = useLeaveGroup(group);
|
||||
|
||||
const isMember = group.relationship?.role === GroupRoles.USER;
|
||||
@ -57,27 +46,6 @@ const GroupOptionsButton = ({ group }: IGroupActionButton) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleMute = () =>
|
||||
dispatch(openModal('CONFIRM', {
|
||||
heading: intl.formatMessage(messages.muteHeading),
|
||||
message: intl.formatMessage(messages.muteMessage),
|
||||
confirm: intl.formatMessage(messages.muteConfirm),
|
||||
confirmationTheme: 'primary',
|
||||
onConfirm: () => muteGroup.mutate(undefined, {
|
||||
onSuccess() {
|
||||
toast.success(intl.formatMessage(messages.muteSuccess));
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
const handleUnmute = () => {
|
||||
unmuteGroup.mutate(undefined, {
|
||||
onSuccess() {
|
||||
toast.success(intl.formatMessage(messages.unmuteSuccess));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleLeave = () =>
|
||||
dispatch(openModal('CONFIRM', {
|
||||
heading: intl.formatMessage(messages.confirmationHeading),
|
||||
@ -103,22 +71,6 @@ const GroupOptionsButton = ({ group }: IGroupActionButton) => {
|
||||
});
|
||||
}
|
||||
|
||||
if (isInGroup) {
|
||||
items.push({
|
||||
text: isMuting ? intl.formatMessage(messages.unmute) : intl.formatMessage(messages.mute),
|
||||
icon: require('@tabler/icons/outline/volume-3.svg'),
|
||||
action: isMuting ? handleUnmute : handleMute,
|
||||
});
|
||||
}
|
||||
|
||||
if (isMember || isAdmin) {
|
||||
items.push({
|
||||
text: intl.formatMessage(messages.report),
|
||||
icon: require('@tabler/icons/outline/flag.svg'),
|
||||
action: () => dispatch(initReport(ReportableEntities.GROUP, account as Account, { group })),
|
||||
});
|
||||
}
|
||||
|
||||
if (isAdmin) {
|
||||
items.push(null);
|
||||
items.push({
|
||||
|
||||
@ -1,124 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { buildGroup, buildGroupTag, buildGroupRelationship } from 'soapbox/jest/factory';
|
||||
import { render, screen } from 'soapbox/jest/test-helpers';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||
|
||||
import GroupTagListItem from './group-tag-list-item';
|
||||
|
||||
describe('<GroupTagListItem />', () => {
|
||||
describe('tag name', () => {
|
||||
const name = 'hello';
|
||||
|
||||
it('should render the tag name', () => {
|
||||
const group = buildGroup();
|
||||
const tag = buildGroupTag({ name });
|
||||
render(<GroupTagListItem group={group} tag={tag} isPinnable />);
|
||||
|
||||
expect(screen.getByTestId('group-tag-list-item')).toHaveTextContent(`#${name}`);
|
||||
});
|
||||
|
||||
describe('when the tag is "visible"', () => {
|
||||
const group = buildGroup();
|
||||
const tag = buildGroupTag({ name, visible: true });
|
||||
|
||||
it('renders the default name', () => {
|
||||
render(<GroupTagListItem group={group} tag={tag} isPinnable />);
|
||||
expect(screen.getByTestId('group-tag-name')).toHaveClass('text-gray-900');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the tag is not "visible" and user is Owner', () => {
|
||||
const group = buildGroup({
|
||||
relationship: buildGroupRelationship({
|
||||
role: GroupRoles.OWNER,
|
||||
member: true,
|
||||
}),
|
||||
});
|
||||
const tag = buildGroupTag({
|
||||
name,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
it('renders the subtle name', () => {
|
||||
render(<GroupTagListItem group={group} tag={tag} isPinnable />);
|
||||
expect(screen.getByTestId('group-tag-name')).toHaveClass('text-gray-400');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the tag is not "visible" and user is Admin or User', () => {
|
||||
const group = buildGroup({
|
||||
relationship: buildGroupRelationship({
|
||||
role: GroupRoles.ADMIN,
|
||||
member: true,
|
||||
}),
|
||||
});
|
||||
const tag = buildGroupTag({
|
||||
name,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
it('renders the subtle name', () => {
|
||||
render(<GroupTagListItem group={group} tag={tag} isPinnable />);
|
||||
expect(screen.getByTestId('group-tag-name')).toHaveClass('text-gray-900');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('pinning', () => {
|
||||
describe('as an owner', () => {
|
||||
const group = buildGroup({
|
||||
relationship: buildGroupRelationship({
|
||||
role: GroupRoles.OWNER,
|
||||
member: true,
|
||||
}),
|
||||
});
|
||||
|
||||
describe('when the tag is visible', () => {
|
||||
const tag = buildGroupTag({ visible: true });
|
||||
|
||||
it('renders the pin icon', () => {
|
||||
render(<GroupTagListItem group={group} tag={tag} isPinnable />);
|
||||
expect(screen.getByTestId('pin-icon')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the tag is not visible', () => {
|
||||
const tag = buildGroupTag({ visible: false });
|
||||
|
||||
it('does not render the pin icon', () => {
|
||||
render(<GroupTagListItem group={group} tag={tag} isPinnable />);
|
||||
expect(screen.queryAllByTestId('pin-icon')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('as a non-owner', () => {
|
||||
const group = buildGroup({
|
||||
relationship: buildGroupRelationship({
|
||||
role: GroupRoles.ADMIN,
|
||||
member: true,
|
||||
}),
|
||||
});
|
||||
|
||||
describe('when the tag is pinned', () => {
|
||||
const tag = buildGroupTag({ pinned: true, visible: true });
|
||||
|
||||
it('does render the pin icon', () => {
|
||||
render(<GroupTagListItem group={group} tag={tag} isPinnable />);
|
||||
screen.debug();
|
||||
expect(screen.queryAllByTestId('pin-icon')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the tag is not pinned', () => {
|
||||
const tag = buildGroupTag({ pinned: false, visible: true });
|
||||
|
||||
it('does not render the pin icon', () => {
|
||||
render(<GroupTagListItem group={group} tag={tag} isPinnable />);
|
||||
expect(screen.queryAllByTestId('pin-icon')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,196 +0,0 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { useUpdateGroupTag } from 'soapbox/api/hooks';
|
||||
import { HStack, Icon, IconButton, Stack, Text, Tooltip } from 'soapbox/components/ui';
|
||||
import { importEntities } from 'soapbox/entity-store/actions';
|
||||
import { Entities } from 'soapbox/entity-store/entities';
|
||||
import { useAppDispatch } from 'soapbox/hooks';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||
import toast from 'soapbox/toast';
|
||||
import { shortNumberFormat } from 'soapbox/utils/numbers';
|
||||
|
||||
import type { Group, GroupTag } from 'soapbox/schemas';
|
||||
|
||||
const messages = defineMessages({
|
||||
hideTag: { id: 'group.tags.hide', defaultMessage: 'Hide topic' },
|
||||
showTag: { id: 'group.tags.show', defaultMessage: 'Show topic' },
|
||||
total: { id: 'group.tags.total', defaultMessage: 'Total Posts' },
|
||||
pinTag: { id: 'group.tags.pin', defaultMessage: 'Pin topic' },
|
||||
unpinTag: { id: 'group.tags.unpin', defaultMessage: 'Unpin topic' },
|
||||
pinSuccess: { id: 'group.tags.pin.success', defaultMessage: 'Pinned!' },
|
||||
unpinSuccess: { id: 'group.tags.unpin.success', defaultMessage: 'Unpinned!' },
|
||||
visibleSuccess: { id: 'group.tags.visible.success', defaultMessage: 'Topic marked as visible' },
|
||||
hiddenSuccess: { id: 'group.tags.hidden.success', defaultMessage: 'Topic marked as hidden' },
|
||||
});
|
||||
|
||||
interface IGroupMemberListItem {
|
||||
tag: GroupTag;
|
||||
group: Group;
|
||||
isPinnable: boolean;
|
||||
}
|
||||
|
||||
const GroupTagListItem = (props: IGroupMemberListItem) => {
|
||||
const { group, tag, isPinnable } = props;
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const intl = useIntl();
|
||||
const { updateGroupTag } = useUpdateGroupTag(group.id, tag.id);
|
||||
|
||||
const isOwner = group.relationship?.role === GroupRoles.OWNER;
|
||||
|
||||
const toggleVisibility = () => {
|
||||
const isHiding = tag.visible;
|
||||
|
||||
updateGroupTag({
|
||||
group_tag_type: isHiding ? 'hidden' : 'normal',
|
||||
}, {
|
||||
onSuccess() {
|
||||
const entity: GroupTag = {
|
||||
...tag,
|
||||
visible: !tag.visible,
|
||||
pinned: isHiding ? false : tag.pinned, // unpin if we're hiding
|
||||
};
|
||||
dispatch(importEntities([entity], Entities.GROUP_TAGS));
|
||||
|
||||
toast.success(
|
||||
entity.visible ?
|
||||
intl.formatMessage(messages.visibleSuccess) :
|
||||
intl.formatMessage(messages.hiddenSuccess),
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const togglePin = () => {
|
||||
updateGroupTag({
|
||||
group_tag_type: tag.pinned ? 'normal' : 'pinned',
|
||||
}, {
|
||||
onSuccess() {
|
||||
const entity = {
|
||||
...tag,
|
||||
pinned: !tag.pinned,
|
||||
};
|
||||
dispatch(importEntities([entity], Entities.GROUP_TAGS));
|
||||
|
||||
toast.success(
|
||||
entity.pinned ?
|
||||
intl.formatMessage(messages.pinSuccess) :
|
||||
intl.formatMessage(messages.unpinSuccess),
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const renderPinIcon = () => {
|
||||
if (!isOwner && tag.pinned) {
|
||||
return (
|
||||
<Icon
|
||||
src={require('@tabler/icons/filled/pin.svg')}
|
||||
className='h-5 w-5 text-gray-600'
|
||||
data-testid='pin-icon'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isOwner) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isPinnable) {
|
||||
return (
|
||||
<Tooltip
|
||||
text={
|
||||
tag.pinned ?
|
||||
intl.formatMessage(messages.unpinTag) :
|
||||
intl.formatMessage(messages.pinTag)
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
onClick={togglePin}
|
||||
theme='transparent'
|
||||
src={
|
||||
tag.pinned ?
|
||||
require('@tabler/icons/filled/pin.svg') :
|
||||
require('@tabler/icons/outline/pin.svg')
|
||||
}
|
||||
iconClassName='h-5 w-5 text-primary-500 dark:text-accent-blue'
|
||||
data-testid='pin-icon'
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isPinnable && tag.pinned) {
|
||||
return (
|
||||
<Tooltip text={intl.formatMessage(messages.unpinTag)}>
|
||||
<IconButton
|
||||
onClick={togglePin}
|
||||
theme='transparent'
|
||||
src={require('@tabler/icons/filled/pin.svg')}
|
||||
iconClassName='h-5 w-5 text-primary-500 dark:text-accent-blue'
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<HStack
|
||||
alignItems='center'
|
||||
justifyContent='between'
|
||||
data-testid='group-tag-list-item'
|
||||
>
|
||||
<Link to={`/group/${group.slug}/tag/${tag.id}`} className='group grow'>
|
||||
<Stack>
|
||||
<Text
|
||||
weight='bold'
|
||||
theme={(tag.visible || !isOwner) ? 'default' : 'subtle'}
|
||||
className='group-hover:underline'
|
||||
data-testid='group-tag-name'
|
||||
>
|
||||
#{tag.name}
|
||||
</Text>
|
||||
<Text size='sm' theme={(tag.visible || !isOwner) ? 'muted' : 'subtle'}>
|
||||
{intl.formatMessage(messages.total)}:
|
||||
{' '}
|
||||
<Text size='sm' theme='inherit' weight='semibold' tag='span'>
|
||||
{shortNumberFormat(tag.uses)}
|
||||
</Text>
|
||||
</Text>
|
||||
</Stack>
|
||||
</Link>
|
||||
|
||||
<HStack alignItems='center' space={2}>
|
||||
{tag.visible ? (
|
||||
renderPinIcon()
|
||||
) : null}
|
||||
|
||||
{isOwner ? (
|
||||
<Tooltip
|
||||
text={
|
||||
tag.visible ?
|
||||
intl.formatMessage(messages.hideTag) :
|
||||
intl.formatMessage(messages.showTag)
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
onClick={toggleVisibility}
|
||||
theme='transparent'
|
||||
src={
|
||||
tag.visible ?
|
||||
require('@tabler/icons/outline/eye.svg') :
|
||||
require('@tabler/icons/outline/eye-off.svg')
|
||||
}
|
||||
iconClassName='h-5 w-5 text-primary-500 dark:text-accent-blue'
|
||||
/>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</HStack>
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default GroupTagListItem;
|
||||
@ -1,59 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { Input, Streamfield } from 'soapbox/components/ui';
|
||||
|
||||
import type { StreamfieldComponent } from 'soapbox/components/ui/streamfield/streamfield';
|
||||
|
||||
const messages = defineMessages({
|
||||
hashtagPlaceholder: { id: 'manage_group.fields.hashtag_placeholder', defaultMessage: 'Add a topic' },
|
||||
});
|
||||
|
||||
interface IGroupTagsField {
|
||||
tags: string[];
|
||||
onChange(tags: string[]): void;
|
||||
onAddItem(): void;
|
||||
onRemoveItem(i: number): void;
|
||||
maxItems?: number;
|
||||
}
|
||||
|
||||
const GroupTagsField: React.FC<IGroupTagsField> = ({ tags, onChange, onAddItem, onRemoveItem, maxItems = 3 }) => {
|
||||
return (
|
||||
<Streamfield
|
||||
label={<FormattedMessage id='group.tags.label' defaultMessage='Tags' />}
|
||||
hint={<FormattedMessage id='group.tags.hint' defaultMessage='Add up to 3 keywords that will serve as core topics of discussion in the group.' />}
|
||||
component={HashtagField}
|
||||
values={tags}
|
||||
onChange={onChange}
|
||||
onAddItem={onAddItem}
|
||||
onRemoveItem={onRemoveItem}
|
||||
maxItems={maxItems}
|
||||
minItems={1}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const HashtagField: StreamfieldComponent<string> = ({ value, onChange, autoFocus = false }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const formattedValue = useMemo(() => {
|
||||
return `#${value}`;
|
||||
}, [value]);
|
||||
|
||||
const handleChange: React.ChangeEventHandler<HTMLInputElement> = ({ target }) => {
|
||||
onChange(target.value.replace('#', ''));
|
||||
};
|
||||
|
||||
return (
|
||||
<Input
|
||||
outerClassName='w-full'
|
||||
type='text'
|
||||
value={formattedValue}
|
||||
onChange={handleChange}
|
||||
placeholder={intl.formatMessage(messages.hashtagPlaceholder)}
|
||||
autoFocus={autoFocus}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default GroupTagsField;
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { useGroup, useGroupTags, useUpdateGroup } from 'soapbox/api/hooks';
|
||||
import { useGroup, useUpdateGroup } from 'soapbox/api/hooks';
|
||||
import { Button, Column, Form, FormActions, FormGroup, Icon, Input, Spinner, Textarea } from 'soapbox/components/ui';
|
||||
import { useAppSelector, useInstance } from 'soapbox/hooks';
|
||||
import { useImageField, useTextField } from 'soapbox/hooks/forms';
|
||||
@ -11,8 +11,6 @@ import { isDefaultAvatar, isDefaultHeader } from 'soapbox/utils/accounts';
|
||||
import AvatarPicker from '../edit-profile/components/avatar-picker';
|
||||
import HeaderPicker from '../edit-profile/components/header-picker';
|
||||
|
||||
import GroupTagsField from './components/group-tags-field';
|
||||
|
||||
const nonDefaultAvatar = (url: string | undefined) => url && isDefaultAvatar(url) ? undefined : url;
|
||||
const nonDefaultHeader = (url: string | undefined) => url && isDefaultHeader(url) ? undefined : url;
|
||||
|
||||
@ -35,10 +33,8 @@ const EditGroup: React.FC<IEditGroup> = ({ params: { groupId } }) => {
|
||||
|
||||
const { group, isLoading } = useGroup(groupId);
|
||||
const { updateGroup } = useUpdateGroup(groupId);
|
||||
const { invalidate } = useGroupTags(groupId);
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [tags, setTags] = useState<string[]>(['']);
|
||||
|
||||
const avatar = useImageField({ maxPixels: 400 * 400, preview: nonDefaultAvatar(group?.avatar) });
|
||||
const header = useImageField({ maxPixels: 1920 * 1080, preview: nonDefaultHeader(group?.header) });
|
||||
@ -61,10 +57,8 @@ const EditGroup: React.FC<IEditGroup> = ({ params: { groupId } }) => {
|
||||
note: note.value,
|
||||
avatar: avatar.file === null ? '' : avatar.file,
|
||||
header: header.file === null ? '' : header.file,
|
||||
tags,
|
||||
}, {
|
||||
onSuccess() {
|
||||
invalidate();
|
||||
toast.success(intl.formatMessage(messages.groupSaved));
|
||||
},
|
||||
onError(error) {
|
||||
@ -79,22 +73,6 @@ const EditGroup: React.FC<IEditGroup> = ({ params: { groupId } }) => {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
const handleAddTag = () => {
|
||||
setTags([...tags, '']);
|
||||
};
|
||||
|
||||
const handleRemoveTag = (i: number) => {
|
||||
const newTags = [...tags];
|
||||
newTags.splice(i, 1);
|
||||
setTags(newTags);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (group) {
|
||||
setTags(group.tags.map((t) => t.name));
|
||||
}
|
||||
}, [group?.id]);
|
||||
|
||||
if (isLoading) {
|
||||
return <Spinner />;
|
||||
}
|
||||
@ -130,15 +108,6 @@ const EditGroup: React.FC<IEditGroup> = ({ params: { groupId } }) => {
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<div className='pb-6'>
|
||||
<GroupTagsField
|
||||
tags={tags}
|
||||
onChange={setTags}
|
||||
onAddItem={handleAddTag}
|
||||
onRemoveItem={handleRemoveTag}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormActions>
|
||||
<Button theme='primary' type='submit' disabled={isSubmitting} block>
|
||||
<FormattedMessage id='edit_profile.save' defaultMessage='Save' />
|
||||
|
||||
@ -82,7 +82,7 @@ const GroupBlockedMembers: React.FC<IGroupBlockedMembers> = ({ params }) => {
|
||||
const emptyMessage = <FormattedMessage id='empty_column.group_blocks' defaultMessage="The group hasn't banned any users yet." />;
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.heading)} backHref={`/group/${group.slug}/manage`}>
|
||||
<Column label={intl.formatMessage(messages.heading)} backHref={`/group/${group.id}/manage`}>
|
||||
<ScrollableList
|
||||
scrollKey='group_blocks'
|
||||
emptyMessage={emptyMessage}
|
||||
|
||||
@ -4,7 +4,6 @@ import React, { useMemo } from 'react';
|
||||
import { useGroup, useGroupMembers, useGroupMembershipRequests } from 'soapbox/api/hooks';
|
||||
import { PendingItemsRow } from 'soapbox/components/pending-items-row';
|
||||
import ScrollableList from 'soapbox/components/scrollable-list';
|
||||
import { useFeatures } from 'soapbox/hooks';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||
|
||||
import PlaceholderAccount from '../placeholder/components/placeholder-account';
|
||||
@ -18,13 +17,9 @@ interface IGroupMembers {
|
||||
params: { groupId: string };
|
||||
}
|
||||
|
||||
export const MAX_ADMIN_COUNT = 5;
|
||||
|
||||
const GroupMembers: React.FC<IGroupMembers> = (props) => {
|
||||
const { groupId } = props.params;
|
||||
|
||||
const features = useFeatures();
|
||||
|
||||
const { group, isFetching: isFetchingGroup } = useGroup(groupId);
|
||||
const { groupMembers: owners, isFetching: isFetchingOwners } = useGroupMembers(groupId, GroupRoles.OWNER);
|
||||
const { groupMembers: admins, isFetching: isFetchingAdmins } = useGroupMembers(groupId, GroupRoles.ADMIN);
|
||||
@ -39,10 +34,6 @@ const GroupMembers: React.FC<IGroupMembers> = (props) => {
|
||||
...users,
|
||||
], [owners, admins, users]);
|
||||
|
||||
const canPromoteToAdmin = features.groupsAdminMax
|
||||
? members.filter((member) => member.role === GroupRoles.ADMIN).length < MAX_ADMIN_COUNT
|
||||
: true;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ScrollableList
|
||||
@ -58,7 +49,7 @@ const GroupMembers: React.FC<IGroupMembers> = (props) => {
|
||||
prepend={(pendingCount > 0) && (
|
||||
<div className={clsx('py-3', { 'border-b border-gray-200 dark:border-gray-800': members.length })}>
|
||||
<PendingItemsRow
|
||||
to={`/group/${group?.slug}/manage/requests`}
|
||||
to={`/group/${group?.id}/manage/requests`}
|
||||
count={pendingCount}
|
||||
/>
|
||||
</div>
|
||||
@ -69,7 +60,6 @@ const GroupMembers: React.FC<IGroupMembers> = (props) => {
|
||||
group={group as Group}
|
||||
member={member}
|
||||
key={member.account.id}
|
||||
canPromoteToAdmin={canPromoteToAdmin}
|
||||
/>
|
||||
))}
|
||||
</ScrollableList>
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { expandGroupTimelineFromTag } from 'soapbox/actions/timelines';
|
||||
import { useGroup, useGroupTag } from 'soapbox/api/hooks';
|
||||
import { Column, Icon, Stack, Text } from 'soapbox/components/ui';
|
||||
import { useAppDispatch } from 'soapbox/hooks';
|
||||
|
||||
import Timeline from '../ui/components/timeline';
|
||||
|
||||
type RouteParams = { tagId: string; groupId: string };
|
||||
|
||||
interface IGroupTimeline {
|
||||
params: RouteParams;
|
||||
}
|
||||
|
||||
const GroupTagTimeline: React.FC<IGroupTimeline> = (props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const groupId = props.params.groupId;
|
||||
const tagId = props.params.tagId;
|
||||
|
||||
const { group } = useGroup(groupId);
|
||||
const { tag, isLoading } = useGroupTag(tagId);
|
||||
|
||||
const handleLoadMore = (maxId: string) => {
|
||||
dispatch(expandGroupTimelineFromTag(groupId, tag?.name as string, { maxId }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (tag?.name) {
|
||||
dispatch(expandGroupTimelineFromTag(groupId, tag?.name));
|
||||
}
|
||||
}, [groupId, tag]);
|
||||
|
||||
|
||||
if (isLoading || !tag || !group) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Column label={`#${tag.name}`}>
|
||||
<Timeline
|
||||
scrollKey='group_timeline'
|
||||
timelineId={`group:tags:${groupId}:${tag.name}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
divideType='border'
|
||||
showGroup={false}
|
||||
emptyMessageCard={false}
|
||||
emptyMessage={
|
||||
<Stack space={4} className='py-6' justifyContent='center' alignItems='center'>
|
||||
<div className='rounded-full bg-gray-200 p-4 dark:bg-gray-800'>
|
||||
<Icon
|
||||
src={require('@tabler/icons/outline/message-2.svg')}
|
||||
className='h-6 w-6 text-gray-600'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Text theme='muted'>
|
||||
<FormattedMessage id='empty_column.group' defaultMessage='There are no posts in this group yet.' />
|
||||
</Text>
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
export default GroupTagTimeline;
|
||||
@ -1,68 +0,0 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { useGroup, useGroupTags } from 'soapbox/api/hooks';
|
||||
import ScrollableList from 'soapbox/components/scrollable-list';
|
||||
import { Icon, Stack, Text } from 'soapbox/components/ui';
|
||||
|
||||
import PlaceholderAccount from '../placeholder/components/placeholder-account';
|
||||
|
||||
import GroupTagListItem from './components/group-tag-list-item';
|
||||
|
||||
import type { Group } from 'soapbox/types/entities';
|
||||
|
||||
interface IGroupTopics {
|
||||
params: { groupId: string };
|
||||
}
|
||||
|
||||
const GroupTopics: React.FC<IGroupTopics> = (props) => {
|
||||
const { groupId } = props.params;
|
||||
|
||||
const { group, isFetching: isFetchingGroup } = useGroup(groupId);
|
||||
const { tags, isFetching: isFetchingTags, hasNextPage, fetchNextPage } = useGroupTags(groupId);
|
||||
|
||||
const isLoading = isFetchingGroup || isFetchingTags;
|
||||
|
||||
const pinnedTags = tags.filter((tag) => tag.pinned);
|
||||
const isPinnable = pinnedTags.length < 3;
|
||||
|
||||
return (
|
||||
<ScrollableList
|
||||
scrollKey='group-tags'
|
||||
hasMore={hasNextPage}
|
||||
onLoadMore={fetchNextPage}
|
||||
isLoading={isLoading || !group}
|
||||
showLoading={!group || isLoading && tags.length === 0}
|
||||
placeholderComponent={PlaceholderAccount}
|
||||
placeholderCount={3}
|
||||
listClassName='divide-y divide-solid divide-gray-300 dark:divide-gray-800'
|
||||
itemClassName='py-3 last:pb-0'
|
||||
emptyMessage={
|
||||
<Stack space={4} className='pt-6' justifyContent='center' alignItems='center'>
|
||||
<div className='rounded-full bg-gray-200 p-4 dark:bg-gray-800'>
|
||||
<Icon
|
||||
src={require('@tabler/icons/outline/hash.svg')}
|
||||
className='h-6 w-6 text-gray-600'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Text theme='muted'>
|
||||
<FormattedMessage id='group.tags.empty' defaultMessage='There are no topics in this group yet.' />
|
||||
</Text>
|
||||
</Stack>
|
||||
}
|
||||
emptyMessageCard={false}
|
||||
>
|
||||
{tags.map((tag) => (
|
||||
<GroupTagListItem
|
||||
key={tag.id}
|
||||
group={group as Group}
|
||||
isPinnable={isPinnable}
|
||||
tag={tag}
|
||||
/>
|
||||
))}
|
||||
</ScrollableList>
|
||||
);
|
||||
};
|
||||
|
||||
export default GroupTopics;
|
||||
@ -3,10 +3,10 @@ import React, { useEffect, useRef } from 'react';
|
||||
import { FormattedMessage, useIntl } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { groupCompose, setGroupTimelineVisible, uploadCompose } from 'soapbox/actions/compose';
|
||||
import { groupCompose, uploadCompose } from 'soapbox/actions/compose';
|
||||
import { expandGroupFeaturedTimeline, expandGroupTimeline } from 'soapbox/actions/timelines';
|
||||
import { useGroup, useGroupStream } from 'soapbox/api/hooks';
|
||||
import { Avatar, HStack, Icon, Stack, Text, Toggle } from 'soapbox/components/ui';
|
||||
import { Avatar, HStack, Icon, Stack, Text } from 'soapbox/components/ui';
|
||||
import ComposeForm from 'soapbox/features/compose/components/compose-form';
|
||||
import { useAppDispatch, useAppSelector, useDraggedFiles, useOwnAccount } from 'soapbox/hooks';
|
||||
import { makeGetStatusIds } from 'soapbox/selectors';
|
||||
@ -33,7 +33,6 @@ const GroupTimeline: React.FC<IGroupTimeline> = (props) => {
|
||||
|
||||
const composeId = `group:${groupId}`;
|
||||
const canComposeGroupStatus = !!account && group?.relationship?.member;
|
||||
const groupTimelineVisible = useAppSelector((state) => !!state.compose.get(composeId)?.group_timeline_visible);
|
||||
const featuredStatusIds = useAppSelector((state) => getStatusIds(state, { type: `group:${group?.id}:pinned` }));
|
||||
|
||||
const { isDragging, isDraggedOver } = useDraggedFiles(composer, (files) => {
|
||||
@ -44,10 +43,6 @@ const GroupTimeline: React.FC<IGroupTimeline> = (props) => {
|
||||
dispatch(expandGroupTimeline(groupId, { maxId }));
|
||||
};
|
||||
|
||||
const handleToggleChange = () => {
|
||||
dispatch(setGroupTimelineVisible(composeId, !groupTimelineVisible));
|
||||
};
|
||||
|
||||
useGroupStream(groupId);
|
||||
|
||||
useEffect(() => {
|
||||
@ -89,12 +84,6 @@ const GroupTimeline: React.FC<IGroupTimeline> = (props) => {
|
||||
<FormattedMessage id='compose_group.share_to_followers' defaultMessage='Share with my followers' />
|
||||
</Text>
|
||||
</label>
|
||||
<Toggle
|
||||
id='group-timeline-visible'
|
||||
checked={groupTimelineVisible}
|
||||
onChange={handleToggleChange}
|
||||
size='sm'
|
||||
/>
|
||||
</HStack>
|
||||
)}
|
||||
/>
|
||||
|
||||
@ -6,10 +6,9 @@ import { openModal } from 'soapbox/actions/modals';
|
||||
import { useDeleteGroup, useGroup } from 'soapbox/api/hooks';
|
||||
import List, { ListItem } from 'soapbox/components/list';
|
||||
import { CardBody, CardHeader, CardTitle, Column, Spinner, Text } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useBackend, useGroupsPath } from 'soapbox/hooks';
|
||||
import { useAppDispatch } from 'soapbox/hooks';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||
import toast from 'soapbox/toast';
|
||||
import { TRUTHSOCIAL } from 'soapbox/utils/features';
|
||||
|
||||
import ColumnForbidden from '../ui/components/column-forbidden';
|
||||
|
||||
@ -36,9 +35,7 @@ interface IManageGroup {
|
||||
const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
||||
const { groupId: id } = params;
|
||||
|
||||
const backend = useBackend();
|
||||
const dispatch = useAppDispatch();
|
||||
const groupsPath = useGroupsPath();
|
||||
const history = useHistory();
|
||||
const intl = useIntl();
|
||||
|
||||
@ -70,14 +67,14 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
||||
deleteGroup.mutate(group.id, {
|
||||
onSuccess() {
|
||||
toast.success(intl.formatMessage(messages.deleteSuccess));
|
||||
history.push(groupsPath);
|
||||
history.push('/groups');
|
||||
},
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.heading)} backHref={`/group/${group.slug}`}>
|
||||
<Column label={intl.formatMessage(messages.heading)} backHref={`/group/${group.id}`}>
|
||||
<CardBody className='space-y-4'>
|
||||
{isOwner && (
|
||||
<>
|
||||
@ -86,7 +83,7 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
||||
</CardHeader>
|
||||
|
||||
<List>
|
||||
<ListItem label={intl.formatMessage(messages.editGroup)} to={`/group/${group.slug}/manage/edit`}>
|
||||
<ListItem label={intl.formatMessage(messages.editGroup)} to={`/group/${group.id}/manage/edit`}>
|
||||
<span dangerouslySetInnerHTML={{ __html: group.display_name_html }} />
|
||||
</ListItem>
|
||||
</List>
|
||||
@ -98,11 +95,9 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
||||
</CardHeader>
|
||||
|
||||
<List>
|
||||
{backend.software !== TRUTHSOCIAL && (
|
||||
<ListItem label={intl.formatMessage(messages.pendingRequests)} to={`/group/${group.slug}/manage/requests`} />
|
||||
)}
|
||||
<ListItem label={intl.formatMessage(messages.pendingRequests)} to={`/group/${group.id}/manage/requests`} />
|
||||
|
||||
<ListItem label={intl.formatMessage(messages.blockedMembers)} to={`/group/${group.slug}/manage/blocks`} />
|
||||
<ListItem label={intl.formatMessage(messages.blockedMembers)} to={`/group/${group.id}/manage/blocks`} />
|
||||
</List>
|
||||
|
||||
{isOwner && (
|
||||
|
||||
Reference in New Issue
Block a user