diff --git a/app/javascript/mastodon/actions/importer/index.js b/app/javascript/mastodon/actions/importer/index.js index 5854482dc..6a85231bb 100644 --- a/app/javascript/mastodon/actions/importer/index.js +++ b/app/javascript/mastodon/actions/importer/index.js @@ -46,11 +46,11 @@ export function importFetchedAccounts(accounts) { return importAccounts({ accounts: normalAccounts }); } -export function importFetchedStatus(status) { - return importFetchedStatuses([status]); +export function importFetchedStatus(status, options = {}) { + return importFetchedStatuses([status], options); } -export function importFetchedStatuses(statuses) { +export function importFetchedStatuses(statuses, options = {}) { return (dispatch, getState) => { const accounts = []; const normalStatuses = []; @@ -58,7 +58,7 @@ export function importFetchedStatuses(statuses) { const filters = []; function processStatus(status) { - pushUnique(normalStatuses, normalizeStatus(status, getState().getIn(['statuses', status.id]))); + pushUnique(normalStatuses, normalizeStatus(status, getState().getIn(['statuses', status.id]), options)); pushUnique(accounts, status.account); if (status.filtered) { diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index 32c3d7666..12c02dd4d 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -27,9 +27,12 @@ function stripQuoteFallback(text) { return wrapper.innerHTML; } -export function normalizeStatus(status, normalOldStatus) { +export function normalizeStatus(status, normalOldStatus, { bogusQuotePolicy = false }) { const normalStatus = { ...status }; + if (bogusQuotePolicy) + normalStatus.quote_approval = null; + normalStatus.account = status.account.id; if (status.reblog && status.reblog.id) { @@ -109,6 +112,8 @@ export function normalizeStatus(status, normalOldStatus) { } if (normalOldStatus) { + normalStatus.quote_approval ||= normalOldStatus.quote_approval; + const list = normalOldStatus.get('media_attachments'); if (normalStatus.media_attachments && list) { normalStatus.media_attachments.forEach(item => { diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js index 7572efe95..5602fcadb 100644 --- a/app/javascript/mastodon/actions/statuses.js +++ b/app/javascript/mastodon/actions/statuses.js @@ -203,8 +203,8 @@ export function deleteStatusFail(id, error) { }; } -export const updateStatus = status => dispatch => - dispatch(importFetchedStatus(status)); +export const updateStatus = (status, { bogusQuotePolicy }) => dispatch => + dispatch(importFetchedStatus(status, { bogusQuotePolicy })); export function muteStatus(id) { return (dispatch) => { diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js index 4299bad5c..34d094f51 100644 --- a/app/javascript/mastodon/actions/streaming.js +++ b/app/javascript/mastodon/actions/streaming.js @@ -52,6 +52,9 @@ const randomUpTo = max => export const connectTimelineStream = (timelineId, channelName, params = {}, options = {}) => { const { messages } = getLocale(); + // Public streams are currently not returning personalized quote policies + const bogusQuotePolicy = channelName.startsWith('public') || channelName.startsWith('hashtag'); + return connectStream(channelName, params, (dispatch, getState) => { // @ts-ignore const locale = getState().getIn(['meta', 'locale']); @@ -97,11 +100,11 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti switch (data.event) { case 'update': // @ts-expect-error - dispatch(updateTimeline(timelineId, JSON.parse(data.payload), options.accept)); + dispatch(updateTimeline(timelineId, JSON.parse(data.payload), { accept: options.accept, bogusQuotePolicy })); break; case 'status.update': // @ts-expect-error - dispatch(updateStatus(JSON.parse(data.payload))); + dispatch(updateStatus(JSON.parse(data.payload), { bogusQuotePolicy })); break; case 'delete': dispatch(deleteFromTimelines(data.payload)); diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js index 65b6d8045..f45090f96 100644 --- a/app/javascript/mastodon/actions/timelines.js +++ b/app/javascript/mastodon/actions/timelines.js @@ -32,7 +32,7 @@ export const loadPending = timeline => ({ timeline, }); -export function updateTimeline(timeline, status, accept) { +export function updateTimeline(timeline, status, { accept = undefined, bogusQuotePolicy = false }) { return (dispatch, getState) => { if (typeof accept === 'function' && !accept(status)) { return; @@ -45,7 +45,7 @@ export function updateTimeline(timeline, status, accept) { return; } - dispatch(importFetchedStatus(status)); + dispatch(importFetchedStatus(status, { bogusQuotePolicy })); dispatch({ type: TIMELINE_UPDATE, diff --git a/app/javascript/mastodon/components/dropdown_menu.tsx b/app/javascript/mastodon/components/dropdown_menu.tsx index 2a97313f5..ced7bc9ce 100644 --- a/app/javascript/mastodon/components/dropdown_menu.tsx +++ b/app/javascript/mastodon/components/dropdown_menu.tsx @@ -26,6 +26,7 @@ import { closeDropdownMenu, } from 'mastodon/actions/dropdown_menu'; import { openModal, closeModal } from 'mastodon/actions/modal'; +import { fetchStatus } from 'mastodon/actions/statuses'; import { CircularProgress } from 'mastodon/components/circular_progress'; import { isUserTouching } from 'mastodon/is_mobile'; import { @@ -302,6 +303,7 @@ interface DropdownProps { */ scrollKey?: string; status?: ImmutableMap; + needsStatusRefresh?: boolean; forceDropdown?: boolean; renderItem?: RenderItemFn; renderHeader?: RenderHeaderFn; @@ -325,6 +327,7 @@ export const Dropdown = ({ placement = 'bottom', offset = [5, 5], status, + needsStatusRefresh, forceDropdown = false, renderItem, renderHeader, @@ -344,6 +347,7 @@ export const Dropdown = ({ const prefetchAccountId = status ? status.getIn(['account', 'id']) : undefined; + const statusId = status?.get('id') as string | undefined; const handleClose = useCallback(() => { if (buttonRef.current) { @@ -408,6 +412,15 @@ export const Dropdown = ({ dispatch(fetchRelationships([prefetchAccountId])); } + if (needsStatusRefresh && statusId) { + dispatch( + fetchStatus(statusId, { + forceFetch: true, + alsoFetchContext: false, + }), + ); + } + if (isUserTouching() && !forceDropdown) { dispatch( openModal({ @@ -441,6 +454,8 @@ export const Dropdown = ({ items, forceDropdown, handleClose, + statusId, + needsStatusRefresh, ], ); diff --git a/app/javascript/mastodon/components/status/boost_button.tsx b/app/javascript/mastodon/components/status/boost_button.tsx index 21c210b9e..b38e8db16 100644 --- a/app/javascript/mastodon/components/status/boost_button.tsx +++ b/app/javascript/mastodon/components/status/boost_button.tsx @@ -8,6 +8,7 @@ import classNames from 'classnames'; import { quoteComposeById } from '@/mastodon/actions/compose_typed'; import { toggleReblog } from '@/mastodon/actions/interactions'; import { openModal } from '@/mastodon/actions/modal'; +import { fetchStatus } from '@/mastodon/actions/statuses'; import { quickBoosting } from '@/mastodon/initial_state'; import type { ActionMenuItem } from '@/mastodon/models/dropdown_menu'; import type { Status } from '@/mastodon/models/status'; @@ -111,6 +112,7 @@ const BoostOrQuoteMenu: FC = ({ status, counters }) => { const statusId = status.get('id') as string; const wasBoosted = !!status.get('reblogged'); + const quoteApproval = status.get('quote_approval'); const showLoginPrompt = useCallback(() => { dispatch( @@ -167,9 +169,16 @@ const BoostOrQuoteMenu: FC = ({ status, counters }) => { dispatch(toggleReblog(status.get('id'), true)); return false; } + + if (quoteApproval === null) { + dispatch( + fetchStatus(statusId, { forceFetch: true, alsoFetchContext: false }), + ); + } + return true; }, - [dispatch, isLoggedIn, showLoginPrompt, status], + [dispatch, isLoggedIn, showLoginPrompt, status, quoteApproval, statusId], ); return ( diff --git a/app/javascript/mastodon/components/status_action_bar/index.jsx b/app/javascript/mastodon/components/status_action_bar/index.jsx index 91b9e05b1..ba184f575 100644 --- a/app/javascript/mastodon/components/status_action_bar/index.jsx +++ b/app/javascript/mastodon/components/status_action_bar/index.jsx @@ -404,6 +404,7 @@ class StatusActionBar extends ImmutablePureComponent {