Account for soft-deleted Groups in the profile

This commit is contained in:
Chewbacca 2023-04-26 15:53:52 -04:00
parent 84641d053a
commit 27f632786f
5 changed files with 69 additions and 25 deletions

View File

@ -52,6 +52,8 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
); );
} }
const isDeleted = !!group.deleted_at;
const onAvatarClick = () => { const onAvatarClick = () => {
const avatar = normalizeAttachment({ const avatar = normalizeAttachment({
type: 'image', type: 'image',
@ -136,24 +138,28 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
dangerouslySetInnerHTML={{ __html: group.display_name_html }} dangerouslySetInnerHTML={{ __html: group.display_name_html }}
/> />
<Stack space={1} alignItems='center'> {!isDeleted && (
<HStack className='text-gray-700 dark:text-gray-600' space={2} wrap> <>
<GroupRelationship group={group} /> <Stack space={1} alignItems='center'>
<GroupPrivacy group={group} /> <HStack className='text-gray-700 dark:text-gray-600' space={2} wrap>
<GroupMemberCount group={group} /> <GroupRelationship group={group} />
</HStack> <GroupPrivacy group={group} />
<GroupMemberCount group={group} />
</HStack>
<Text <Text
theme='muted' theme='muted'
align='center' align='center'
dangerouslySetInnerHTML={{ __html: group.note_emojified }} dangerouslySetInnerHTML={{ __html: group.note_emojified }}
/> />
</Stack> </Stack>
<HStack alignItems='center' space={2}> <HStack alignItems='center' space={2}>
<GroupOptionsButton group={group} /> <GroupOptionsButton group={group} />
<GroupActionButton group={group} /> <GroupActionButton group={group} />
</HStack> </HStack>
</>
)}
</Stack> </Stack>
</div> </div>
); );

View File

@ -768,8 +768,10 @@
"gdpr.message": "{siteTitle} uses session cookies, which are essential to the website's functioning.", "gdpr.message": "{siteTitle} uses session cookies, which are essential to the website's functioning.",
"gdpr.title": "{siteTitle} uses cookies", "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}).", "getting_started.open_source_notice": "{code_name} is open source software. You can contribute or report issues at {code_link} (v{code_version}).",
"group.banned.message": "You are banned from",
"group.cancel_request": "Cancel Request", "group.cancel_request": "Cancel Request",
"group.delete.success": "Group successfully deleted", "group.delete.success": "Group successfully deleted",
"group.deleted.message": "This group has been deleted.",
"group.demote.user.success": "@{name} is now a member", "group.demote.user.success": "@{name} is now a member",
"group.group_mod_authorize.fail": "Failed to approve @{name}", "group.group_mod_authorize.fail": "Failed to approve @{name}",
"group.group_mod_block": "Ban from group", "group.group_mod_block": "Ban from group",
@ -801,6 +803,7 @@
"group.privacy.public": "Public", "group.privacy.public": "Public",
"group.privacy.public.full": "Public Group", "group.privacy.public.full": "Public Group",
"group.privacy.public.info": "Discoverable. Anyone can join.", "group.privacy.public.info": "Discoverable. Anyone can join.",
"group.private.message": "Content is only visible to group members",
"group.promote.admin.confirmation.message": "Are you sure you want to assign the admin role to @{name}?", "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.confirmation.title": "Assign Admin Role",
"group.promote.admin.success": "@{name} is now an admin", "group.promote.admin.success": "@{name} is now an admin",

View File

@ -21,6 +21,7 @@ export const GroupRecord = ImmutableRecord({
avatar: '', avatar: '',
avatar_static: '', avatar_static: '',
created_at: '', created_at: '',
deleted_at: null,
display_name: '', display_name: '',
domain: '', domain: '',
emojis: [] as Emoji[], emojis: [] as Emoji[],

View File

@ -1,5 +1,5 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import { useRouteMatch } from 'react-router-dom'; import { useRouteMatch } from 'react-router-dom';
import GroupLookupHoc from 'soapbox/components/hoc/group-lookup-hoc'; import GroupLookupHoc from 'soapbox/components/hoc/group-lookup-hoc';
@ -34,26 +34,56 @@ interface IGroupPage {
children: React.ReactNode children: React.ReactNode
} }
const PrivacyBlankslate = () => ( const DeletedBlankslate = () => (
<Stack space={4} className='py-10' alignItems='center'> <Stack space={4} className='py-10' alignItems='center'>
<div className='rounded-full bg-gray-200 p-3'> <div className='rounded-full bg-danger-200 p-3 dark:bg-danger-400/20'>
<Icon src={require('@tabler/icons/eye-off.svg')} className='h-6 w-6 text-gray-600' /> <Icon
src={require('@tabler/icons/trash.svg')}
className='h-6 w-6 text-danger-600 dark:text-danger-400'
/>
</div> </div>
<Text theme='muted'> <Text theme='muted'>
Content is only visible to group members <FormattedMessage
id='group.deleted.message'
defaultMessage='This group has been deleted.'
/>
</Text>
</Stack>
);
const PrivacyBlankslate = () => (
<Stack space={4} className='py-10' alignItems='center'>
<div className='rounded-full bg-gray-200 p-3 dark:bg-gray-800'>
<Icon
src={require('@tabler/icons/eye-off.svg')}
className='h-6 w-6 text-gray-600 dark:text-gray-600'
/>
</div>
<Text theme='muted'>
<FormattedMessage
id='group.private.message'
defaultMessage='Content is only visible to group members'
/>
</Text> </Text>
</Stack> </Stack>
); );
const BlockedBlankslate = ({ group }: { group: Group }) => ( const BlockedBlankslate = ({ group }: { group: Group }) => (
<Stack space={4} className='py-10' alignItems='center'> <Stack space={4} className='py-10' alignItems='center'>
<div className='rounded-full bg-danger-200 p-3'> <div className='rounded-full bg-danger-200 p-3 dark:bg-danger-400/20'>
<Icon src={require('@tabler/icons/eye-off.svg')} className='h-6 w-6 text-danger-600' /> <Icon
src={require('@tabler/icons/ban.svg')}
className='h-6 w-6 text-danger-600 dark:text-danger-400'
/>
</div> </div>
<Text theme='muted'> <Text theme='muted'>
You are banned from <FormattedMessage
id='group.banned.message'
defaultMessage='You are banned from'
/>
{' '} {' '}
<Text theme='inherit' tag='span' dangerouslySetInnerHTML={{ __html: group.display_name_html }} /> <Text theme='inherit' tag='span' dangerouslySetInnerHTML={{ __html: group.display_name_html }} />
</Text> </Text>
@ -75,6 +105,7 @@ const GroupPage: React.FC<IGroupPage> = ({ params, children }) => {
const isMember = !!group?.relationship?.member; const isMember = !!group?.relationship?.member;
const isBlocked = group?.relationship?.blocked_by; const isBlocked = group?.relationship?.blocked_by;
const isPrivate = group?.locked; const isPrivate = group?.locked;
const isDeleted = !!group?.deleted_at;
const tabItems = useMemo(() => { const tabItems = useMemo(() => {
const items = []; const items = [];
@ -108,7 +139,9 @@ const GroupPage: React.FC<IGroupPage> = ({ params, children }) => {
}, [features.groupsTags, pending.length]); }, [features.groupsTags, pending.length]);
const renderChildren = () => { const renderChildren = () => {
if (!isMember && isPrivate) { if (isDeleted) {
return <DeletedBlankslate />;
} else if (!isMember && isPrivate) {
return <PrivacyBlankslate />; return <PrivacyBlankslate />;
} else if (isBlocked) { } else if (isBlocked) {
return <BlockedBlankslate group={group} />; return <BlockedBlankslate group={group} />;

View File

@ -16,6 +16,7 @@ const groupSchema = z.object({
avatar: z.string().catch(avatarMissing), avatar: z.string().catch(avatarMissing),
avatar_static: z.string().catch(''), avatar_static: z.string().catch(''),
created_at: z.string().datetime().catch(new Date().toUTCString()), created_at: z.string().datetime().catch(new Date().toUTCString()),
deleted_at: z.string().datetime().or(z.null()).catch(null),
display_name: z.string().catch(''), display_name: z.string().catch(''),
domain: z.string().catch(''), domain: z.string().catch(''),
emojis: filteredArray(customEmojiSchema).catch([]), emojis: filteredArray(customEmojiSchema).catch([]),