Fix posts coming from public/hashtag streaming being marked as unquotable (#36860)
This commit is contained in:
@@ -46,11 +46,11 @@ export function importFetchedAccounts(accounts) {
|
|||||||
return importAccounts({ accounts: normalAccounts });
|
return importAccounts({ accounts: normalAccounts });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function importFetchedStatus(status) {
|
export function importFetchedStatus(status, options = {}) {
|
||||||
return importFetchedStatuses([status]);
|
return importFetchedStatuses([status], options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function importFetchedStatuses(statuses) {
|
export function importFetchedStatuses(statuses, options = {}) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const accounts = [];
|
const accounts = [];
|
||||||
const normalStatuses = [];
|
const normalStatuses = [];
|
||||||
@@ -58,7 +58,7 @@ export function importFetchedStatuses(statuses) {
|
|||||||
const filters = [];
|
const filters = [];
|
||||||
|
|
||||||
function processStatus(status) {
|
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);
|
pushUnique(accounts, status.account);
|
||||||
|
|
||||||
if (status.filtered) {
|
if (status.filtered) {
|
||||||
|
|||||||
@@ -27,9 +27,12 @@ function stripQuoteFallback(text) {
|
|||||||
return wrapper.innerHTML;
|
return wrapper.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeStatus(status, normalOldStatus) {
|
export function normalizeStatus(status, normalOldStatus, { bogusQuotePolicy = false }) {
|
||||||
const normalStatus = { ...status };
|
const normalStatus = { ...status };
|
||||||
|
|
||||||
|
if (bogusQuotePolicy)
|
||||||
|
normalStatus.quote_approval = null;
|
||||||
|
|
||||||
normalStatus.account = status.account.id;
|
normalStatus.account = status.account.id;
|
||||||
|
|
||||||
if (status.reblog && status.reblog.id) {
|
if (status.reblog && status.reblog.id) {
|
||||||
@@ -109,6 +112,8 @@ export function normalizeStatus(status, normalOldStatus) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (normalOldStatus) {
|
if (normalOldStatus) {
|
||||||
|
normalStatus.quote_approval ||= normalOldStatus.quote_approval;
|
||||||
|
|
||||||
const list = normalOldStatus.get('media_attachments');
|
const list = normalOldStatus.get('media_attachments');
|
||||||
if (normalStatus.media_attachments && list) {
|
if (normalStatus.media_attachments && list) {
|
||||||
normalStatus.media_attachments.forEach(item => {
|
normalStatus.media_attachments.forEach(item => {
|
||||||
|
|||||||
@@ -203,8 +203,8 @@ export function deleteStatusFail(id, error) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateStatus = status => dispatch =>
|
export const updateStatus = (status, { bogusQuotePolicy }) => dispatch =>
|
||||||
dispatch(importFetchedStatus(status));
|
dispatch(importFetchedStatus(status, { bogusQuotePolicy }));
|
||||||
|
|
||||||
export function muteStatus(id) {
|
export function muteStatus(id) {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ const randomUpTo = max =>
|
|||||||
export const connectTimelineStream = (timelineId, channelName, params = {}, options = {}) => {
|
export const connectTimelineStream = (timelineId, channelName, params = {}, options = {}) => {
|
||||||
const { messages } = getLocale();
|
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) => {
|
return connectStream(channelName, params, (dispatch, getState) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const locale = getState().getIn(['meta', 'locale']);
|
const locale = getState().getIn(['meta', 'locale']);
|
||||||
@@ -97,11 +100,11 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
|
|||||||
switch (data.event) {
|
switch (data.event) {
|
||||||
case 'update':
|
case 'update':
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
dispatch(updateTimeline(timelineId, JSON.parse(data.payload), options.accept));
|
dispatch(updateTimeline(timelineId, JSON.parse(data.payload), { accept: options.accept, bogusQuotePolicy }));
|
||||||
break;
|
break;
|
||||||
case 'status.update':
|
case 'status.update':
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
dispatch(updateStatus(JSON.parse(data.payload)));
|
dispatch(updateStatus(JSON.parse(data.payload), { bogusQuotePolicy }));
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
dispatch(deleteFromTimelines(data.payload));
|
dispatch(deleteFromTimelines(data.payload));
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const loadPending = timeline => ({
|
|||||||
timeline,
|
timeline,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function updateTimeline(timeline, status, accept) {
|
export function updateTimeline(timeline, status, { accept = undefined, bogusQuotePolicy = false }) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
if (typeof accept === 'function' && !accept(status)) {
|
if (typeof accept === 'function' && !accept(status)) {
|
||||||
return;
|
return;
|
||||||
@@ -45,7 +45,7 @@ export function updateTimeline(timeline, status, accept) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(importFetchedStatus(status));
|
dispatch(importFetchedStatus(status, { bogusQuotePolicy }));
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TIMELINE_UPDATE,
|
type: TIMELINE_UPDATE,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
closeDropdownMenu,
|
closeDropdownMenu,
|
||||||
} from 'mastodon/actions/dropdown_menu';
|
} from 'mastodon/actions/dropdown_menu';
|
||||||
import { openModal, closeModal } from 'mastodon/actions/modal';
|
import { openModal, closeModal } from 'mastodon/actions/modal';
|
||||||
|
import { fetchStatus } from 'mastodon/actions/statuses';
|
||||||
import { CircularProgress } from 'mastodon/components/circular_progress';
|
import { CircularProgress } from 'mastodon/components/circular_progress';
|
||||||
import { isUserTouching } from 'mastodon/is_mobile';
|
import { isUserTouching } from 'mastodon/is_mobile';
|
||||||
import {
|
import {
|
||||||
@@ -302,6 +303,7 @@ interface DropdownProps<Item extends object | null = MenuItem> {
|
|||||||
*/
|
*/
|
||||||
scrollKey?: string;
|
scrollKey?: string;
|
||||||
status?: ImmutableMap<string, unknown>;
|
status?: ImmutableMap<string, unknown>;
|
||||||
|
needsStatusRefresh?: boolean;
|
||||||
forceDropdown?: boolean;
|
forceDropdown?: boolean;
|
||||||
renderItem?: RenderItemFn<Item>;
|
renderItem?: RenderItemFn<Item>;
|
||||||
renderHeader?: RenderHeaderFn<Item>;
|
renderHeader?: RenderHeaderFn<Item>;
|
||||||
@@ -325,6 +327,7 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||||||
placement = 'bottom',
|
placement = 'bottom',
|
||||||
offset = [5, 5],
|
offset = [5, 5],
|
||||||
status,
|
status,
|
||||||
|
needsStatusRefresh,
|
||||||
forceDropdown = false,
|
forceDropdown = false,
|
||||||
renderItem,
|
renderItem,
|
||||||
renderHeader,
|
renderHeader,
|
||||||
@@ -344,6 +347,7 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||||||
const prefetchAccountId = status
|
const prefetchAccountId = status
|
||||||
? status.getIn(['account', 'id'])
|
? status.getIn(['account', 'id'])
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const statusId = status?.get('id') as string | undefined;
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
if (buttonRef.current) {
|
if (buttonRef.current) {
|
||||||
@@ -408,6 +412,15 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||||||
dispatch(fetchRelationships([prefetchAccountId]));
|
dispatch(fetchRelationships([prefetchAccountId]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needsStatusRefresh && statusId) {
|
||||||
|
dispatch(
|
||||||
|
fetchStatus(statusId, {
|
||||||
|
forceFetch: true,
|
||||||
|
alsoFetchContext: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isUserTouching() && !forceDropdown) {
|
if (isUserTouching() && !forceDropdown) {
|
||||||
dispatch(
|
dispatch(
|
||||||
openModal({
|
openModal({
|
||||||
@@ -441,6 +454,8 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||||||
items,
|
items,
|
||||||
forceDropdown,
|
forceDropdown,
|
||||||
handleClose,
|
handleClose,
|
||||||
|
statusId,
|
||||||
|
needsStatusRefresh,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import classNames from 'classnames';
|
|||||||
import { quoteComposeById } from '@/mastodon/actions/compose_typed';
|
import { quoteComposeById } from '@/mastodon/actions/compose_typed';
|
||||||
import { toggleReblog } from '@/mastodon/actions/interactions';
|
import { toggleReblog } from '@/mastodon/actions/interactions';
|
||||||
import { openModal } from '@/mastodon/actions/modal';
|
import { openModal } from '@/mastodon/actions/modal';
|
||||||
|
import { fetchStatus } from '@/mastodon/actions/statuses';
|
||||||
import { quickBoosting } from '@/mastodon/initial_state';
|
import { quickBoosting } from '@/mastodon/initial_state';
|
||||||
import type { ActionMenuItem } from '@/mastodon/models/dropdown_menu';
|
import type { ActionMenuItem } from '@/mastodon/models/dropdown_menu';
|
||||||
import type { Status } from '@/mastodon/models/status';
|
import type { Status } from '@/mastodon/models/status';
|
||||||
@@ -111,6 +112,7 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
|
|
||||||
const statusId = status.get('id') as string;
|
const statusId = status.get('id') as string;
|
||||||
const wasBoosted = !!status.get('reblogged');
|
const wasBoosted = !!status.get('reblogged');
|
||||||
|
const quoteApproval = status.get('quote_approval');
|
||||||
|
|
||||||
const showLoginPrompt = useCallback(() => {
|
const showLoginPrompt = useCallback(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
@@ -167,9 +169,16 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
dispatch(toggleReblog(status.get('id'), true));
|
dispatch(toggleReblog(status.get('id'), true));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quoteApproval === null) {
|
||||||
|
dispatch(
|
||||||
|
fetchStatus(statusId, { forceFetch: true, alsoFetchContext: false }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[dispatch, isLoggedIn, showLoginPrompt, status],
|
[dispatch, isLoggedIn, showLoginPrompt, status, quoteApproval, statusId],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -404,6 +404,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
scrollKey={scrollKey}
|
scrollKey={scrollKey}
|
||||||
status={status}
|
status={status}
|
||||||
|
needsStatusRefresh={quickBoosting && status.get('quote_approval') === null}
|
||||||
items={menu}
|
items={menu}
|
||||||
icon='ellipsis-h'
|
icon='ellipsis-h'
|
||||||
iconComponent={MoreHorizIcon}
|
iconComponent={MoreHorizIcon}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
UNSAFE_componentWillMount () {
|
UNSAFE_componentWillMount () {
|
||||||
this.props.dispatch(fetchStatus(this.props.params.statusId));
|
this.props.dispatch(fetchStatus(this.props.params.statusId, { forceFetch: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
@@ -170,7 +170,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
|
|
||||||
UNSAFE_componentWillReceiveProps (nextProps) {
|
UNSAFE_componentWillReceiveProps (nextProps) {
|
||||||
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
|
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
|
||||||
this.props.dispatch(fetchStatus(nextProps.params.statusId));
|
this.props.dispatch(fetchStatus(nextProps.params.statusId, { forceFetch: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) {
|
if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ function getStatusResultFunction(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusBase.get('isLoading')) {
|
if (statusBase.get('isLoading') && !statusBase.get('content')) {
|
||||||
return {
|
return {
|
||||||
status: null,
|
status: null,
|
||||||
loadingState: 'loading',
|
loadingState: 'loading',
|
||||||
@@ -74,7 +74,7 @@ function getStatusResultFunction(
|
|||||||
map.set('matched_filters', filtered);
|
map.set('matched_filters', filtered);
|
||||||
map.set('matched_media_filters', mediaFiltered);
|
map.set('matched_media_filters', mediaFiltered);
|
||||||
}),
|
}),
|
||||||
loadingState: 'complete'
|
loadingState: statusBase.get('isLoading') ? 'loading' : 'complete'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user