From a13756148d353c7479f68e65a210f6d88d26c785 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Fri, 23 May 2025 17:26:06 +0200 Subject: [PATCH] fix: Show hint for quotes hidden by filter (#34790) --- .../mastodon/components/status_quoted.tsx | 34 ++++++++++++++++--- app/javascript/mastodon/locales/en.json | 1 + 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/app/javascript/mastodon/components/status_quoted.tsx b/app/javascript/mastodon/components/status_quoted.tsx index 5b840b441c..c99f63704e 100644 --- a/app/javascript/mastodon/components/status_quoted.tsx +++ b/app/javascript/mastodon/components/status_quoted.tsx @@ -1,3 +1,5 @@ +import { useMemo } from 'react'; + import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; @@ -10,9 +12,11 @@ import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react' import { Icon } from 'mastodon/components/icon'; import StatusContainer from 'mastodon/containers/status_container'; import type { Status } from 'mastodon/models/status'; +import type { RootState } from 'mastodon/store'; import { useAppSelector } from 'mastodon/store'; import QuoteIcon from '../../images/quote.svg?react'; +import { makeGetStatus } from '../selectors'; const MAX_QUOTE_POSTS_NESTING_LEVEL = 1; @@ -65,6 +69,10 @@ const QuoteLink: React.FC<{ }; type QuoteMap = ImmutableMap<'state' | 'quoted_status', string | null>; +type GetStatusSelector = ( + state: RootState, + props: { id?: string | null; contextType?: string }, +) => Status | null; export const QuotedStatus: React.FC<{ quote: QuoteMap; @@ -73,34 +81,50 @@ export const QuotedStatus: React.FC<{ nestingLevel?: number; }> = ({ quote, contextType, nestingLevel = 1, variant = 'full' }) => { const quotedStatusId = quote.get('quoted_status'); - const state = quote.get('state'); + const quoteState = quote.get('state'); const status = useAppSelector((state) => quotedStatusId ? state.statuses.get(quotedStatusId) : undefined, ); let quoteError: React.ReactNode = null; - if (state === 'deleted') { + // In order to find out whether the quoted post should be completely hidden + // due to a matching filter, we run it through the selector used by `status_container`. + // If this returns null even though `status` exists, it's because it's filtered. + const getStatus = useMemo(() => makeGetStatus(), []) as GetStatusSelector; + const statusWithExtraData = useAppSelector((state) => + getStatus(state, { id: quotedStatusId, contextType }), + ); + const isFilteredAndHidden = status && statusWithExtraData === null; + + if (isFilteredAndHidden) { + quoteError = ( + + ); + } else if (quoteState === 'deleted') { quoteError = ( ); - } else if (state === 'unauthorized') { + } else if (quoteState === 'unauthorized') { quoteError = ( ); - } else if (state === 'pending') { + } else if (quoteState === 'pending') { quoteError = ( ); - } else if (state === 'rejected' || state === 'revoked') { + } else if (quoteState === 'rejected' || quoteState === 'revoked') { quoteError = (