From 8efdf1a8989a3ec621a01690d4d25f3b52a528e1 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 11 Oct 2018 01:31:03 +0200 Subject: [PATCH] Do not push DMs into the home feed (#8940) * Do not push DMs into the home feed * Show DMs column after sending a DM, if DMs column is not already shown --- app/javascript/mastodon/actions/compose.js | 29 ++++++++++--------- .../mastodon/actions/conversations.js | 11 +++++++ .../compose/components/compose_form.js | 6 +++- .../features/compose/components/upload.js | 6 +++- .../containers/compose_form_container.js | 4 +-- .../compose/containers/upload_container.js | 4 +-- .../features/direct_timeline/index.js | 5 +++- .../mastodon/reducers/conversations.js | 7 +++++ app/services/batched_remove_status_service.rb | 11 ------- app/services/fan_out_on_write_service.rb | 25 +--------------- app/services/remove_status_service.rb | 8 ----- 11 files changed, 52 insertions(+), 64 deletions(-) diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 6d975cd1e4..f726712283 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -56,7 +56,7 @@ export function changeCompose(text) { }; }; -export function replyCompose(status, router) { +export function replyCompose(status, routerHistory) { return (dispatch, getState) => { dispatch({ type: COMPOSE_REPLY, @@ -64,7 +64,7 @@ export function replyCompose(status, router) { }); if (!getState().getIn(['compose', 'mounted'])) { - router.push('/statuses/new'); + routerHistory.push('/statuses/new'); } }; }; @@ -81,7 +81,7 @@ export function resetCompose() { }; }; -export function mentionCompose(account, router) { +export function mentionCompose(account, routerHistory) { return (dispatch, getState) => { dispatch({ type: COMPOSE_MENTION, @@ -89,12 +89,12 @@ export function mentionCompose(account, router) { }); if (!getState().getIn(['compose', 'mounted'])) { - router.push('/statuses/new'); + routerHistory.push('/statuses/new'); } }; }; -export function directCompose(account, router) { +export function directCompose(account, routerHistory) { return (dispatch, getState) => { dispatch({ type: COMPOSE_DIRECT, @@ -102,12 +102,12 @@ export function directCompose(account, router) { }); if (!getState().getIn(['compose', 'mounted'])) { - router.push('/statuses/new'); + routerHistory.push('/statuses/new'); } }; }; -export function submitCompose() { +export function submitCompose(routerHistory) { return function (dispatch, getState) { const status = getState().getIn(['compose', 'text'], ''); const media = getState().getIn(['compose', 'media_attachments']); @@ -133,21 +133,22 @@ export function submitCompose() { dispatch(insertIntoTagHistory(response.data.tags, status)); dispatch(submitComposeSuccess({ ...response.data })); - // To make the app more responsive, immediately get the status into the columns + // To make the app more responsive, immediately push the status + // into the columns - const insertIfOnline = (timelineId) => { + const insertIfOnline = timelineId => { if (getState().getIn(['timelines', timelineId, 'items', 0]) !== null) { dispatch(updateTimeline(timelineId, { ...response.data })); } }; - insertIfOnline('home'); - - if (response.data.in_reply_to_id === null && response.data.visibility === 'public') { + if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0) { + routerHistory.push('/timelines/direct'); + } else if (response.data.visibility !== 'direct') { + insertIfOnline('home'); + } else if (response.data.in_reply_to_id === null && response.data.visibility === 'public') { insertIfOnline('community'); insertIfOnline('public'); - } else if (response.data.visibility === 'direct') { - insertIfOnline('direct'); } }).catch(function (error) { dispatch(submitComposeFail(error)); diff --git a/app/javascript/mastodon/actions/conversations.js b/app/javascript/mastodon/actions/conversations.js index 3840d23caf..cab05c1bab 100644 --- a/app/javascript/mastodon/actions/conversations.js +++ b/app/javascript/mastodon/actions/conversations.js @@ -5,11 +5,22 @@ import { importFetchedStatus, } from './importer'; +export const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT'; +export const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT'; + export const CONVERSATIONS_FETCH_REQUEST = 'CONVERSATIONS_FETCH_REQUEST'; export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS'; export const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL'; export const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE'; +export const mountConversations = () => ({ + type: CONVERSATIONS_MOUNT, +}); + +export const unmountConversations = () => ({ + type: CONVERSATIONS_UNMOUNT, +}); + export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => { dispatch(expandConversationsRequest()); diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js index 365114b931..27178fe198 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.js +++ b/app/javascript/mastodon/features/compose/components/compose_form.js @@ -30,6 +30,10 @@ const messages = defineMessages({ export default @injectIntl class ComposeForm extends ImmutablePureComponent { + static contextTypes = { + router: PropTypes.object, + }; + static propTypes = { intl: PropTypes.object.isRequired, text: PropTypes.string.isRequired, @@ -84,7 +88,7 @@ class ComposeForm extends ImmutablePureComponent { return; } - this.props.onSubmit(); + this.props.onSubmit(this.context.router.history); } onSuggestionsClearRequested = () => { diff --git a/app/javascript/mastodon/features/compose/components/upload.js b/app/javascript/mastodon/features/compose/components/upload.js index e377da90c0..66c93452ce 100644 --- a/app/javascript/mastodon/features/compose/components/upload.js +++ b/app/javascript/mastodon/features/compose/components/upload.js @@ -14,6 +14,10 @@ const messages = defineMessages({ export default @injectIntl class Upload extends ImmutablePureComponent { + static contextTypes = { + router: PropTypes.object, + }; + static propTypes = { media: ImmutablePropTypes.map.isRequired, intl: PropTypes.object.isRequired, @@ -37,7 +41,7 @@ class Upload extends ImmutablePureComponent { handleSubmit = () => { this.handleInputBlur(); - this.props.onSubmit(); + this.props.onSubmit(this.context.router.history); } handleUndoClick = () => { diff --git a/app/javascript/mastodon/features/compose/containers/compose_form_container.js b/app/javascript/mastodon/features/compose/containers/compose_form_container.js index 3822dd711f..5d7fb8852b 100644 --- a/app/javascript/mastodon/features/compose/containers/compose_form_container.js +++ b/app/javascript/mastodon/features/compose/containers/compose_form_container.js @@ -33,8 +33,8 @@ const mapDispatchToProps = (dispatch) => ({ dispatch(changeCompose(text)); }, - onSubmit () { - dispatch(submitCompose()); + onSubmit (router) { + dispatch(submitCompose(router)); }, onClearSuggestions () { diff --git a/app/javascript/mastodon/features/compose/containers/upload_container.js b/app/javascript/mastodon/features/compose/containers/upload_container.js index 9f3aab4bcd..b6d81f03ac 100644 --- a/app/javascript/mastodon/features/compose/containers/upload_container.js +++ b/app/javascript/mastodon/features/compose/containers/upload_container.js @@ -22,8 +22,8 @@ const mapDispatchToProps = dispatch => ({ dispatch(openModal('FOCAL_POINT', { id })); }, - onSubmit () { - dispatch(submitCompose()); + onSubmit (router) { + dispatch(submitCompose(router)); }, }); diff --git a/app/javascript/mastodon/features/direct_timeline/index.js b/app/javascript/mastodon/features/direct_timeline/index.js index 41ec73d98f..d202f3bfd9 100644 --- a/app/javascript/mastodon/features/direct_timeline/index.js +++ b/app/javascript/mastodon/features/direct_timeline/index.js @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import Column from '../../components/column'; import ColumnHeader from '../../components/column_header'; -import { expandConversations } from '../../actions/conversations'; +import { mountConversations, unmountConversations, expandConversations } from '../../actions/conversations'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { connectDirectStream } from '../../actions/streaming'; @@ -48,11 +48,14 @@ class DirectTimeline extends React.PureComponent { componentDidMount () { const { dispatch } = this.props; + dispatch(mountConversations()); dispatch(expandConversations()); this.disconnect = dispatch(connectDirectStream()); } componentWillUnmount () { + this.props.dispatch(unmountConversations()); + if (this.disconnect) { this.disconnect(); this.disconnect = null; diff --git a/app/javascript/mastodon/reducers/conversations.js b/app/javascript/mastodon/reducers/conversations.js index f339abf56e..6b3f22d25b 100644 --- a/app/javascript/mastodon/reducers/conversations.js +++ b/app/javascript/mastodon/reducers/conversations.js @@ -1,5 +1,7 @@ import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { + CONVERSATIONS_MOUNT, + CONVERSATIONS_UNMOUNT, CONVERSATIONS_FETCH_REQUEST, CONVERSATIONS_FETCH_SUCCESS, CONVERSATIONS_FETCH_FAIL, @@ -11,6 +13,7 @@ const initialState = ImmutableMap({ items: ImmutableList(), isLoading: false, hasMore: true, + mounted: false, }); const conversationToMap = item => ImmutableMap({ @@ -73,6 +76,10 @@ export default function conversations(state = initialState, action) { return expandNormalizedConversations(state, action.conversations, action.next); case CONVERSATIONS_UPDATE: return updateConversation(state, action.conversation); + case CONVERSATIONS_MOUNT: + return state.update('mounted', count => count + 1); + case CONVERSATIONS_UNMOUNT: + return state.update('mounted', count => count - 1); default: return state; } diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb index ebb4034aaf..2fcb3cc66b 100644 --- a/app/services/batched_remove_status_service.rb +++ b/app/services/batched_remove_status_service.rb @@ -39,7 +39,6 @@ class BatchedRemoveStatusService < BaseService # Cannot be batched statuses.each do |status| unpush_from_public_timelines(status) - unpush_from_direct_timelines(status) if status.direct_visibility? batch_salmon_slaps(status) if status.local? end @@ -96,16 +95,6 @@ class BatchedRemoveStatusService < BaseService end end - def unpush_from_direct_timelines(status) - payload = @json_payloads[status.id] - redis.pipelined do - @mentions[status.id].each do |mention| - redis.publish("timeline:direct:#{mention.account.id}", payload) if mention.account.local? - end - redis.publish("timeline:direct:#{status.account.id}", payload) if status.account.local? - end - end - def batch_salmon_slaps(status) return if @mentions[status.id].empty? diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index ab520276b2..5ddddf3a90 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -6,15 +6,12 @@ class FanOutOnWriteService < BaseService def call(status) raise Mastodon::RaceConditionError if status.visibility.nil? - deliver_to_self(status) if status.account.local? - render_anonymous_payload(status) if status.direct_visibility? - deliver_to_mentioned_followers(status) - deliver_to_direct_timelines(status) deliver_to_own_conversation(status) else + deliver_to_self(status) if status.account.local? deliver_to_followers(status) deliver_to_lists(status) end @@ -56,16 +53,6 @@ class FanOutOnWriteService < BaseService end end - def deliver_to_mentioned_followers(status) - Rails.logger.debug "Delivering status #{status.id} to mentioned followers" - - status.mentions.includes(:account).each do |mention| - mentioned_account = mention.account - next if !mentioned_account.local? || !mentioned_account.following?(status.account) || FeedManager.instance.filter?(:home, status, mention.account_id) - FeedManager.instance.push_to_home(mentioned_account, status) - end - end - def render_anonymous_payload(status) @payload = InlineRenderer.render(status, nil, :status) @payload = Oj.dump(event: :update, payload: @payload) @@ -94,16 +81,6 @@ class FanOutOnWriteService < BaseService Redis.current.publish('timeline:public:local:media', @payload) if status.local? end - def deliver_to_direct_timelines(status) - Rails.logger.debug "Delivering status #{status.id} to direct timelines" - - status.mentions.includes(:account).each do |mention| - Redis.current.publish("timeline:direct:#{mention.account.id}", @payload) if mention.account.local? - end - - Redis.current.publish("timeline:direct:#{status.account.id}", @payload) if status.account.local? - end - def deliver_to_own_conversation(status) AccountConversation.add_status(status.account, status) end diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index 1a53093b89..1ee645e6d8 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -21,7 +21,6 @@ class RemoveStatusService < BaseService remove_from_hashtags remove_from_public remove_from_media if status.media_attachments.any? - remove_from_direct if status.direct_visibility? @status.destroy! @@ -153,13 +152,6 @@ class RemoveStatusService < BaseService Redis.current.publish('timeline:public:local:media', @payload) if @status.local? end - def remove_from_direct - @mentions.each do |mention| - Redis.current.publish("timeline:direct:#{mention.account.id}", @payload) if mention.account.local? - end - Redis.current.publish("timeline:direct:#{@account.id}", @payload) if @account.local? - end - def redis Redis.current end