Fix error alerts for deleted quotes (#35918)
This commit is contained in:
		@@ -3,7 +3,7 @@ import { browserHistory } from 'mastodon/components/router';
 | 
				
			|||||||
import api from '../api';
 | 
					import api from '../api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ensureComposeIsVisible, setComposeToStatus } from './compose';
 | 
					import { ensureComposeIsVisible, setComposeToStatus } from './compose';
 | 
				
			||||||
import { importFetchedStatus, importFetchedStatuses, importFetchedAccount } from './importer';
 | 
					import { importFetchedStatus, importFetchedAccount } from './importer';
 | 
				
			||||||
import { fetchContext } from './statuses_typed';
 | 
					import { fetchContext } from './statuses_typed';
 | 
				
			||||||
import { deleteFromTimelines } from './timelines';
 | 
					import { deleteFromTimelines } from './timelines';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -48,7 +48,18 @@ export function fetchStatusRequest(id, skipLoading) {
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function fetchStatus(id, forceFetch = false, alsoFetchContext = true) {
 | 
					/**
 | 
				
			||||||
 | 
					 * @param {string} id
 | 
				
			||||||
 | 
					 * @param {Object} [options]
 | 
				
			||||||
 | 
					 * @param {boolean} [options.forceFetch]
 | 
				
			||||||
 | 
					 * @param {boolean} [options.alsoFetchContext]
 | 
				
			||||||
 | 
					 * @param {string | null | undefined} [options.parentQuotePostId]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function fetchStatus(id, {
 | 
				
			||||||
 | 
					  forceFetch = false,
 | 
				
			||||||
 | 
					  alsoFetchContext = true,
 | 
				
			||||||
 | 
					  parentQuotePostId,
 | 
				
			||||||
 | 
					} = {}) {
 | 
				
			||||||
  return (dispatch, getState) => {
 | 
					  return (dispatch, getState) => {
 | 
				
			||||||
    const skipLoading = !forceFetch && getState().getIn(['statuses', id], null) !== null;
 | 
					    const skipLoading = !forceFetch && getState().getIn(['statuses', id], null) !== null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -66,7 +77,7 @@ export function fetchStatus(id, forceFetch = false, alsoFetchContext = true) {
 | 
				
			|||||||
      dispatch(importFetchedStatus(response.data));
 | 
					      dispatch(importFetchedStatus(response.data));
 | 
				
			||||||
      dispatch(fetchStatusSuccess(skipLoading));
 | 
					      dispatch(fetchStatusSuccess(skipLoading));
 | 
				
			||||||
    }).catch(error => {
 | 
					    }).catch(error => {
 | 
				
			||||||
      dispatch(fetchStatusFail(id, error, skipLoading));
 | 
					      dispatch(fetchStatusFail(id, error, skipLoading, parentQuotePostId));
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -78,11 +89,12 @@ export function fetchStatusSuccess(skipLoading) {
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function fetchStatusFail(id, error, skipLoading) {
 | 
					export function fetchStatusFail(id, error, skipLoading, parentQuotePostId) {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    type: STATUS_FETCH_FAIL,
 | 
					    type: STATUS_FETCH_FAIL,
 | 
				
			||||||
    id,
 | 
					    id,
 | 
				
			||||||
    error,
 | 
					    error,
 | 
				
			||||||
 | 
					    parentQuotePostId,
 | 
				
			||||||
    skipLoading,
 | 
					    skipLoading,
 | 
				
			||||||
    skipAlert: true,
 | 
					    skipAlert: true,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,9 +37,7 @@ const QuoteWrapper: React.FC<{
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const NestedQuoteLink: React.FC<{
 | 
					const NestedQuoteLink: React.FC<{ status: Status }> = ({ status }) => {
 | 
				
			||||||
  status: Status;
 | 
					 | 
				
			||||||
}> = ({ status }) => {
 | 
					 | 
				
			||||||
  const accountId = status.get('account') as string;
 | 
					  const accountId = status.get('account') as string;
 | 
				
			||||||
  const account = useAppSelector((state) =>
 | 
					  const account = useAppSelector((state) =>
 | 
				
			||||||
    accountId ? state.accounts.get(accountId) : undefined,
 | 
					    accountId ? state.accounts.get(accountId) : undefined,
 | 
				
			||||||
@@ -78,22 +76,40 @@ type GetStatusSelector = (
 | 
				
			|||||||
export const QuotedStatus: React.FC<{
 | 
					export const QuotedStatus: React.FC<{
 | 
				
			||||||
  quote: QuoteMap;
 | 
					  quote: QuoteMap;
 | 
				
			||||||
  contextType?: string;
 | 
					  contextType?: string;
 | 
				
			||||||
 | 
					  parentQuotePostId?: string | null;
 | 
				
			||||||
  variant?: 'full' | 'link';
 | 
					  variant?: 'full' | 'link';
 | 
				
			||||||
  nestingLevel?: number;
 | 
					  nestingLevel?: number;
 | 
				
			||||||
}> = ({ quote, contextType, nestingLevel = 1, variant = 'full' }) => {
 | 
					}> = ({
 | 
				
			||||||
 | 
					  quote,
 | 
				
			||||||
 | 
					  contextType,
 | 
				
			||||||
 | 
					  parentQuotePostId,
 | 
				
			||||||
 | 
					  nestingLevel = 1,
 | 
				
			||||||
 | 
					  variant = 'full',
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
  const dispatch = useAppDispatch();
 | 
					  const dispatch = useAppDispatch();
 | 
				
			||||||
 | 
					  const quoteState = useAppSelector((state) =>
 | 
				
			||||||
 | 
					    parentQuotePostId
 | 
				
			||||||
 | 
					      ? state.statuses.getIn([parentQuotePostId, 'quote', 'state'])
 | 
				
			||||||
 | 
					      : quote.get('state'),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const quotedStatusId = quote.get('quoted_status');
 | 
					  const quotedStatusId = quote.get('quoted_status');
 | 
				
			||||||
  const quoteState = quote.get('state');
 | 
					 | 
				
			||||||
  const status = useAppSelector((state) =>
 | 
					  const status = useAppSelector((state) =>
 | 
				
			||||||
    quotedStatusId ? state.statuses.get(quotedStatusId) : undefined,
 | 
					    quotedStatusId ? state.statuses.get(quotedStatusId) : undefined,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  const isQuoteLoaded = !!status && !status.get('isLoading');
 | 
					
 | 
				
			||||||
 | 
					  const shouldLoadQuote = !status?.get('isLoading') && quoteState !== 'deleted';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (!isQuoteLoaded && quotedStatusId) {
 | 
					    if (shouldLoadQuote && quotedStatusId) {
 | 
				
			||||||
      dispatch(fetchStatus(quotedStatusId));
 | 
					      dispatch(
 | 
				
			||||||
 | 
					        fetchStatus(quotedStatusId, {
 | 
				
			||||||
 | 
					          parentQuotePostId,
 | 
				
			||||||
 | 
					          alsoFetchContext: false,
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [isQuoteLoaded, quotedStatusId, dispatch]);
 | 
					  }, [shouldLoadQuote, quotedStatusId, parentQuotePostId, dispatch]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // In order to find out whether the quoted post should be completely hidden
 | 
					  // 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`.
 | 
					  // due to a matching filter, we run it through the selector used by `status_container`.
 | 
				
			||||||
@@ -174,6 +190,7 @@ export const QuotedStatus: React.FC<{
 | 
				
			|||||||
        {canRenderChildQuote && (
 | 
					        {canRenderChildQuote && (
 | 
				
			||||||
          <QuotedStatus
 | 
					          <QuotedStatus
 | 
				
			||||||
            quote={childQuote}
 | 
					            quote={childQuote}
 | 
				
			||||||
 | 
					            parentQuotePostId={quotedStatusId}
 | 
				
			||||||
            contextType={contextType}
 | 
					            contextType={contextType}
 | 
				
			||||||
            variant={
 | 
					            variant={
 | 
				
			||||||
              nestingLevel === MAX_QUOTE_POSTS_NESTING_LEVEL ? 'link' : 'full'
 | 
					              nestingLevel === MAX_QUOTE_POSTS_NESTING_LEVEL ? 'link' : 'full'
 | 
				
			||||||
@@ -209,7 +226,11 @@ export const StatusQuoteManager = (props: StatusQuoteManagerProps) => {
 | 
				
			|||||||
  if (quote) {
 | 
					  if (quote) {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <StatusContainer {...props}>
 | 
					      <StatusContainer {...props}>
 | 
				
			||||||
        <QuotedStatus quote={quote} contextType={props.contextType} />
 | 
					        <QuotedStatus
 | 
				
			||||||
 | 
					          quote={quote}
 | 
				
			||||||
 | 
					          parentQuotePostId={status?.get('id') as string}
 | 
				
			||||||
 | 
					          contextType={props.contextType}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
      </StatusContainer>
 | 
					      </StatusContainer>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,7 @@ const Embed: React.FC<{ id: string }> = ({ id }) => {
 | 
				
			|||||||
  const dispatchRenderSignal = useRenderSignal();
 | 
					  const dispatchRenderSignal = useRenderSignal();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    dispatch(fetchStatus(id, false, false));
 | 
					    dispatch(fetchStatus(id, { alsoFetchContext: false }));
 | 
				
			||||||
  }, [dispatch, id]);
 | 
					  }, [dispatch, id]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleToggleHidden = useCallback(() => {
 | 
					  const handleToggleHidden = useCallback(() => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -381,7 +381,10 @@ export const DetailedStatus: React.FC<{
 | 
				
			|||||||
            {hashtagBar}
 | 
					            {hashtagBar}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            {status.get('quote') && (
 | 
					            {status.get('quote') && (
 | 
				
			||||||
              <QuotedStatus quote={status.get('quote')} />
 | 
					              <QuotedStatus
 | 
				
			||||||
 | 
					                quote={status.get('quote')}
 | 
				
			||||||
 | 
					                parentQuotePostId={status.get('id')}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
            )}
 | 
					            )}
 | 
				
			||||||
          </>
 | 
					          </>
 | 
				
			||||||
        )}
 | 
					        )}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@ class FilterModal extends ImmutablePureComponent {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  handleSuccess = () => {
 | 
					  handleSuccess = () => {
 | 
				
			||||||
    const { dispatch, statusId } = this.props;
 | 
					    const { dispatch, statusId } = this.props;
 | 
				
			||||||
    dispatch(fetchStatus(statusId, true));
 | 
					    dispatch(fetchStatus(statusId, {forceFetch: true}));
 | 
				
			||||||
    this.setState({ isSubmitting: false, isSubmitted: true, step: 'submitted' });
 | 
					    this.setState({ isSubmitting: false, isSubmitted: true, step: 'submitted' });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,8 +73,15 @@ export default function statuses(state = initialState, action) {
 | 
				
			|||||||
  switch(action.type) {
 | 
					  switch(action.type) {
 | 
				
			||||||
  case STATUS_FETCH_REQUEST:
 | 
					  case STATUS_FETCH_REQUEST:
 | 
				
			||||||
    return state.setIn([action.id, 'isLoading'], true);
 | 
					    return state.setIn([action.id, 'isLoading'], true);
 | 
				
			||||||
  case STATUS_FETCH_FAIL:
 | 
					  case STATUS_FETCH_FAIL: {
 | 
				
			||||||
    return state.delete(action.id);
 | 
					    if (action.parentQuotePostId && action.error.status === 404) {
 | 
				
			||||||
 | 
					      return state
 | 
				
			||||||
 | 
					        .delete(action.id)
 | 
				
			||||||
 | 
					        .setIn([action.parentQuotePostId, 'quote', 'state'], 'deleted')
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return state.delete(action.id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  case STATUS_IMPORT:
 | 
					  case STATUS_IMPORT:
 | 
				
			||||||
    return importStatus(state, action.status);
 | 
					    return importStatus(state, action.status);
 | 
				
			||||||
  case STATUSES_IMPORT:
 | 
					  case STATUSES_IMPORT:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user