From 75fca715e9ae8ad347edbaf6511a052f161a3de3 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 2 Sep 2025 17:48:37 +0200 Subject: [PATCH] Change public self-quotes of private posts to be disallowed (#35975) --- .../ui/components/visibility_modal.tsx | 75 ++++++++++++++----- app/javascript/mastodon/locales/en.json | 1 + app/javascript/mastodon/reducers/compose.js | 3 +- .../status/interaction_policy_concern.rb | 2 +- app/services/post_status_service.rb | 1 + spec/policies/status_policy_spec.rb | 4 +- spec/services/post_status_service_spec.rb | 8 ++ 7 files changed, 70 insertions(+), 24 deletions(-) diff --git a/app/javascript/mastodon/features/ui/components/visibility_modal.tsx b/app/javascript/mastodon/features/ui/components/visibility_modal.tsx index 8b48cad5401..e8570dcb93f 100644 --- a/app/javascript/mastodon/features/ui/components/visibility_modal.tsx +++ b/app/javascript/mastodon/features/ui/components/visibility_modal.tsx @@ -89,6 +89,24 @@ const selectStatusPolicy = createAppSelector( }, ); +const selectDisablePublicVisibilities = createAppSelector( + [ + (state) => state.statuses, + (_state, statusId?: string) => !!statusId, + (state) => state.compose.get('quoted_status_id') as string | null, + ], + (statuses, isEditing, statusId) => { + if (isEditing || !statusId) return false; + + const status = statuses.get(statusId); + if (!status) { + return false; + } + + return status.get('visibility') === 'private'; + }, +); + export const VisibilityModal: FC = forwardRef( // eslint-disable-next-line @typescript-eslint/no-unused-vars ({ onClose, onChange, statusId }, _ref) => { @@ -110,24 +128,12 @@ export const VisibilityModal: FC = forwardRef( const disableVisibility = !!statusId; const disableQuotePolicy = visibility === 'private' || visibility === 'direct'; + const disablePublicVisibilities: boolean = useAppSelector( + selectDisablePublicVisibilities, + ); - const visibilityItems = useMemo[]>( - () => [ - { - value: 'public', - text: intl.formatMessage(privacyMessages.public_short), - meta: intl.formatMessage(privacyMessages.public_long), - icon: 'globe', - iconComponent: PublicIcon, - }, - { - value: 'unlisted', - text: intl.formatMessage(privacyMessages.unlisted_short), - meta: intl.formatMessage(privacyMessages.unlisted_long), - extra: intl.formatMessage(privacyMessages.unlisted_extra), - icon: 'unlock', - iconComponent: QuietTimeIcon, - }, + const visibilityItems = useMemo[]>(() => { + const items: SelectItem[] = [ { value: 'private', text: intl.formatMessage(privacyMessages.private_short), @@ -142,9 +148,30 @@ export const VisibilityModal: FC = forwardRef( icon: 'at', iconComponent: AlternateEmailIcon, }, - ], - [intl], - ); + ]; + + if (!disablePublicVisibilities) { + items.unshift( + { + value: 'public', + text: intl.formatMessage(privacyMessages.public_short), + meta: intl.formatMessage(privacyMessages.public_long), + icon: 'globe', + iconComponent: PublicIcon, + }, + { + value: 'unlisted', + text: intl.formatMessage(privacyMessages.unlisted_short), + meta: intl.formatMessage(privacyMessages.unlisted_long), + extra: intl.formatMessage(privacyMessages.unlisted_extra), + icon: 'unlock', + iconComponent: QuietTimeIcon, + }, + ); + } + + return items; + }, [intl, disablePublicVisibilities]); const quoteItems = useMemo[]>( () => [ { value: 'public', text: intl.formatMessage(messages.quotePublic) }, @@ -236,6 +263,14 @@ export const VisibilityModal: FC = forwardRef( />

)} + {!statusId && disablePublicVisibilities && ( +

+ +

+ )}