Merge pull request #1815 from ClearlyClaire/glitch-soc/features/upstream-cw-reveal

Add option to share CW toggle state across instances of a post
This commit is contained in:
Claire 2022-07-25 13:30:17 +02:00 committed by GitHub
commit 3a7c641dd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 226 additions and 101 deletions

View File

@ -63,7 +63,7 @@ export function importFetchedStatuses(statuses) {
const polls = []; const polls = [];
function processStatus(status) { function processStatus(status) {
pushUnique(normalStatuses, normalizeStatus(status, getState().getIn(['statuses', status.id]))); pushUnique(normalStatuses, normalizeStatus(status, getState().getIn(['statuses', status.id]), getState().get('local_settings')));
pushUnique(accounts, status.account); pushUnique(accounts, status.account);
if (status.reblog && status.reblog.id) { if (status.reblog && status.reblog.id) {

View File

@ -1,6 +1,7 @@
import escapeTextContentForBrowser from 'escape-html'; import escapeTextContentForBrowser from 'escape-html';
import emojify from 'flavours/glitch/util/emoji'; import emojify from 'flavours/glitch/util/emoji';
import { unescapeHTML } from 'flavours/glitch/util/html'; import { unescapeHTML } from 'flavours/glitch/util/html';
import { autoHideCW } from 'flavours/glitch/util/content_warning';
const domParser = new DOMParser(); const domParser = new DOMParser();
@ -41,7 +42,7 @@ export function normalizeAccount(account) {
return account; return account;
} }
export function normalizeStatus(status, normalOldStatus) { export function normalizeStatus(status, normalOldStatus, settings) {
const normalStatus = { ...status }; const normalStatus = { ...status };
normalStatus.account = status.account.id; normalStatus.account = status.account.id;
@ -60,6 +61,7 @@ export function normalizeStatus(status, normalOldStatus) {
normalStatus.search_index = normalOldStatus.get('search_index'); normalStatus.search_index = normalOldStatus.get('search_index');
normalStatus.contentHtml = normalOldStatus.get('contentHtml'); normalStatus.contentHtml = normalOldStatus.get('contentHtml');
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml'); normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml');
normalStatus.hidden = normalOldStatus.get('hidden');
} else { } else {
const spoilerText = normalStatus.spoiler_text || ''; const spoilerText = normalStatus.spoiler_text || '';
const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n'); const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
@ -68,6 +70,7 @@ export function normalizeStatus(status, normalOldStatus) {
normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent; normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
normalStatus.contentHtml = emojify(normalStatus.content, emojiMap); normalStatus.contentHtml = emojify(normalStatus.content, emojiMap);
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap); normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
normalStatus.hidden = (spoilerText.length > 0 || normalStatus.sensitive) && autoHideCW(settings, spoilerText);
} }
return normalStatus; return normalStatus;

View File

@ -24,6 +24,10 @@ export const STATUS_UNMUTE_REQUEST = 'STATUS_UNMUTE_REQUEST';
export const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS'; export const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS';
export const STATUS_UNMUTE_FAIL = 'STATUS_UNMUTE_FAIL'; export const STATUS_UNMUTE_FAIL = 'STATUS_UNMUTE_FAIL';
export const STATUS_REVEAL = 'STATUS_REVEAL';
export const STATUS_HIDE = 'STATUS_HIDE';
export const STATUS_COLLAPSE = 'STATUS_COLLAPSE';
export const REDRAFT = 'REDRAFT'; export const REDRAFT = 'REDRAFT';
export const STATUS_FETCH_SOURCE_REQUEST = 'STATUS_FETCH_SOURCE_REQUEST'; export const STATUS_FETCH_SOURCE_REQUEST = 'STATUS_FETCH_SOURCE_REQUEST';
@ -277,3 +281,33 @@ export function unmuteStatusFail(id, error) {
error, error,
}; };
}; };
export function hideStatus(ids) {
if (!Array.isArray(ids)) {
ids = [ids];
}
return {
type: STATUS_HIDE,
ids,
};
};
export function revealStatus(ids) {
if (!Array.isArray(ids)) {
ids = [ids];
}
return {
type: STATUS_REVEAL,
ids,
};
};
export function toggleStatusCollapse(id, isCollapsed) {
return {
type: STATUS_COLLAPSE,
id,
isCollapsed,
};
}

View File

@ -81,8 +81,8 @@ class Status extends ImmutablePureComponent {
onBlock: PropTypes.func, onBlock: PropTypes.func,
onEmbed: PropTypes.func, onEmbed: PropTypes.func,
onHeightChange: PropTypes.func, onHeightChange: PropTypes.func,
onToggleHidden: PropTypes.func,
muted: PropTypes.bool, muted: PropTypes.bool,
collapse: PropTypes.bool,
hidden: PropTypes.bool, hidden: PropTypes.bool,
unread: PropTypes.bool, unread: PropTypes.bool,
prepend: PropTypes.string, prepend: PropTypes.string,
@ -121,7 +121,6 @@ class Status extends ImmutablePureComponent {
'settings', 'settings',
'prepend', 'prepend',
'muted', 'muted',
'collapse',
'notification', 'notification',
'hidden', 'hidden',
'expanded', 'expanded',
@ -149,14 +148,14 @@ class Status extends ImmutablePureComponent {
let updated = false; let updated = false;
// Make sure the state mirrors props we track… // Make sure the state mirrors props we track…
if (nextProps.collapse !== prevState.collapseProp) {
update.collapseProp = nextProps.collapse;
updated = true;
}
if (nextProps.expanded !== prevState.expandedProp) { if (nextProps.expanded !== prevState.expandedProp) {
update.expandedProp = nextProps.expanded; update.expandedProp = nextProps.expanded;
updated = true; updated = true;
} }
if (nextProps.status?.get('hidden') !== prevState.statusPropHidden) {
update.statusPropHidden = nextProps.status?.get('hidden');
updated = true;
}
// Update state based on new props // Update state based on new props
if (!nextProps.settings.getIn(['collapsed', 'enabled'])) { if (!nextProps.settings.getIn(['collapsed', 'enabled'])) {
@ -164,14 +163,19 @@ class Status extends ImmutablePureComponent {
update.isCollapsed = false; update.isCollapsed = false;
updated = true; updated = true;
} }
} else if ( }
nextProps.collapse !== prevState.collapseProp &&
nextProps.collapse !== undefined // Handle uncollapsing toots when the shared CW state is expanded
if (nextProps.settings.getIn(['content_warnings', 'shared_state']) &&
nextProps.status?.get('spoiler_text')?.length && nextProps.status?.get('hidden') === false &&
prevState.statusPropHidden !== false && prevState.isCollapsed
) { ) {
update.isCollapsed = nextProps.collapse; update.isCollapsed = false;
if (nextProps.collapse) update.isExpanded = false;
updated = true; updated = true;
} }
// The “expanded” prop is used to one-off change the local state.
// It's used in the thread view when unfolding/re-folding all CWs at once.
if (nextProps.expanded !== prevState.expandedProp && if (nextProps.expanded !== prevState.expandedProp &&
nextProps.expanded !== undefined nextProps.expanded !== undefined
) { ) {
@ -180,16 +184,10 @@ class Status extends ImmutablePureComponent {
updated = true; updated = true;
} }
if (nextProps.expanded === undefined && if (prevState.isExpanded === undefined && update.isExpanded === undefined) {
prevState.isExpanded === undefined && update.isExpanded = autoUnfoldCW(nextProps.settings, nextProps.status);
update.isExpanded === undefined
) {
const isExpanded = autoUnfoldCW(nextProps.settings, nextProps.status);
if (isExpanded !== undefined) {
update.isExpanded = isExpanded;
updated = true; updated = true;
} }
}
if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) { if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) {
update.showMedia = defaultMediaVisibility(nextProps.status, nextProps.settings); update.showMedia = defaultMediaVisibility(nextProps.status, nextProps.settings);
@ -243,22 +241,18 @@ class Status extends ImmutablePureComponent {
const autoCollapseSettings = settings.getIn(['collapsed', 'auto']); const autoCollapseSettings = settings.getIn(['collapsed', 'auto']);
if (function () { // Don't autocollapse if CW state is shared and status is explicitly revealed,
switch (true) { // as it could cause surprising changes when receiving notifications
case !!collapse: if (settings.getIn(['content_warnings', 'shared_state']) && status.get('spoiler_text').length && !status.get('hidden')) return;
case !!autoCollapseSettings.get('all'):
case autoCollapseSettings.get('notifications') && !!muted: if (collapse ||
case autoCollapseSettings.get('lengthy') && node.clientHeight > ( autoCollapseSettings.get('all') ||
status.get('media_attachments').size && !muted ? 650 : 400 (autoCollapseSettings.get('notifications') && muted) ||
): (autoCollapseSettings.get('lengthy') && node.clientHeight > ((status.get('media_attachments').size && !muted) ? 650 : 400)) ||
case autoCollapseSettings.get('reblogs') && prepend === 'reblogged_by': (autoCollapseSettings.get('reblogs') && prepend === 'reblogged_by') ||
case autoCollapseSettings.get('replies') && status.get('in_reply_to_id', null) !== null: (autoCollapseSettings.get('replies') && status.get('in_reply_to_id', null) !== null) ||
case autoCollapseSettings.get('media') && !(status.get('spoiler_text').length) && !!status.get('media_attachments').size: (autoCollapseSettings.get('media') && !(status.get('spoiler_text').length) && status.get('media_attachments').size > 0)
return true; ) {
default:
return false;
}
}()) {
this.setCollapsed(true); this.setCollapsed(true);
// Hack to fix timeline jumps on second rendering when auto-collapsing // Hack to fix timeline jumps on second rendering when auto-collapsing
this.setState({ autoCollapsed: true }); this.setState({ autoCollapsed: true });
@ -309,16 +303,20 @@ class Status extends ImmutablePureComponent {
// is enabled, so we don't have to. // is enabled, so we don't have to.
setCollapsed = (value) => { setCollapsed = (value) => {
if (this.props.settings.getIn(['collapsed', 'enabled'])) { if (this.props.settings.getIn(['collapsed', 'enabled'])) {
this.setState({ isCollapsed: value });
if (value) { if (value) {
this.setExpansion(false); this.setExpansion(false);
} }
this.setState({ isCollapsed: value });
} else { } else {
this.setState({ isCollapsed: false }); this.setState({ isCollapsed: false });
} }
} }
setExpansion = (value) => { setExpansion = (value) => {
if (this.props.settings.getIn(['content_warnings', 'shared_state']) && this.props.status.get('hidden') === value) {
this.props.onToggleHidden(this.props.status);
}
this.setState({ isExpanded: value }); this.setState({ isExpanded: value });
if (value) { if (value) {
this.setCollapsed(false); this.setCollapsed(false);
@ -365,7 +363,9 @@ class Status extends ImmutablePureComponent {
} }
handleExpandedToggle = () => { handleExpandedToggle = () => {
if (this.props.status.get('spoiler_text')) { if (this.props.settings.getIn(['content_warnings', 'shared_state'])) {
this.props.onToggleHidden(this.props.status);
} else if (this.props.status.get('spoiler_text')) {
this.setExpansion(!this.state.isExpanded); this.setExpansion(!this.state.isExpanded);
} }
}; };
@ -505,7 +505,7 @@ class Status extends ImmutablePureComponent {
usingPiP, usingPiP,
...other ...other
} = this.props; } = this.props;
const { isExpanded, isCollapsed, forceFilter } = this.state; const { isCollapsed, forceFilter } = this.state;
let background = null; let background = null;
let attachments = null; let attachments = null;
@ -528,6 +528,8 @@ class Status extends ImmutablePureComponent {
return null; return null;
} }
const isExpanded = settings.getIn(['content_warnings', 'shared_state']) ? !status.get('hidden') : this.state.isExpanded;
const handlers = { const handlers = {
reply: this.handleHotkeyReply, reply: this.handleHotkeyReply,
favourite: this.handleHotkeyFavourite, favourite: this.handleHotkeyFavourite,

View File

@ -17,7 +17,14 @@ import {
pin, pin,
unpin, unpin,
} from 'flavours/glitch/actions/interactions'; } from 'flavours/glitch/actions/interactions';
import { muteStatus, unmuteStatus, deleteStatus, editStatus } from 'flavours/glitch/actions/statuses'; import {
muteStatus,
unmuteStatus,
deleteStatus,
hideStatus,
revealStatus,
editStatus
} from 'flavours/glitch/actions/statuses';
import { initMuteModal } from 'flavours/glitch/actions/mutes'; import { initMuteModal } from 'flavours/glitch/actions/mutes';
import { initBlockModal } from 'flavours/glitch/actions/blocks'; import { initBlockModal } from 'flavours/glitch/actions/blocks';
import { initReport } from 'flavours/glitch/actions/reports'; import { initReport } from 'flavours/glitch/actions/reports';
@ -252,6 +259,14 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
} }
}, },
onToggleHidden (status) {
if (status.get('hidden')) {
dispatch(revealStatus(status.get('id')));
} else {
dispatch(hideStatus(status.get('id')));
}
},
deployPictureInPicture (status, type, mediaProps) { deployPictureInPicture (status, type, mediaProps) {
dispatch((_, getState) => { dispatch((_, getState) => {
if (getState().getIn(['local_settings', 'media', 'pop_in_player'])) { if (getState().getIn(['local_settings', 'media', 'pop_in_player'])) {

View File

@ -132,6 +132,8 @@ class Conversation extends ImmutablePureComponent {
} }
handleShowMore = () => { handleShowMore = () => {
this.props.onToggleHidden(this.props.lastStatus);
if (this.props.lastStatus.get('spoiler_text')) { if (this.props.lastStatus.get('spoiler_text')) {
this.setExpansion(!this.state.isExpanded); this.setExpansion(!this.state.isExpanded);
} }
@ -143,12 +145,13 @@ class Conversation extends ImmutablePureComponent {
render () { render () {
const { accounts, lastStatus, unread, scrollKey, intl } = this.props; const { accounts, lastStatus, unread, scrollKey, intl } = this.props;
const { isExpanded } = this.state;
if (lastStatus === null) { if (lastStatus === null) {
return null; return null;
} }
const isExpanded = this.props.settings.getIn(['content_warnings', 'shared_state']) ? !lastStatus.get('hidden') : this.state.isExpanded;
const menu = [ const menu = [
{ text: intl.formatMessage(messages.open), action: this.handleClick }, { text: intl.formatMessage(messages.open), action: this.handleClick },
null, null,

View File

@ -23,6 +23,7 @@ const mapStateToProps = () => {
accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)), accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)),
unread: conversation.get('unread'), unread: conversation.get('unread'),
lastStatus: lastStatusId && getStatus(state, { id: lastStatusId }), lastStatus: lastStatusId && getStatus(state, { id: lastStatusId }),
settings: state.get('local_settings'),
}; };
}; };
}; };

View File

@ -303,6 +303,15 @@ class LocalSettingsPage extends React.PureComponent {
({ intl, onChange, settings }) => ( ({ intl, onChange, settings }) => (
<div className='glitch local-settings__page content_warnings'> <div className='glitch local-settings__page content_warnings'>
<h1><FormattedMessage id='settings.content_warnings' defaultMessage='Content warnings' /></h1> <h1><FormattedMessage id='settings.content_warnings' defaultMessage='Content warnings' /></h1>
<LocalSettingsPageItem
settings={settings}
item={['content_warnings', 'shared_state']}
id='mastodon-settings--content_warnings-shared_state'
onChange={onChange}
>
<FormattedMessage id='settings.content_warnings_shared_state' defaultMessage='Show/hide content of all copies at once' />
<span className='hint'><FormattedMessage id='settings.content_warnings_shared_state_hint' defaultMessage='Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW' /></span>
</LocalSettingsPageItem>
<LocalSettingsPageItem <LocalSettingsPageItem
settings={settings} settings={settings}
item={['content_warnings', 'media_outside']} item={['content_warnings', 'media_outside']}
@ -312,6 +321,8 @@ class LocalSettingsPage extends React.PureComponent {
<FormattedMessage id='settings.content_warnings_media_outside' defaultMessage='Display media attachments outside content warnings' /> <FormattedMessage id='settings.content_warnings_media_outside' defaultMessage='Display media attachments outside content warnings' />
<span className='hint'><FormattedMessage id='settings.content_warnings_media_outside_hint' defaultMessage='Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments' /></span> <span className='hint'><FormattedMessage id='settings.content_warnings_media_outside_hint' defaultMessage='Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments' /></span>
</LocalSettingsPageItem> </LocalSettingsPageItem>
<section>
<h2><FormattedMessage id='settings.content_warnings_unfold_opts' defaultMessage='Auto-unfolding options' /></h2>
<DeprecatedLocalSettingsPageItem <DeprecatedLocalSettingsPageItem
id='mastodon-settings--content_warnings-auto_unfold' id='mastodon-settings--content_warnings-auto_unfold'
value={expandSpoilers} value={expandSpoilers}
@ -344,6 +355,7 @@ class LocalSettingsPage extends React.PureComponent {
> >
<FormattedMessage id='settings.content_warnings_filter' defaultMessage='Content warnings to not automatically unfold:' /> <FormattedMessage id='settings.content_warnings_filter' defaultMessage='Content warnings to not automatically unfold:' />
</LocalSettingsPageItem> </LocalSettingsPageItem>
</section>
</div> </div>
), ),
({ intl, onChange, settings }) => ( ({ intl, onChange, settings }) => (

View File

@ -26,7 +26,14 @@ import {
directCompose, directCompose,
} from 'flavours/glitch/actions/compose'; } from 'flavours/glitch/actions/compose';
import { changeLocalSetting } from 'flavours/glitch/actions/local_settings'; import { changeLocalSetting } from 'flavours/glitch/actions/local_settings';
import { muteStatus, unmuteStatus, deleteStatus, editStatus } from 'flavours/glitch/actions/statuses'; import {
muteStatus,
unmuteStatus,
deleteStatus,
editStatus,
hideStatus,
revealStatus
} from 'flavours/glitch/actions/statuses';
import { initMuteModal } from 'flavours/glitch/actions/mutes'; import { initMuteModal } from 'flavours/glitch/actions/mutes';
import { initBlockModal } from 'flavours/glitch/actions/blocks'; import { initBlockModal } from 'flavours/glitch/actions/blocks';
import { initReport } from 'flavours/glitch/actions/reports'; import { initReport } from 'flavours/glitch/actions/reports';
@ -215,11 +222,19 @@ class Status extends ImmutablePureComponent {
return updated ? update : null; return updated ? update : null;
} }
handleExpandedToggle = () => { handleToggleHidden = () => {
if (this.props.status.get('spoiler_text')) { const { status } = this.props;
if (this.props.settings.getIn(['content_warnings', 'shared_state'])) {
if (status.get('hidden')) {
this.props.dispatch(revealStatus(status.get('id')));
} else {
this.props.dispatch(hideStatus(status.get('id')));
}
} else if (this.props.status.get('spoiler_text')) {
this.setExpansion(!this.state.isExpanded); this.setExpansion(!this.state.isExpanded);
} }
}; }
handleToggleMediaVisibility = () => { handleToggleMediaVisibility = () => {
this.setState({ showMedia: !this.state.showMedia }); this.setState({ showMedia: !this.state.showMedia });
@ -354,7 +369,19 @@ class Status extends ImmutablePureComponent {
} }
handleToggleAll = () => { handleToggleAll = () => {
const { isExpanded } = this.state; const { status, ancestorsIds, descendantsIds, settings } = this.props;
const statusIds = [status.get('id')].concat(ancestorsIds.toJS(), descendantsIds.toJS());
let { isExpanded } = this.state;
if (settings.getIn(['content_warnings', 'shared_state']))
isExpanded = !status.get('hidden');
if (!isExpanded) {
this.props.dispatch(revealStatus(statusIds));
} else {
this.props.dispatch(hideStatus(statusIds));
}
this.setState({ isExpanded: !isExpanded, threadExpanded: !isExpanded }); this.setState({ isExpanded: !isExpanded, threadExpanded: !isExpanded });
} }
@ -513,9 +540,8 @@ class Status extends ImmutablePureComponent {
render () { render () {
let ancestors, descendants; let ancestors, descendants;
const { setExpansion } = this;
const { status, settings, ancestorsIds, descendantsIds, intl, domain, multiColumn, usingPiP } = this.props; const { status, settings, ancestorsIds, descendantsIds, intl, domain, multiColumn, usingPiP } = this.props;
const { fullscreen, isExpanded } = this.state; const { fullscreen } = this.state;
if (status === null) { if (status === null) {
return ( return (
@ -526,6 +552,8 @@ class Status extends ImmutablePureComponent {
); );
} }
const isExpanded = settings.getIn(['content_warnings', 'shared_state']) ? !status.get('hidden') : this.state.isExpanded;
if (ancestorsIds && ancestorsIds.size > 0) { if (ancestorsIds && ancestorsIds.size > 0) {
ancestors = <div>{this.renderChildren(ancestorsIds)}</div>; ancestors = <div>{this.renderChildren(ancestorsIds)}</div>;
} }
@ -543,7 +571,7 @@ class Status extends ImmutablePureComponent {
bookmark: this.handleHotkeyBookmark, bookmark: this.handleHotkeyBookmark,
mention: this.handleHotkeyMention, mention: this.handleHotkeyMention,
openProfile: this.handleHotkeyOpenProfile, openProfile: this.handleHotkeyOpenProfile,
toggleSpoiler: this.handleExpandedToggle, toggleSpoiler: this.handleToggleHidden,
toggleSensitive: this.handleHotkeyToggleSensitive, toggleSensitive: this.handleHotkeyToggleSensitive,
openMedia: this.handleHotkeyOpenMedia, openMedia: this.handleHotkeyOpenMedia,
}; };
@ -574,7 +602,7 @@ class Status extends ImmutablePureComponent {
onOpenVideo={this.handleOpenVideo} onOpenVideo={this.handleOpenVideo}
onOpenMedia={this.handleOpenMedia} onOpenMedia={this.handleOpenMedia}
expanded={isExpanded} expanded={isExpanded}
onToggleHidden={this.handleExpandedToggle} onToggleHidden={this.handleToggleHidden}
domain={domain} domain={domain}
showMedia={this.state.showMedia} showMedia={this.state.showMedia}
onToggleMediaVisibility={this.handleToggleMediaVisibility} onToggleMediaVisibility={this.handleToggleMediaVisibility}

View File

@ -27,6 +27,7 @@ const initialState = ImmutableMap({
content_warnings : ImmutableMap({ content_warnings : ImmutableMap({
filter : null, filter : null,
media_outside: false, media_outside: false,
shared_state : false,
}), }),
collapsed : ImmutableMap({ collapsed : ImmutableMap({
enabled : true, enabled : true,

View File

@ -10,6 +10,9 @@ import {
import { import {
STATUS_MUTE_SUCCESS, STATUS_MUTE_SUCCESS,
STATUS_UNMUTE_SUCCESS, STATUS_UNMUTE_SUCCESS,
STATUS_REVEAL,
STATUS_HIDE,
STATUS_COLLAPSE,
} from 'flavours/glitch/actions/statuses'; } from 'flavours/glitch/actions/statuses';
import { import {
TIMELINE_DELETE, TIMELINE_DELETE,
@ -56,6 +59,24 @@ export default function statuses(state = initialState, action) {
return state.setIn([action.id, 'muted'], true); return state.setIn([action.id, 'muted'], true);
case STATUS_UNMUTE_SUCCESS: case STATUS_UNMUTE_SUCCESS:
return state.setIn([action.id, 'muted'], false); return state.setIn([action.id, 'muted'], false);
case STATUS_REVEAL:
return state.withMutations(map => {
action.ids.forEach(id => {
if (!(state.get(id) === undefined)) {
map.setIn([id, 'hidden'], false);
}
});
});
case STATUS_HIDE:
return state.withMutations(map => {
action.ids.forEach(id => {
if (!(state.get(id) === undefined)) {
map.setIn([id, 'hidden'], true);
}
});
});
case STATUS_COLLAPSE:
return state.setIn([action.id, 'collapsed'], action.isCollapsed);
case TIMELINE_DELETE: case TIMELINE_DELETE:
return deleteStatus(state, action.id, action.references); return deleteStatus(state, action.id, action.references);
default: default:

View File

@ -1,26 +1,31 @@
import { expandSpoilers } from 'flavours/glitch/util/initial_state'; import { expandSpoilers } from 'flavours/glitch/util/initial_state';
export function autoUnfoldCW (settings, status) { function _autoUnfoldCW(spoiler_text, skip_unfold_regex) {
if (!expandSpoilers) { if (!expandSpoilers)
return false; return false;
}
const rawRegex = settings.getIn(['content_warnings', 'filter']); if (!skip_unfold_regex)
if (!rawRegex) {
return true; return true;
}
let regex = null; let regex = null;
try { try {
regex = rawRegex && new RegExp(rawRegex.trim(), 'i'); regex = new RegExp(skip_unfold_regex.trim(), 'i');
} catch (e) { } catch (e) {
// Bad regex, don't affect filters // Bad regex, skip filters
return true;
} }
if (!(status && regex)) { return !regex.test(spoiler_text);
return undefined; }
}
return !regex.test(status.get('spoiler_text')); export function autoHideCW(settings, spoiler_text) {
return !_autoUnfoldCW(spoiler_text, settings.getIn(['content_warnings', 'filter']));
}
export function autoUnfoldCW(settings, status) {
if (!status)
return false;
return _autoUnfoldCW(status.get('spoiler_text'), settings.getIn(['content_warnings', 'filter']));
} }