Refactor status normalizer
This commit is contained in:
parent
1e4659248f
commit
f7f18fac79
|
@ -1,8 +1,5 @@
|
|||
import { getSettings } from '../settings';
|
||||
|
||||
import {
|
||||
normalizeAccount,
|
||||
normalizeStatus,
|
||||
normalizePoll,
|
||||
} from './normalizer';
|
||||
|
||||
|
@ -60,11 +57,6 @@ export function importFetchedStatus(status, idempotencyKey) {
|
|||
// Skip broken statuses
|
||||
if (isBroken(status)) return;
|
||||
|
||||
const normalOldStatus = getState().getIn(['statuses', status.id]);
|
||||
const expandSpoilers = getSettings(getState()).get('expandSpoilers');
|
||||
|
||||
const normalizedStatus = normalizeStatus(status, normalOldStatus, expandSpoilers);
|
||||
|
||||
if (status.reblog?.id) {
|
||||
dispatch(importFetchedStatus(status.reblog));
|
||||
}
|
||||
|
@ -83,7 +75,7 @@ export function importFetchedStatus(status, idempotencyKey) {
|
|||
}
|
||||
|
||||
dispatch(importFetchedAccount(status.account));
|
||||
dispatch(importStatus(normalizedStatus, idempotencyKey));
|
||||
dispatch(importStatus(status, idempotencyKey));
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -106,17 +98,12 @@ const isBroken = status => {
|
|||
export function importFetchedStatuses(statuses) {
|
||||
return (dispatch, getState) => {
|
||||
const accounts = [];
|
||||
const normalStatuses = [];
|
||||
const polls = [];
|
||||
|
||||
function processStatus(status) {
|
||||
// Skip broken statuses
|
||||
if (isBroken(status)) return;
|
||||
|
||||
const normalOldStatus = getState().getIn(['statuses', status.id]);
|
||||
const expandSpoilers = getSettings(getState()).get('expandSpoilers');
|
||||
|
||||
normalStatuses.push(normalizeStatus(status, normalOldStatus, expandSpoilers));
|
||||
accounts.push(status.account);
|
||||
|
||||
if (status.reblog?.id) {
|
||||
|
@ -141,7 +128,7 @@ export function importFetchedStatuses(statuses) {
|
|||
|
||||
dispatch(importPolls(polls));
|
||||
dispatch(importFetchedAccounts(accounts));
|
||||
dispatch(importStatuses(normalStatuses));
|
||||
dispatch(importStatuses(statuses));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
import escapeTextContentForBrowser from 'escape-html';
|
||||
|
||||
import { stripCompatibilityFeatures } from 'soapbox/utils/html';
|
||||
|
||||
import emojify from '../../features/emoji/emoji';
|
||||
import { unescapeHTML } from '../../utils/html';
|
||||
|
||||
const domParser = new DOMParser();
|
||||
|
||||
const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => {
|
||||
obj[`:${emoji.shortcode}:`] = emoji;
|
||||
return obj;
|
||||
|
@ -45,61 +41,6 @@ export function normalizeAccount(account) {
|
|||
return account;
|
||||
}
|
||||
|
||||
export function normalizeStatus(status, normalOldStatus, expandSpoilers) {
|
||||
const normalStatus = { ...status };
|
||||
|
||||
// Some backends can return null, or omit these required fields
|
||||
if (!normalStatus.emojis) normalStatus.emojis = [];
|
||||
if (!normalStatus.spoiler_text) normalStatus.spoiler_text = '';
|
||||
|
||||
// Copy the pleroma object too, so we can modify our copy
|
||||
if (status.pleroma) {
|
||||
normalStatus.pleroma = { ...status.pleroma };
|
||||
}
|
||||
|
||||
normalStatus.account = status.account.id;
|
||||
|
||||
if (status.reblog?.id) {
|
||||
normalStatus.reblog = status.reblog.id;
|
||||
}
|
||||
|
||||
if (status.poll?.id) {
|
||||
normalStatus.poll = status.poll.id;
|
||||
}
|
||||
|
||||
if (status.pleroma?.quote?.id) {
|
||||
// Normalize quote to the top-level, so delete the original for performance
|
||||
normalStatus.quote = status.pleroma.quote.id;
|
||||
delete normalStatus.pleroma.quote;
|
||||
} else if (status.quote?.id) {
|
||||
// Fedibird compatibility, because why not
|
||||
normalStatus.quote = status.quote.id;
|
||||
} else if (status.quote_id) {
|
||||
// Fedibird: fall back to quote_id
|
||||
normalStatus.quote = status.quote_id;
|
||||
}
|
||||
|
||||
// Only calculate these values when status first encountered
|
||||
// Otherwise keep the ones already in the reducer
|
||||
if (normalOldStatus) {
|
||||
normalStatus.search_index = normalOldStatus.get('search_index');
|
||||
normalStatus.contentHtml = normalOldStatus.get('contentHtml');
|
||||
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml');
|
||||
normalStatus.hidden = normalOldStatus.get('hidden');
|
||||
} else {
|
||||
const spoilerText = normalStatus.spoiler_text || '';
|
||||
const searchContent = ([spoilerText, status.content].concat((status.poll?.options) ? status.poll.options.map(option => option.title) : [])).join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
|
||||
const emojiMap = makeEmojiMap(normalStatus);
|
||||
|
||||
normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
|
||||
normalStatus.contentHtml = stripCompatibilityFeatures(emojify(normalStatus.content, emojiMap));
|
||||
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
|
||||
normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive;
|
||||
}
|
||||
|
||||
return normalStatus;
|
||||
}
|
||||
|
||||
export function normalizePoll(poll) {
|
||||
const normalPoll = { ...poll };
|
||||
|
||||
|
|
|
@ -43,27 +43,8 @@ class StatusReplyMentions extends ImmutablePureComponent {
|
|||
const to = status.get('mentions', []);
|
||||
|
||||
// The post is a reply, but it has no mentions.
|
||||
// Rare, but it can happen.
|
||||
if (to.size === 0) {
|
||||
// The author is replying to themself.
|
||||
if (status.get('in_reply_to_account_id') === status.getIn(['account', 'id'])) {
|
||||
return (
|
||||
<div className='reply-mentions'>
|
||||
<FormattedMessage
|
||||
id='reply_mentions.reply'
|
||||
defaultMessage='Replying to {accounts}{more}'
|
||||
values={{
|
||||
accounts: (<>
|
||||
<HoverRefWrapper accountId={status.getIn(['account', 'id'])} inline>
|
||||
<Link to={`/@${status.getIn(['account', 'acct'])}`} className='reply-mentions__account'>@{status.getIn(['account', 'username'])}</Link>
|
||||
</HoverRefWrapper>
|
||||
</>),
|
||||
more: false,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
// The reply-to is unknown. Rare, but it can happen.
|
||||
return (
|
||||
<div className='reply-mentions'>
|
||||
<FormattedMessage
|
||||
|
@ -73,8 +54,6 @@ class StatusReplyMentions extends ImmutablePureComponent {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The typical case with a reply-to and a list of mentions.
|
||||
return (
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { fromJS } from 'immutable';
|
||||
|
||||
import { normalizeStatus } from 'soapbox/actions/importer/normalizer';
|
||||
import { normalizeStatus } from 'soapbox/normalizers/status';
|
||||
import { makeGetAccount } from 'soapbox/selectors';
|
||||
|
||||
export const buildStatus = (state, scheduledStatus) => {
|
||||
|
@ -10,7 +10,7 @@ export const buildStatus = (state, scheduledStatus) => {
|
|||
const params = scheduledStatus.get('params');
|
||||
const account = getAccount(state, me);
|
||||
|
||||
const status = normalizeStatus({
|
||||
const status = {
|
||||
account,
|
||||
application: null,
|
||||
bookmarked: false,
|
||||
|
@ -40,7 +40,7 @@ export const buildStatus = (state, scheduledStatus) => {
|
|||
uri: `/scheduled_statuses/${scheduledStatus.get('id')}`,
|
||||
url: `/scheduled_statuses/${scheduledStatus.get('id')}`,
|
||||
visibility: params.get('visibility'),
|
||||
});
|
||||
|
||||
return fromJS(status).set('account', account);
|
||||
};
|
||||
|
||||
return normalizeStatus(fromJS(status));
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { fromJS } from 'immutable';
|
||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
|
||||
import { normalizeStatus } from 'soapbox/actions/importer/normalizer';
|
||||
import { normalizeStatus } from 'soapbox/normalizers/status';
|
||||
import { makeGetAccount, makeGetStatus } from 'soapbox/selectors';
|
||||
|
||||
export const buildStatus = (state, pendingStatus, idempotencyKey) => {
|
||||
|
@ -26,7 +26,7 @@ export const buildStatus = (state, pendingStatus, idempotencyKey) => {
|
|||
}));
|
||||
}
|
||||
|
||||
const status = normalizeStatus({
|
||||
const status = {
|
||||
account,
|
||||
application: null,
|
||||
bookmarked: false,
|
||||
|
@ -57,7 +57,7 @@ export const buildStatus = (state, pendingStatus, idempotencyKey) => {
|
|||
uri: '',
|
||||
url: '',
|
||||
visibility: pendingStatus.get('visibility', 'public'),
|
||||
});
|
||||
|
||||
return fromJS(status).set('account', account);
|
||||
};
|
||||
|
||||
return normalizeStatus(fromJS(status));
|
||||
};
|
||||
|
|
|
@ -2,6 +2,14 @@ import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
|||
|
||||
import { accountToMention } from 'soapbox/utils/accounts';
|
||||
|
||||
// Some backends can return null, or omit these required fields
|
||||
const setRequiredFields = status => {
|
||||
return status.merge({
|
||||
emojis: status.get('emojis') || [],
|
||||
spoiler_text: status.get('spoiler_text') || '',
|
||||
});
|
||||
};
|
||||
|
||||
// Ensure attachments have required fields
|
||||
// https://docs.joinmastodon.org/entities/attachment/
|
||||
const normalizeAttachment = attachment => {
|
||||
|
@ -62,6 +70,7 @@ const addSelfMention = status => {
|
|||
|
||||
export const normalizeStatus = status => {
|
||||
return status.withMutations(status => {
|
||||
setRequiredFields(status);
|
||||
fixMentions(status);
|
||||
addSelfMention(status);
|
||||
normalizeAttachments(status);
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import escapeTextContentForBrowser from 'escape-html';
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
import emojify from 'soapbox/features/emoji/emoji';
|
||||
import { normalizeStatus } from 'soapbox/normalizers/status';
|
||||
import { simulateEmojiReact, simulateUnEmojiReact } from 'soapbox/utils/emoji_reacts';
|
||||
import { stripCompatibilityFeatures } from 'soapbox/utils/html';
|
||||
|
||||
import {
|
||||
EMOJI_REACT_REQUEST,
|
||||
|
@ -27,6 +30,46 @@ import {
|
|||
} from '../actions/statuses';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
|
||||
const domParser = new DOMParser();
|
||||
|
||||
const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
|
||||
obj[`:${emoji.get('shortcode')}:`] = emoji;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
const minifyStatus = status => {
|
||||
return status.merge({
|
||||
account: status.getIn(['account', 'id'], null),
|
||||
reblog: status.getIn(['reblog', 'id'], null),
|
||||
poll: status.getIn(['poll', 'id'], null),
|
||||
quote: status.getIn(['quote', 'id']) || status.getIn(['pleroma', 'quote', 'id']) || null,
|
||||
});
|
||||
};
|
||||
|
||||
// Only calculate these values when status first encountered
|
||||
// Otherwise keep the ones already in the reducer
|
||||
const calculateStatus = (status, oldStatus) => {
|
||||
if (oldStatus) {
|
||||
return status.merge({
|
||||
search_index: oldStatus.get('search_index'),
|
||||
contentHtml: oldStatus.get('contentHtml'),
|
||||
spoilerHtml: oldStatus.get('spoilerHtml'),
|
||||
hidden: oldStatus.get('hidden'),
|
||||
});
|
||||
} else {
|
||||
const spoilerText = status.get('spoiler_text') || '';
|
||||
const searchContent = ([spoilerText, status.get('content')].concat(status.getIn(['poll', 'options']) ? status.getIn(['poll', 'options']).map(option => option.get('title')) : [])).join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
|
||||
const emojiMap = makeEmojiMap(status);
|
||||
|
||||
return status.merge({
|
||||
search_index: domParser.parseFromString(searchContent, 'text/html').documentElement.textContent,
|
||||
contentHtml: stripCompatibilityFeatures(emojify(status.get('content'), emojiMap)),
|
||||
spoilerHtml: emojify(escapeTextContentForBrowser(spoilerText), emojiMap),
|
||||
hidden: spoilerText.length > 0 || status.get('sensitive'),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const isQuote = status => {
|
||||
return Boolean(status.get('quote_id') || status.getIn(['pleroma', 'quote_url']));
|
||||
};
|
||||
|
@ -45,9 +88,13 @@ const fixQuote = (state, status) => {
|
|||
};
|
||||
|
||||
const fixStatus = (state, status) => {
|
||||
const oldStatus = state.get(status.get('id'));
|
||||
|
||||
return status.withMutations(status => {
|
||||
normalizeStatus(status);
|
||||
fixQuote(state, status);
|
||||
calculateStatus(status, oldStatus);
|
||||
minifyStatus(status);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue