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}}",