From 0c84346c3ae14a1362735211cb618fb64b0e1655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Wed, 30 Oct 2024 21:10:17 +0100 Subject: [PATCH] pl-fe: Support GoToSocial interaction policies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../groups/popover/group-popover.tsx | 7 +- .../src/components/status-action-bar.tsx | 104 +++++++++++++++++- .../features/account/components/header.tsx | 1 - .../event/components/event-header.tsx | 1 - packages/pl-fe/src/locales/en.json | 15 +++ 5 files changed, 121 insertions(+), 7 deletions(-) diff --git a/packages/pl-fe/src/components/groups/popover/group-popover.tsx b/packages/pl-fe/src/components/groups/popover/group-popover.tsx index c2f5f5bed..197e40398 100644 --- a/packages/pl-fe/src/components/groups/popover/group-popover.tsx +++ b/packages/pl-fe/src/components/groups/popover/group-popover.tsx @@ -106,10 +106,9 @@ const GroupPopover = (props: IGroupPopoverContainer) => { } isFlush - children={ -
{children}
- } - /> + > +
{children}
+ ); }; diff --git a/packages/pl-fe/src/components/status-action-bar.tsx b/packages/pl-fe/src/components/status-action-bar.tsx index d913ed7df..1061a9b06 100644 --- a/packages/pl-fe/src/components/status-action-bar.tsx +++ b/packages/pl-fe/src/components/status-action-bar.tsx @@ -27,6 +27,7 @@ import EmojiPickerDropdown from 'pl-fe/features/emoji/containers/emoji-picker-dr import { languages } from 'pl-fe/features/preferences'; import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; import { useAppSelector } from 'pl-fe/hooks/use-app-selector'; +import { useCanInteract } from 'pl-fe/hooks/use-can-interact'; import { useFeatures } from 'pl-fe/hooks/use-features'; import { useInstance } from 'pl-fe/hooks/use-instance'; import { useOwnAccount } from 'pl-fe/hooks/use-own-account'; @@ -38,6 +39,9 @@ import toast from 'pl-fe/toast'; import copy from 'pl-fe/utils/copy'; import GroupPopover from './groups/popover/group-popover'; +import Popover from './ui/popover'; +import Stack from './ui/stack'; +import Text from './ui/text'; import type { Menu } from 'pl-fe/components/dropdown-menu'; import type { Emoji as EmojiType } from 'pl-fe/features/emoji'; @@ -111,8 +115,76 @@ const messages = defineMessages({ addKnownLanguage: { id: 'status.add_known_language', defaultMessage: 'Do not auto-translate posts in {language}.' }, translate: { id: 'status.translate', defaultMessage: 'Translate' }, hideTranslation: { id: 'status.hide_translation', defaultMessage: 'Hide translation' }, + + favouriteInteractionPolicyHeader: { id: 'status.interaction_policy.favourite.header', defaultMessage: 'The author limits who can like this post.' }, + reblogInteractionPolicyHeader: { id: 'status.interaction_policy.reblog.header', defaultMessage: 'The author limits who can repost this post.' }, + replyInteractionPolicyHeader: { id: 'status.interaction_policy.reply.header', defaultMessage: 'The author limits who can reply to this post.' }, + + favouriteInteractionPolicyFollowers: { id: 'status.interaction_policy.favourite.followers_only', defaultMessage: 'Only users following the author can like.' }, + favouriteInteractionPolicyFollowing: { id: 'status.interaction_policy.favourite.following_only', defaultMessage: 'Only users followed by the author can like.' }, + favouriteInteractionPolicyMutuals: { id: 'status.interaction_policy.favourite.mutuals_only', defaultMessage: 'Only users mutually following the author can like.' }, + favouriteInteractionPolicyMentioned: { id: 'status.interaction_policy.favourite.mentioned_only', defaultMessage: 'Only users mentioned by the author can like.' }, + + reblogInteractionPolicyFollowers: { id: 'status.interaction_policy.reblog.followers_only', defaultMessage: 'Only users following the author can repost.' }, + reblogInteractionPolicyFollowing: { id: 'status.interaction_policy.reblog.following_only', defaultMessage: 'Only users followed by the author can repost.' }, + reblogInteractionPolicyMutuals: { id: 'status.interaction_policy.reblog.mutuals_only', defaultMessage: 'Only users mutually following the author can repost.' }, + reblogInteractionPolicyMentioned: { id: 'status.interaction_policy.reblog.mentioned_only', defaultMessage: 'Only users mentioned by the author can repost.' }, + + replyInteractionPolicyFollowers: { id: 'status.interaction_policy.reply.followers_only', defaultMessage: 'Only users following the author can reply.' }, + replyInteractionPolicyFollowing: { id: 'status.interaction_policy.reply.following_only', defaultMessage: 'Only users followed by the author can reply.' }, + replyInteractionPolicyMutuals: { id: 'status.interaction_policy.reply.mutuals_only', defaultMessage: 'Only users mutually following the author can reply.' }, + replyInteractionPolicyMentioned: { id: 'status.interaction_policy.reply.mentioned_only', defaultMessage: 'Only users mentioned by the author can reply.' }, }); +interface IInteractionPopover { + type: 'favourite' | 'reblog' | 'reply'; + allowed: ReturnType['allowed']; +} + +const INTERACTION_POLICY_HEADERS = { + favourite: messages.favouriteInteractionPolicyHeader, + reblog: messages.reblogInteractionPolicyHeader, + reply: messages.replyInteractionPolicyHeader, +}; + +const INTERACTION_POLICY_DESCRIPTIONS = { + favourite: { + followers: messages.favouriteInteractionPolicyFollowers, + following: messages.favouriteInteractionPolicyFollowing, + mutuals: messages.favouriteInteractionPolicyMutuals, + mentioned: messages.favouriteInteractionPolicyMentioned, + }, + reblog: { + followers: messages.reblogInteractionPolicyFollowers, + following: messages.reblogInteractionPolicyFollowing, + mutuals: messages.reblogInteractionPolicyMutuals, + mentioned: messages.reblogInteractionPolicyMentioned, + }, + reply: { + followers: messages.replyInteractionPolicyFollowers, + following: messages.replyInteractionPolicyFollowing, + mutuals: messages.replyInteractionPolicyMutuals, + mentioned: messages.replyInteractionPolicyMentioned, + }, +}; + +const InteractionPopover: React.FC = ({ type, allowed }) => { + const intl = useIntl(); + + const allowedType = allowed?.includes('followers') ? 'followers' : allowed?.includes('following') ? 'following' : allowed?.includes('mutuals') ? 'mutuals' : 'mentioned'; + + return ( + + + {intl.formatMessage(INTERACTION_POLICY_HEADERS[type])} + + + {intl.formatMessage(INTERACTION_POLICY_DESCRIPTIONS[type][allowedType])} + + + ); +}; + interface IActionButton extends Pick { me: Me; onOpenUnauthorizedModal: (action?: UnauthorizedModalAction) => void; @@ -133,6 +205,7 @@ const ReplyButton: React.FC = ({ const dispatch = useAppDispatch(); const intl = useIntl(); + const canReply = useCanInteract(status, 'can_reply'); const { groupRelationship } = useGroupRelationship(status.group_id || undefined); let replyTitle; @@ -170,6 +243,15 @@ const ReplyButton: React.FC = ({ /> ); + if (me && !canReply.canInteract) return ( + } + > + {replyButton} + + ); + return status.group ? ( = ({ const { boostModal } = useSettings(); const { openModal } = useModalsStore(); + const canReblog = useCanInteract(status, 'can_reblog'); let reblogIcon = require('@tabler/icons/outline/repeat.svg'); @@ -239,6 +322,15 @@ const ReblogButton: React.FC = ({ /> ); + if (me && !canReblog.canInteract) return ( + } + > + {reblogButton} + + ); + if (!features.quotePosts || !me) return reblogButton; const handleQuoteClick: React.EventHandler = (e) => { @@ -282,6 +374,7 @@ const FavouriteButton: React.FC = ({ const intl = useIntl(); const { openModal } = useModalsStore(); + const canFavourite = useCanInteract(status, 'can_favourite'); const handleFavouriteClick: React.EventHandler = (e) => { if (me) { @@ -295,7 +388,7 @@ const FavouriteButton: React.FC = ({ openModal('FAVOURITES', { statusId: status.id }); } : undefined; - return ( + const favouriteButton = ( = ({ theme={statusActionButtonTheme} /> ); + + if (me && !canFavourite.canInteract) return ( + } + > + {favouriteButton} + + ); }; const DislikeButton: React.FC = ({ diff --git a/packages/pl-fe/src/features/account/components/header.tsx b/packages/pl-fe/src/features/account/components/header.tsx index 645c28ed0..025503507 100644 --- a/packages/pl-fe/src/features/account/components/header.tsx +++ b/packages/pl-fe/src/features/account/components/header.tsx @@ -683,7 +683,6 @@ const Header: React.FC = ({ account }) => { theme='outlined' className='px-2' iconClassName='h-4 w-4' - children={null} /> )} diff --git a/packages/pl-fe/src/features/event/components/event-header.tsx b/packages/pl-fe/src/features/event/components/event-header.tsx index 6d458b100..df2717ef2 100644 --- a/packages/pl-fe/src/features/event/components/event-header.tsx +++ b/packages/pl-fe/src/features/event/components/event-header.tsx @@ -389,7 +389,6 @@ const EventHeader: React.FC = ({ status }) => { theme='outlined' className='h-[30px] px-2' iconClassName='h-4 w-4' - children={null} /> diff --git a/packages/pl-fe/src/locales/en.json b/packages/pl-fe/src/locales/en.json index 8ba99c5cd..51dba15d6 100644 --- a/packages/pl-fe/src/locales/en.json +++ b/packages/pl-fe/src/locales/en.json @@ -1459,6 +1459,21 @@ "status.group": "Posted in {group}", "status.group_mod_delete": "Delete post from group", "status.hide_translation": "Hide translation", + "status.interaction_policy.favourite.followers_only": "Only users following the author can like.", + "status.interaction_policy.favourite.following_only": "Only users followed by the author can like.", + "status.interaction_policy.favourite.header": "The author limits who can like this post.", + "status.interaction_policy.favourite.mentioned_only": "Only users mentioned by the author can like.", + "status.interaction_policy.favourite.mutuals_only": "Only users mutually following the author can like.", + "status.interaction_policy.reblog.followers_only": "Only users following the author can repost.", + "status.interaction_policy.reblog.following_only": "Only users followed by the author can repost.", + "status.interaction_policy.reblog.header": "The author limits who can repost this post.", + "status.interaction_policy.reblog.mentioned_only": "Only users mentioned by the author can repost.", + "status.interaction_policy.reblog.mutuals_only": "Only users mutually following the author can repost.", + "status.interaction_policy.reply.followers_only": "Only users following the author can reply.", + "status.interaction_policy.reply.following_only": "Only users followed by the author can reply.", + "status.interaction_policy.reply.header": "The author limits who can reply to this post.", + "status.interaction_policy.reply.mentioned_only": "Only users mentioned by the author can reply.", + "status.interaction_policy.reply.mutuals_only": "Only users mutually following the author can reply.", "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}}",