diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 022ae6de85a..973275fbbac 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -106,6 +106,7 @@ class Status extends ImmutablePureComponent { statusId: undefined, revealBehindCW: undefined, showCard: false, + bypassFilter: false, } // Avoid checking props that are functions (and whose equality will always @@ -126,6 +127,7 @@ class Status extends ImmutablePureComponent { 'isExpanded', 'isCollapsed', 'showMedia', + 'bypassFilter', ] // If our settings have changed to disable collapsed statuses, then we @@ -427,6 +429,15 @@ class Status extends ImmutablePureComponent { this.handleToggleMediaVisibility(); } + handleUnfilterClick = e => { + const { onUnfilter, status } = this.props; + onUnfilter(status.get('reblog') ? status.get('reblog') : status, () => this.setState({ bypassFilter: true })); + } + + handleFilterClick = () => { + this.setState({ bypassFilter: false }); + } + handleRef = c => { this.node = c; } @@ -485,7 +496,7 @@ class Status extends ImmutablePureComponent { ); } - if (status.get('filtered') || status.getIn(['reblog', 'filtered'])) { + if ((status.get('filtered') || status.getIn(['reblog', 'filtered'])) && !this.state.bypassFilter) { const minHandlers = this.props.muted ? {} : { moveUp: this.handleHotkeyMoveUp, moveDown: this.handleHotkeyMoveDown, @@ -495,6 +506,9 @@ class Status extends ImmutablePureComponent {
+
); @@ -689,6 +703,7 @@ class Status extends ImmutablePureComponent { account={status.get('account')} showReplyCount={settings.get('show_reply_count')} directMessage={!!otherAccounts} + onFilter={this.handleFilterClick} /> ) : null} {notification ? ( diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js index c424fbde135..4ef518f5e90 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.js +++ b/app/javascript/flavours/glitch/components/status_action_bar.js @@ -35,6 +35,7 @@ const messages = defineMessages({ admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' }, admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' }, copy: { id: 'status.copy', defaultMessage: 'Copy link to status' }, + hide: { id: 'status.hide', defaultMessage: 'Hide toot' }, }); const obfuscatedCount = count => { @@ -69,6 +70,7 @@ export default class StatusActionBar extends ImmutablePureComponent { onMuteConversation: PropTypes.func, onPin: PropTypes.func, onBookmark: PropTypes.func, + onFilter: PropTypes.func, withDismiss: PropTypes.bool, showReplyCount: PropTypes.bool, directMessage: PropTypes.bool, @@ -191,6 +193,10 @@ export default class StatusActionBar extends ImmutablePureComponent { } } + handleFilterClick = () => { + this.props.onFilter(); + } + render () { const { status, intl, withDismiss, showReplyCount, directMessage } = this.props; @@ -263,6 +269,10 @@ export default class StatusActionBar extends ImmutablePureComponent { ); + const filterButton = status.get('filtered') && ( + + ); + let replyButton = ( , shareButton, , + filterButton,
, diff --git a/app/javascript/flavours/glitch/containers/status_container.js b/app/javascript/flavours/glitch/containers/status_container.js index a6069cb905d..ba12ad38d51 100644 --- a/app/javascript/flavours/glitch/containers/status_container.js +++ b/app/javascript/flavours/glitch/containers/status_container.js @@ -1,7 +1,8 @@ import React from 'react'; import { connect } from 'react-redux'; import Status from 'flavours/glitch/components/status'; -import { makeGetStatus } from 'flavours/glitch/selectors'; +import { List as ImmutableList } from 'immutable'; +import { makeGetStatus, regexFromFilters, toServerSideType } from 'flavours/glitch/selectors'; import { replyCompose, mentionCompose, @@ -26,6 +27,7 @@ import { changeLocalSetting } from 'flavours/glitch/actions/local_settings'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { boostModal, favouriteModal, deleteModal } from 'flavours/glitch/util/initial_state'; import { showAlertForError } from '../actions/alerts'; +import AccountContainer from 'flavours/glitch/containers/account_container'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -36,8 +38,49 @@ const messages = defineMessages({ replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' }, replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' }, + unfilterConfirm: { id: 'confirmations.unfilter.confirm', defaultMessage: 'Show' }, }); +class SpoilerMachin extends React.PureComponent { + state = { + hidden: true, + } + + handleSpoilerClick = () => { + this.setState({ hidden: !this.state.hidden }); + } + + render () { + const { spoilerText, children } = this.props; + const { hidden } = this.state; + + const toggleText = hidden ? + : + ; + + return ([ +

+ {spoilerText} + {' '} + +

, +
+ {children} +
+ ]); + } +} + const makeMapStateToProps = () => { const getStatus = makeGetStatus(); @@ -69,7 +112,7 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -const mapDispatchToProps = (dispatch, { intl }) => ({ +const mapDispatchToProps = (dispatch, { intl, contextType }) => ({ onReply (status, router) { dispatch((_, getState) => { @@ -189,6 +232,33 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ })); }, + onUnfilter (status, onConfirm) { + dispatch((_, getState) => { + let state = getState(); + const serverSideType = toServerSideType(contextType); + const enabledFilters = state.get('filters', ImmutableList()).filter(filter => filter.get('context').includes(serverSideType) && (filter.get('expires_at') === null || Date.parse(filter.get('expires_at')) > (new Date()))).toArray(); + const searchIndex = status.get('search_index'); + const matchingFilters = enabledFilters.filter(filter => regexFromFilters([filter]).test(searchIndex)); + dispatch(openModal('CONFIRM', { + message: [ + , +
+ + + + +
    + {matchingFilters.map(filter =>
  • {filter.get('phrase')}
  • )} +
+
+
+ ], + confirm: intl.formatMessage(messages.unfilterConfirm), + onConfirm: onConfirm, + })); + }); + }, + onReport (status) { dispatch(initReport(status.get('account'), status)); }, diff --git a/app/javascript/flavours/glitch/selectors/index.js b/app/javascript/flavours/glitch/selectors/index.js index 9e458253275..3424058d30e 100644 --- a/app/javascript/flavours/glitch/selectors/index.js +++ b/app/javascript/flavours/glitch/selectors/index.js @@ -20,7 +20,7 @@ export const makeGetAccount = () => { }); }; -const toServerSideType = columnType => { +export const toServerSideType = columnType => { switch (columnType) { case 'home': case 'notifications': @@ -39,7 +39,7 @@ const toServerSideType = columnType => { const escapeRegExp = string => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -const regexFromFilters = filters => { +export const regexFromFilters = filters => { if (filters.size === 0) { return null; } diff --git a/app/javascript/flavours/glitch/styles/components/modal.scss b/app/javascript/flavours/glitch/styles/components/modal.scss index 65b2e75f0a4..b219688402e 100644 --- a/app/javascript/flavours/glitch/styles/components/modal.scss +++ b/app/javascript/flavours/glitch/styles/components/modal.scss @@ -820,3 +820,33 @@ left: 0; } } + +.filtered-status-info { + text-align: start; + + .spoiler__text { + margin-top: 20px; + } + + .account { + border-bottom: 0; + } + + .account__display-name strong { + color: $inverted-text-color; + } + + .status__content__spoiler { + display: none; + + &--visible { + display: flex; + } + } + + ul { + padding: 10px; + margin-left: 12px; + list-style: disc inside; + } +} diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss index fa115f21bd7..cf8f396930c 100644 --- a/app/javascript/flavours/glitch/styles/components/status.scss +++ b/app/javascript/flavours/glitch/styles/components/status.scss @@ -996,3 +996,19 @@ a.status-card.compact:hover { } } } + +.status__wrapper--filtered__button { + display: block; + font-size: 15px; + line-height: 20px; + color: lighten($ui-highlight-color, 8%); + border: 0; + background: transparent; + padding: 0; + padding-top: 8px; + + &:hover, + &:active { + text-decoration: underline; + } +}