diff options
author | Eugen Rochko <eugen@zeonfederated.com> | 2016-11-23 22:57:57 +0100 |
---|---|---|
committer | Eugen Rochko <eugen@zeonfederated.com> | 2016-11-23 22:57:57 +0100 |
commit | 7cee27f51790617859da87c7aef432373b40aad4 (patch) | |
tree | 68bec6934312f7155faddc96f4b77473ba9a8205 /app | |
parent | 3373ae02ded0ac5847350da9f9550721cdebe732 (diff) |
Fix unfollows not clearing reblogs, fix blocks not clearing reblogs and notifications,
skip ActionCable for follow/unfollow/block events, instead clear UI from blocked account's posts instantly if block request succeeds. Add forgotten i18n for sensitive content
Diffstat (limited to 'app')
13 files changed, 86 insertions, 27 deletions
diff --git a/app/assets/javascripts/components/actions/accounts.jsx b/app/assets/javascripts/components/actions/accounts.jsx index 4a0777a64..759435afe 100644 --- a/app/assets/javascripts/components/actions/accounts.jsx +++ b/app/assets/javascripts/components/actions/accounts.jsx @@ -246,7 +246,8 @@ export function blockAccount(id) { dispatch(blockAccountRequest(id)); api(getState).post(`/api/v1/accounts/${id}/block`).then(response => { - dispatch(blockAccountSuccess(response.data)); + // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers + dispatch(blockAccountSuccess(response.data, getState().get('statuses'))); }).catch(error => { dispatch(blockAccountFail(id, error)); }); @@ -272,10 +273,11 @@ export function blockAccountRequest(id) { }; }; -export function blockAccountSuccess(relationship) { +export function blockAccountSuccess(relationship, statuses) { return { type: ACCOUNT_BLOCK_SUCCESS, - relationship + relationship, + statuses }; }; diff --git a/app/assets/javascripts/components/components/status.jsx b/app/assets/javascripts/components/components/status.jsx index bf851e5bf..603561ab3 100644 --- a/app/assets/javascripts/components/components/status.jsx +++ b/app/assets/javascripts/components/components/status.jsx @@ -34,6 +34,7 @@ const Status = React.createClass({ onReblog: React.PropTypes.func, onDelete: React.PropTypes.func, onOpenMedia: React.PropTypes.func, + onBlock: React.PropTypes.func, me: React.PropTypes.number, muted: React.PropTypes.bool }, diff --git a/app/assets/javascripts/components/components/status_action_bar.jsx b/app/assets/javascripts/components/components/status_action_bar.jsx index dec1decff..35feda88b 100644 --- a/app/assets/javascripts/components/components/status_action_bar.jsx +++ b/app/assets/javascripts/components/components/status_action_bar.jsx @@ -7,6 +7,7 @@ import { defineMessages, injectIntl } from 'react-intl'; const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, mention: { id: 'status.mention', defaultMessage: 'Mention' }, + block: { id: 'account.block', defaultMessage: 'Block' }, reply: { id: 'status.reply', defaultMessage: 'Reply' }, reblog: { id: 'status.reblog', defaultMessage: 'Reblog' }, favourite: { id: 'status.favourite', defaultMessage: 'Favourite' } @@ -24,7 +25,8 @@ const StatusActionBar = React.createClass({ onFavourite: React.PropTypes.func, onReblog: React.PropTypes.func, onDelete: React.PropTypes.func, - onMention: React.PropTypes.func + onMention: React.PropTypes.func, + onBlock: React.PropTypes.func }, mixins: [PureRenderMixin], @@ -49,6 +51,10 @@ const StatusActionBar = React.createClass({ this.props.onMention(this.props.status.get('account')); }, + handleBlockClick () { + this.props.onBlock(this.props.status.get('account')); + }, + render () { const { status, me, intl } = this.props; let menu = []; @@ -57,6 +63,7 @@ const StatusActionBar = React.createClass({ menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); } else { menu.push({ text: intl.formatMessage(messages.mention), action: this.handleMentionClick }); + menu.push({ text: intl.formatMessage(messages.block), action: this.handleBlockClick }); } return ( diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx index 87c7c65f3..c9f037ec2 100644 --- a/app/assets/javascripts/components/containers/mastodon.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -75,11 +75,6 @@ const Mastodon = React.createClass({ return store.dispatch(updateTimeline(data.timeline, JSON.parse(data.message))); case 'delete': return store.dispatch(deleteFromTimelines(data.id)); - case 'merge': - case 'unmerge': - return store.dispatch(refreshTimeline('home', true)); - case 'block': - return store.dispatch(refreshTimeline('mentions', true)); case 'notification': return store.dispatch(updateNotifications(JSON.parse(data.message), getMessagesForLocale(locale), locale)); } diff --git a/app/assets/javascripts/components/containers/status_container.jsx b/app/assets/javascripts/components/containers/status_container.jsx index 28756b5ef..6a882eab4 100644 --- a/app/assets/javascripts/components/containers/status_container.jsx +++ b/app/assets/javascripts/components/containers/status_container.jsx @@ -1,18 +1,19 @@ -import { connect } from 'react-redux'; -import Status from '../components/status'; +import { connect } from 'react-redux'; +import Status from '../components/status'; import { makeGetStatus } from '../selectors'; import { replyCompose, mentionCompose -} from '../actions/compose'; +} from '../actions/compose'; import { reblog, favourite, unreblog, unfavourite -} from '../actions/interactions'; -import { deleteStatus } from '../actions/statuses'; -import { openMedia } from '../actions/modal'; +} from '../actions/interactions'; +import { blockAccount } from '../actions/accounts'; +import { deleteStatus } from '../actions/statuses'; +import { openMedia } from '../actions/modal'; import { createSelector } from 'reselect' const mapStateToProps = (state, props) => ({ @@ -91,6 +92,10 @@ const mapDispatchToProps = (dispatch) => ({ onOpenMedia (url) { dispatch(openMedia(url)); + }, + + onBlock (account) { + dispatch(blockAccount(account.get('id'))); } }); diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx index 7c42e481d..5ad1ca172 100644 --- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx @@ -8,7 +8,7 @@ import Autosuggest from 'react-autosuggest'; import AutosuggestAccountContainer from '../../compose/containers/autosuggest_account_container'; import { debounce } from 'react-decoration'; import UploadButtonContainer from '../containers/upload_button_container'; -import { defineMessages, injectIntl } from 'react-intl'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import Toggle from 'react-toggle'; const messages = defineMessages({ @@ -188,7 +188,7 @@ const ComposeForm = React.createClass({ <label style={{ display: 'block', lineHeight: '24px', verticalAlign: 'middle', marginTop: '10px', borderTop: '1px solid #616b86', paddingTop: '10px' }}> <Toggle checked={this.props.sensitive} onChange={this.handleChangeSensitivity} /> - <span style={{ display: 'inline-block', verticalAlign: 'middle', marginBottom: '14px', marginLeft: '8px', color: '#9baec8' }}>Sensitive content</span> + <span style={{ display: 'inline-block', verticalAlign: 'middle', marginBottom: '14px', marginLeft: '8px', color: '#9baec8' }}><FormattedMessage id='compose_form.sensitive' defaultMessage='Mark content as sensitive' /></span> </label> </div> ); diff --git a/app/assets/javascripts/components/locales/en.jsx b/app/assets/javascripts/components/locales/en.jsx index f3326bf90..0ea324f66 100644 --- a/app/assets/javascripts/components/locales/en.jsx +++ b/app/assets/javascripts/components/locales/en.jsx @@ -8,6 +8,8 @@ const en = { "status.reblog": "Reblog", "status.favourite": "Favourite", "status.reblogged_by": "{name} reblogged", + "status.sensitive_warning": "Sensitive content", + "status.sensitive_toggle": "Click to view", "video_player.toggle_sound": "Toggle sound", "account.mention": "Mention", "account.edit_profile": "Edit profile", @@ -35,6 +37,7 @@ const en = { "tabs_bar.notifications": "Notifications", "compose_form.placeholder": "What is on your mind?", "compose_form.publish": "Toot", + "compose_form.sensitive": "Mark content as sensitive", "navigation_bar.settings": "Settings", "navigation_bar.public_timeline": "Public timeline", "navigation_bar.logout": "Logout", diff --git a/app/assets/javascripts/components/reducers/notifications.jsx b/app/assets/javascripts/components/reducers/notifications.jsx index 0e67e732a..617a833d2 100644 --- a/app/assets/javascripts/components/reducers/notifications.jsx +++ b/app/assets/javascripts/components/reducers/notifications.jsx @@ -3,6 +3,7 @@ import { NOTIFICATIONS_REFRESH_SUCCESS, NOTIFICATIONS_EXPAND_SUCCESS } from '../actions/notifications'; +import { ACCOUNT_BLOCK_SUCCESS } from '../actions/accounts'; import Immutable from 'immutable'; const initialState = Immutable.Map({ @@ -43,6 +44,10 @@ const appendNormalizedNotifications = (state, notifications, next) => { return state.update('items', list => list.push(...items)).set('next', next); }; +const filterNotifications = (state, relationship) => { + return state.update('items', list => list.filterNot(item => item.get('account') === relationship.id)); +}; + export default function notifications(state = initialState, action) { switch(action.type) { case NOTIFICATIONS_UPDATE: @@ -51,6 +56,8 @@ export default function notifications(state = initialState, action) { return normalizeNotifications(state, action.notifications, action.next); case NOTIFICATIONS_EXPAND_SUCCESS: return appendNormalizedNotifications(state, action.notifications, action.next); + case ACCOUNT_BLOCK_SUCCESS: + return filterNotifications(state, action.relationship); default: return state; } diff --git a/app/assets/javascripts/components/reducers/statuses.jsx b/app/assets/javascripts/components/reducers/statuses.jsx index 2a24a75e4..f42b1a481 100644 --- a/app/assets/javascripts/components/reducers/statuses.jsx +++ b/app/assets/javascripts/components/reducers/statuses.jsx @@ -16,7 +16,8 @@ import { } from '../actions/timelines'; import { ACCOUNT_TIMELINE_FETCH_SUCCESS, - ACCOUNT_TIMELINE_EXPAND_SUCCESS + ACCOUNT_TIMELINE_EXPAND_SUCCESS, + ACCOUNT_BLOCK_SUCCESS } from '../actions/accounts'; import { NOTIFICATIONS_UPDATE, @@ -56,6 +57,18 @@ const deleteStatus = (state, id, references) => { return state.delete(id); }; +const filterStatuses = (state, relationship) => { + state.forEach(status => { + if (status.get('account') !== relationship.id) { + return; + } + + state = deleteStatus(state, status.get('id'), state.filter(item => item.get('reblog') === status.get('id'))); + }); + + return state; +}; + const initialState = Immutable.Map(); export default function statuses(state = initialState, action) { @@ -79,6 +92,8 @@ export default function statuses(state = initialState, action) { return normalizeStatuses(state, action.statuses); case TIMELINE_DELETE: return deleteStatus(state, action.id, action.references); + case ACCOUNT_BLOCK_SUCCESS: + return filterStatuses(state, action.relationship); default: return state; } diff --git a/app/assets/javascripts/components/reducers/timelines.jsx b/app/assets/javascripts/components/reducers/timelines.jsx index 9e79a4100..358734eaf 100644 --- a/app/assets/javascripts/components/reducers/timelines.jsx +++ b/app/assets/javascripts/components/reducers/timelines.jsx @@ -13,7 +13,8 @@ import { import { ACCOUNT_FETCH_SUCCESS, ACCOUNT_TIMELINE_FETCH_SUCCESS, - ACCOUNT_TIMELINE_EXPAND_SUCCESS + ACCOUNT_TIMELINE_EXPAND_SUCCESS, + ACCOUNT_BLOCK_SUCCESS } from '../actions/accounts'; import { STATUS_FETCH_SUCCESS, @@ -140,6 +141,21 @@ const deleteStatus = (state, id, accountId, references) => { return state; }; +const filterTimelines = (state, relationship, statuses) => { + let references; + + statuses.forEach(status => { + if (status.get('account') !== relationship.id) { + return; + } + + references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => [item.get('id'), item.get('account')]); + state = deleteStatus(state, status.get('id'), status.get('account'), references); + }); + + return state; +}; + const normalizeContext = (state, id, ancestors, descendants) => { const ancestorsIds = ancestors.map(ancestor => ancestor.get('id')); const descendantsIds = descendants.map(descendant => descendant.get('id')); @@ -166,6 +182,8 @@ export default function timelines(state = initialState, action) { return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses), action.replace); case ACCOUNT_TIMELINE_EXPAND_SUCCESS: return appendNormalizedAccountTimeline(state, action.id, Immutable.fromJS(action.statuses)); + case ACCOUNT_BLOCK_SUCCESS: + return filterTimelines(state, action.relationship, action.statuses); default: return state; } diff --git a/app/services/block_service.rb b/app/services/block_service.rb index 388a592e0..6a032a5a1 100644 --- a/app/services/block_service.rb +++ b/app/services/block_service.rb @@ -6,19 +6,27 @@ class BlockService < BaseService UnfollowService.new.call(account, target_account) if account.following?(target_account) account.block!(target_account) - clear_mentions(account, target_account) + clear_timelines(account, target_account) + clear_notifications(account, target_account) end private - def clear_mentions(account, target_account) - timeline_key = FeedManager.instance.key(:mentions, account.id) + def clear_timelines(account, target_account) + mentions_key = FeedManager.instance.key(:mentions, account.id) + home_key = FeedManager.instance.key(:home, account.id) target_account.statuses.select('id').find_each do |status| - redis.zrem(timeline_key, status.id) + redis.zrem(mentions_key, status.id) + redis.zrem(home_key, status.id) end + end - FeedManager.instance.broadcast(account.id, type: 'block', id: target_account.id) + def clear_notifications(account, target_account) + Notification.where(account: account).joins(:follow).where(activity_type: 'Follow', follows: { account_id: target_account.id }).destroy_all + Notification.where(account: account).joins(mention: :status).where(activity_type: 'Mention', statuses: { account_id: target_account.id }).destroy_all + Notification.where(account: account).joins(:favourite).where(activity_type: 'Favourite', favourites: { account_id: target_account.id }).destroy_all + Notification.where(account: account).joins(:status).where(activity_type: 'Status', statuses: { account_id: target_account.id }).destroy_all end def redis diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb index cdae254bf..a57e1b28a 100644 --- a/app/services/follow_service.rb +++ b/app/services/follow_service.rb @@ -33,7 +33,6 @@ class FollowService < BaseService end FeedManager.instance.trim(:home, into_account.id) - FeedManager.instance.broadcast(into_account.id, type: 'merge') end def redis diff --git a/app/services/unfollow_service.rb b/app/services/unfollow_service.rb index b3386a99c..7973a3611 100644 --- a/app/services/unfollow_service.rb +++ b/app/services/unfollow_service.rb @@ -17,9 +17,8 @@ class UnfollowService < BaseService from_account.statuses.select('id').find_each do |status| redis.zrem(timeline_key, status.id) + redis.zremrangebyscore(timeline_key, status.id, status.id) end - - FeedManager.instance.broadcast(into_account.id, type: 'unmerge') end def redis |