forked from treehouse/mastodon
Merge pull request #1815 from ClearlyClaire/glitch-soc/features/upstream-cw-reveal
Add option to share CW toggle state across instances of a postrebase/4.0.0rc1
commit
3a7c641dd4
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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'])) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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'),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 }) => (
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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']));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue