Allow removing deleted quotes from composer (#36080)

This commit is contained in:
diondiondion 2025-09-11 16:44:20 +02:00 committed by GitHub
commit 84d1ba980b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 140 additions and 66 deletions

View file

@ -1,4 +1,4 @@
import { useEffect, useMemo } from 'react';
import { useEffect, useMemo, useRef } from 'react';
import { FormattedMessage } from 'react-intl';
@ -13,7 +13,9 @@ import type { RootState } from 'mastodon/store';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
import { fetchStatus } from '../actions/statuses';
import { makeGetStatus } from '../selectors';
import { makeGetStatusWithExtraInfo } from '../selectors';
import { Button } from './button';
const MAX_QUOTE_POSTS_NESTING_LEVEL = 1;
@ -55,11 +57,15 @@ const NestedQuoteLink: React.FC<{ status: Status }> = ({ status }) => {
);
};
type QuoteMap = ImmutableMap<'state' | 'quoted_status', string | null>;
type GetStatusSelector = (
state: RootState,
props: { id?: string | null; contextType?: string },
) => Status | null;
) => {
status: Status | null;
loadingState: 'not-found' | 'loading' | 'filtered' | 'complete';
};
type QuoteMap = ImmutableMap<'state' | 'quoted_status', string | null>;
interface QuotedStatusProps {
quote: QuoteMap;
@ -86,31 +92,41 @@ export const QuotedStatus: React.FC<QuotedStatusProps> = ({
);
const quotedStatusId = quote.get('quoted_status');
const status = useAppSelector((state) =>
quotedStatusId ? state.statuses.get(quotedStatusId) : undefined,
const getStatusSelector = useMemo(
() => makeGetStatusWithExtraInfo() as GetStatusSelector,
[],
);
const { status, loadingState } = useAppSelector((state) =>
getStatusSelector(state, { id: quotedStatusId, contextType }),
);
const shouldLoadQuote = !status?.get('isLoading') && quoteState !== 'deleted';
const shouldFetchQuote =
!status?.get('isLoading') &&
quoteState !== 'deleted' &&
loadingState === 'not-found';
const isLoaded = loadingState === 'complete';
const isFetchingQuoteRef = useRef(false);
useEffect(() => {
if (shouldLoadQuote && quotedStatusId) {
if (isLoaded) {
isFetchingQuoteRef.current = false;
}
}, [isLoaded]);
useEffect(() => {
if (shouldFetchQuote && quotedStatusId && !isFetchingQuoteRef.current) {
dispatch(
fetchStatus(quotedStatusId, {
parentQuotePostId,
alsoFetchContext: false,
}),
);
isFetchingQuoteRef.current = true;
}
}, [shouldLoadQuote, quotedStatusId, parentQuotePostId, dispatch]);
}, [shouldFetchQuote, quotedStatusId, parentQuotePostId, dispatch]);
// 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;
const isFilteredAndHidden = loadingState === 'filtered';
let quoteError: React.ReactNode = null;
@ -154,10 +170,20 @@ export const QuotedStatus: React.FC<QuotedStatusProps> = ({
quoteState === 'unauthorized'
) {
quoteError = (
<FormattedMessage
id='status.quote_error.not_available'
defaultMessage='Post unavailable'
/>
<>
<FormattedMessage
id='status.quote_error.not_available'
defaultMessage='Post unavailable'
/>
{contextType === 'composer' && (
<Button compact plain onClick={onQuoteCancel}>
<FormattedMessage
id='status.remove_quote'
defaultMessage='Remove'
/>
</Button>
)}
</>
);
}