import { forwardRef, useCallback, useId, useMemo } from 'react'; import type { FC } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import classNames from 'classnames'; import { changeComposeVisibility } from '@/flavours/glitch/actions/compose'; import { setStatusQuotePolicy } from '@/flavours/glitch/actions/statuses_typed'; import type { ApiQuotePolicy } from '@/flavours/glitch/api_types/quotes'; import { isQuotePolicy } from '@/flavours/glitch/api_types/quotes'; import type { StatusVisibility } from '@/flavours/glitch/api_types/statuses'; import { Dropdown } from '@/flavours/glitch/components/dropdown'; import type { SelectItem } from '@/flavours/glitch/components/dropdown_selector'; import { IconButton } from '@/flavours/glitch/components/icon_button'; import { messages as privacyMessages } from '@/flavours/glitch/features/compose/components/privacy_dropdown'; import { createAppSelector, useAppDispatch, useAppSelector, } from '@/flavours/glitch/store'; import CloseIcon from '@/material-icons/400-24px/close.svg?react'; import type { BaseConfirmationModalProps } from './confirmation_modals/confirmation_modal'; const messages = defineMessages({ close: { id: 'lightbox.close', defaultMessage: 'Close' }, buttonTitle: { id: 'visibility_modal.button_title', defaultMessage: 'Set visibility', }, quotePublic: { id: 'visibility_modal.quote_public', defaultMessage: 'Anyone', }, quoteFollowers: { id: 'visibility_modal.quote_followers', defaultMessage: 'Followers only', }, quoteNobody: { id: 'visibility_modal.quote_nobody', defaultMessage: 'No one', }, }); interface VisibilityModalProps extends BaseConfirmationModalProps { statusId: string; } const selectStatusPolicy = createAppSelector( [(state) => state.statuses, (_state, statusId: string) => statusId], (statuses, statusId) => { const status = statuses.get(statusId); if (!status) { return 'public'; } const policy = (status.getIn(['quote_approval', 'automatic', 0]) as string) || 'nobody'; const visibility = status.get('visibility') as StatusVisibility; // If the status is private or direct, it cannot be quoted by anyone. if (visibility === 'private' || visibility === 'direct') { return 'nobody'; } // If the status has a specific quote policy, return it. if (isQuotePolicy(policy)) { return policy; } // Otherwise, return the default based on visibility. if (visibility === 'unlisted') { return 'followers'; } return 'public'; }, ); export const VisibilityModal: FC = forwardRef( // eslint-disable-next-line @typescript-eslint/no-unused-vars ({ onClose, statusId }, ref) => { const intl = useIntl(); const currentVisibility = useAppSelector( (state) => (state.statuses.getIn([statusId, 'visibility'], 'public') as | StatusVisibility | undefined) ?? 'public', ); const currentQuotePolicy = useAppSelector((state) => selectStatusPolicy(state, statusId), ); const disableQuotePolicy = currentVisibility === 'private' || currentVisibility === 'direct'; const isSaving = useAppSelector( (state) => state.statuses.getIn([statusId, 'isSavingQuotePolicy']) === true, ); const visibilityItems = useMemo[]>( () => [ { value: 'public', text: intl.formatMessage(privacyMessages.public_short), meta: intl.formatMessage(privacyMessages.public_long), }, { value: 'unlisted', text: intl.formatMessage(privacyMessages.unlisted_short), meta: intl.formatMessage(privacyMessages.unlisted_long), }, { value: 'private', text: intl.formatMessage(privacyMessages.private_short), meta: intl.formatMessage(privacyMessages.private_long), }, { value: 'direct', text: intl.formatMessage(privacyMessages.direct_short), meta: intl.formatMessage(privacyMessages.direct_long), }, ], [intl], ); const quoteItems = useMemo[]>( () => [ { value: 'public', text: intl.formatMessage(messages.quotePublic) }, { value: 'followers', text: intl.formatMessage(messages.quoteFollowers), }, { value: 'nobody', text: intl.formatMessage(messages.quoteNobody) }, ], [intl], ); const dispatch = useAppDispatch(); const handleVisibilityChange = useCallback( (value: string) => { // Published statuses cannot change visibility. if (statusId) { return; } dispatch(changeComposeVisibility(value)); }, [dispatch, statusId], ); const handleQuotePolicyChange = useCallback( (value: string) => { if (isQuotePolicy(value) && !disableQuotePolicy) { void dispatch(setStatusQuotePolicy({ policy: value, statusId })); } }, [disableQuotePolicy, dispatch, statusId], ); const privacyDropdownId = useId(); const quoteDropdownId = useId(); return (
{(chunks) => ( {chunks} )}
( {chunks} ), }} tagName='p' />
); }, ); VisibilityModal.displayName = 'VisibilityModal'; const QuotePolicyHelper: FC<{ policy: ApiQuotePolicy; visibility: StatusVisibility; }> = ({ policy, visibility }) => { if (visibility === 'unlisted' && policy !== 'nobody') { return (

); } if (visibility === 'private') { return (

); } if (visibility === 'direct') { return (

); } return null; };