2
0

Change display of blocked and muted quoted users (#36619)

This commit is contained in:
Claire
2025-10-29 10:13:12 +01:00
committed by GitHub
parent 4896d2c4c6
commit e437bb919f
5 changed files with 98 additions and 13 deletions

View File

@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useRef } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
@@ -83,6 +83,62 @@ const LimitedAccountHint: React.FC<{ accountId: string }> = ({ accountId }) => {
); );
}; };
const FilteredQuote: React.FC<{
reveal: VoidFunction;
quotedAccountId: string;
quoteState: string;
}> = ({ reveal, quotedAccountId, quoteState }) => {
const account = useAppSelector((state) =>
quotedAccountId ? state.accounts.get(quotedAccountId) : undefined,
);
const quoteAuthorName = account?.acct;
const domain = quoteAuthorName?.split('@')[1];
let message;
switch (quoteState) {
case 'blocked_account':
message = (
<FormattedMessage
id='status.quote_error.blocked_account_hint.title'
defaultMessage="This post is hidden because you've blocked @{name}."
values={{ name: quoteAuthorName }}
/>
);
break;
case 'blocked_domain':
message = (
<FormattedMessage
id='status.quote_error.blocked_domain_hint.title'
defaultMessage="This post is hidden because you've blocked {domain}."
values={{ domain }}
/>
);
break;
case 'muted_account':
message = (
<FormattedMessage
id='status.quote_error.muted_account_hint.title'
defaultMessage="This post is hidden because you've muted @{name}."
values={{ name: quoteAuthorName }}
/>
);
}
return (
<>
{message}
<button onClick={reveal} className='link-button'>
<FormattedMessage
id='status.quote_error.limited_account_hint.action'
defaultMessage='Show anyway'
/>
</button>
</>
);
};
interface QuotedStatusProps { interface QuotedStatusProps {
quote: QuoteMap; quote: QuoteMap;
contextType?: string; contextType?: string;
@@ -130,6 +186,11 @@ export const QuotedStatus: React.FC<QuotedStatusProps> = ({
const isLoaded = loadingState === 'complete'; const isLoaded = loadingState === 'complete';
const isFetchingQuoteRef = useRef(false); const isFetchingQuoteRef = useRef(false);
const [revealed, setRevealed] = useState(false);
const reveal = useCallback(() => {
setRevealed(true);
}, [setRevealed]);
useEffect(() => { useEffect(() => {
if (isLoaded) { if (isLoaded) {
@@ -189,6 +250,20 @@ export const QuotedStatus: React.FC<QuotedStatusProps> = ({
defaultMessage='Post removed by author' defaultMessage='Post removed by author'
/> />
); );
} else if (
(quoteState === 'blocked_account' ||
quoteState === 'blocked_domain' ||
quoteState === 'muted_account') &&
!revealed &&
accountId
) {
quoteError = (
<FilteredQuote
quoteState={quoteState}
reveal={reveal}
quotedAccountId={accountId}
/>
);
} else if ( } else if (
!status || !status ||
!quotedStatusId || !quotedStatusId ||

View File

@@ -911,9 +911,12 @@
"status.pin": "Pin on profile", "status.pin": "Pin on profile",
"status.quote": "Quote", "status.quote": "Quote",
"status.quote.cancel": "Cancel quote", "status.quote.cancel": "Cancel quote",
"status.quote_error.blocked_account_hint.title": "This post is hidden because you've blocked @{name}.",
"status.quote_error.blocked_domain_hint.title": "This post is hidden because you've blocked {domain}.",
"status.quote_error.filtered": "Hidden due to one of your filters", "status.quote_error.filtered": "Hidden due to one of your filters",
"status.quote_error.limited_account_hint.action": "Show anyway", "status.quote_error.limited_account_hint.action": "Show anyway",
"status.quote_error.limited_account_hint.title": "This account has been hidden by the moderators of {domain}.", "status.quote_error.limited_account_hint.title": "This account has been hidden by the moderators of {domain}.",
"status.quote_error.muted_account_hint.title": "This post is hidden because you've muted @{name}.",
"status.quote_error.not_available": "Post unavailable", "status.quote_error.not_available": "Post unavailable",
"status.quote_error.pending_approval": "Post pending", "status.quote_error.pending_approval": "Post pending",
"status.quote_error.pending_approval_popout.body": "On Mastodon, you can control whether someone can quote you. This post is pending while we're getting the original author's approval.", "status.quote_error.pending_approval_popout.body": "On Mastodon, you can control whether someone can quote you. This post is pending while we're getting the original author's approval.",

View File

@@ -98,12 +98,12 @@ class StatusCacheHydrator
if quote.quoted_status.nil? if quote.quoted_status.nil?
payload[nested ? :quoted_status_id : :quoted_status] = nil payload[nested ? :quoted_status_id : :quoted_status] = nil
payload[:state] = 'deleted' payload[:state] = 'deleted'
elsif StatusFilter.new(quote.quoted_status, Account.find_by(id: account_id)).filtered_for_quote?
payload[nested ? :quoted_status_id : :quoted_status] = nil
payload[:state] = 'unauthorized'
else else
payload[:state] = 'accepted' filter_state = StatusFilter.new(quote.quoted_status, Account.find_by(id: account_id)).filter_state_for_quote
if nested payload[:state] = filter_state || 'accepted'
if filter_state == 'unauthorized'
payload[nested ? :quoted_status_id : :quoted_status] = nil
elsif nested
payload[:quoted_status_id] = quote.quoted_status_id&.to_s payload[:quoted_status_id] = quote.quoted_status_id&.to_s
else else
payload[:quoted_status] = StatusCacheHydrator.new(quote.quoted_status).hydrate(account_id, nested: true) payload[:quoted_status] = StatusCacheHydrator.new(quote.quoted_status).hydrate(account_id, nested: true)

View File

@@ -15,10 +15,18 @@ class StatusFilter
blocked_by_policy? || (account_present? && filtered_status?) || silenced_account? blocked_by_policy? || (account_present? && filtered_status?) || silenced_account?
end end
def filtered_for_quote? def filter_state_for_quote
return false if !account.nil? && account.id == status.account_id if !account.nil? && account.id == status.account_id
nil
blocked_by_policy? || (account_present? && filtered_status?) elsif blocked_by_policy?
'unauthorized'
elsif account_present? && blocking_domain?
'blocked_domain'
elsif account_present? && blocking_account?
'blocked_account'
elsif account_present? && muting_account?
'muted_account'
end
end end
private private

View File

@@ -8,13 +8,12 @@ class REST::BaseQuoteSerializer < ActiveModel::Serializer
# Extra states when a status is unavailable # Extra states when a status is unavailable
return 'deleted' if object.quoted_status.nil? return 'deleted' if object.quoted_status.nil?
return 'unauthorized' if status_filter.filtered_for_quote?
object.state status_filter.filter_state_for_quote || object.state
end end
def quoted_status def quoted_status
object.quoted_status if object.accepted? && object.quoted_status.present? && !object.quoted_status&.reblog? && !status_filter.filtered_for_quote? object.quoted_status if object.accepted? && object.quoted_status.present? && !object.quoted_status&.reblog? && status_filter.filter_state_for_quote != 'unauthorized'
end end
private private