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
This commit is contained in:
Eugen Rochko 2018-10-11 01:31:03 +02:00 committed by GitHub
parent ce087ef889
commit 8efdf1a898
11 changed files with 52 additions and 64 deletions

View file

@ -56,7 +56,7 @@ export function changeCompose(text) {
}; };
}; };
export function replyCompose(status, router) { export function replyCompose(status, routerHistory) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch({ dispatch({
type: COMPOSE_REPLY, type: COMPOSE_REPLY,
@ -64,7 +64,7 @@ export function replyCompose(status, router) {
}); });
if (!getState().getIn(['compose', 'mounted'])) { 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) => { return (dispatch, getState) => {
dispatch({ dispatch({
type: COMPOSE_MENTION, type: COMPOSE_MENTION,
@ -89,12 +89,12 @@ export function mentionCompose(account, router) {
}); });
if (!getState().getIn(['compose', 'mounted'])) { 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) => { return (dispatch, getState) => {
dispatch({ dispatch({
type: COMPOSE_DIRECT, type: COMPOSE_DIRECT,
@ -102,12 +102,12 @@ export function directCompose(account, router) {
}); });
if (!getState().getIn(['compose', 'mounted'])) { if (!getState().getIn(['compose', 'mounted'])) {
router.push('/statuses/new'); routerHistory.push('/statuses/new');
} }
}; };
}; };
export function submitCompose() { export function submitCompose(routerHistory) {
return function (dispatch, getState) { return function (dispatch, getState) {
const status = getState().getIn(['compose', 'text'], ''); const status = getState().getIn(['compose', 'text'], '');
const media = getState().getIn(['compose', 'media_attachments']); const media = getState().getIn(['compose', 'media_attachments']);
@ -133,21 +133,22 @@ export function submitCompose() {
dispatch(insertIntoTagHistory(response.data.tags, status)); dispatch(insertIntoTagHistory(response.data.tags, status));
dispatch(submitComposeSuccess({ ...response.data })); 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) { if (getState().getIn(['timelines', timelineId, 'items', 0]) !== null) {
dispatch(updateTimeline(timelineId, { ...response.data })); dispatch(updateTimeline(timelineId, { ...response.data }));
} }
}; };
if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0) {
routerHistory.push('/timelines/direct');
} else if (response.data.visibility !== 'direct') {
insertIfOnline('home'); insertIfOnline('home');
} else if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
insertIfOnline('community'); insertIfOnline('community');
insertIfOnline('public'); insertIfOnline('public');
} else if (response.data.visibility === 'direct') {
insertIfOnline('direct');
} }
}).catch(function (error) { }).catch(function (error) {
dispatch(submitComposeFail(error)); dispatch(submitComposeFail(error));

View file

@ -5,11 +5,22 @@ import {
importFetchedStatus, importFetchedStatus,
} from './importer'; } 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_REQUEST = 'CONVERSATIONS_FETCH_REQUEST';
export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS'; export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS';
export const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL'; export const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL';
export const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE'; export const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE';
export const mountConversations = () => ({
type: CONVERSATIONS_MOUNT,
});
export const unmountConversations = () => ({
type: CONVERSATIONS_UNMOUNT,
});
export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => { export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
dispatch(expandConversationsRequest()); dispatch(expandConversationsRequest());

View file

@ -30,6 +30,10 @@ const messages = defineMessages({
export default @injectIntl export default @injectIntl
class ComposeForm extends ImmutablePureComponent { class ComposeForm extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
};
static propTypes = { static propTypes = {
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
text: PropTypes.string.isRequired, text: PropTypes.string.isRequired,
@ -84,7 +88,7 @@ class ComposeForm extends ImmutablePureComponent {
return; return;
} }
this.props.onSubmit(); this.props.onSubmit(this.context.router.history);
} }
onSuggestionsClearRequested = () => { onSuggestionsClearRequested = () => {

View file

@ -14,6 +14,10 @@ const messages = defineMessages({
export default @injectIntl export default @injectIntl
class Upload extends ImmutablePureComponent { class Upload extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
};
static propTypes = { static propTypes = {
media: ImmutablePropTypes.map.isRequired, media: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -37,7 +41,7 @@ class Upload extends ImmutablePureComponent {
handleSubmit = () => { handleSubmit = () => {
this.handleInputBlur(); this.handleInputBlur();
this.props.onSubmit(); this.props.onSubmit(this.context.router.history);
} }
handleUndoClick = () => { handleUndoClick = () => {

View file

@ -33,8 +33,8 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(changeCompose(text)); dispatch(changeCompose(text));
}, },
onSubmit () { onSubmit (router) {
dispatch(submitCompose()); dispatch(submitCompose(router));
}, },
onClearSuggestions () { onClearSuggestions () {

View file

@ -22,8 +22,8 @@ const mapDispatchToProps = dispatch => ({
dispatch(openModal('FOCAL_POINT', { id })); dispatch(openModal('FOCAL_POINT', { id }));
}, },
onSubmit () { onSubmit (router) {
dispatch(submitCompose()); dispatch(submitCompose(router));
}, },
}); });

View file

@ -3,7 +3,7 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Column from '../../components/column'; import Column from '../../components/column';
import ColumnHeader from '../../components/column_header'; 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 { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connectDirectStream } from '../../actions/streaming'; import { connectDirectStream } from '../../actions/streaming';
@ -48,11 +48,14 @@ class DirectTimeline extends React.PureComponent {
componentDidMount () { componentDidMount () {
const { dispatch } = this.props; const { dispatch } = this.props;
dispatch(mountConversations());
dispatch(expandConversations()); dispatch(expandConversations());
this.disconnect = dispatch(connectDirectStream()); this.disconnect = dispatch(connectDirectStream());
} }
componentWillUnmount () { componentWillUnmount () {
this.props.dispatch(unmountConversations());
if (this.disconnect) { if (this.disconnect) {
this.disconnect(); this.disconnect();
this.disconnect = null; this.disconnect = null;

View file

@ -1,5 +1,7 @@
import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { import {
CONVERSATIONS_MOUNT,
CONVERSATIONS_UNMOUNT,
CONVERSATIONS_FETCH_REQUEST, CONVERSATIONS_FETCH_REQUEST,
CONVERSATIONS_FETCH_SUCCESS, CONVERSATIONS_FETCH_SUCCESS,
CONVERSATIONS_FETCH_FAIL, CONVERSATIONS_FETCH_FAIL,
@ -11,6 +13,7 @@ const initialState = ImmutableMap({
items: ImmutableList(), items: ImmutableList(),
isLoading: false, isLoading: false,
hasMore: true, hasMore: true,
mounted: false,
}); });
const conversationToMap = item => ImmutableMap({ const conversationToMap = item => ImmutableMap({
@ -73,6 +76,10 @@ export default function conversations(state = initialState, action) {
return expandNormalizedConversations(state, action.conversations, action.next); return expandNormalizedConversations(state, action.conversations, action.next);
case CONVERSATIONS_UPDATE: case CONVERSATIONS_UPDATE:
return updateConversation(state, action.conversation); 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: default:
return state; return state;
} }

View file

@ -39,7 +39,6 @@ class BatchedRemoveStatusService < BaseService
# Cannot be batched # Cannot be batched
statuses.each do |status| statuses.each do |status|
unpush_from_public_timelines(status) unpush_from_public_timelines(status)
unpush_from_direct_timelines(status) if status.direct_visibility?
batch_salmon_slaps(status) if status.local? batch_salmon_slaps(status) if status.local?
end end
@ -96,16 +95,6 @@ class BatchedRemoveStatusService < BaseService
end end
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) def batch_salmon_slaps(status)
return if @mentions[status.id].empty? return if @mentions[status.id].empty?

View file

@ -6,15 +6,12 @@ class FanOutOnWriteService < BaseService
def call(status) def call(status)
raise Mastodon::RaceConditionError if status.visibility.nil? raise Mastodon::RaceConditionError if status.visibility.nil?
deliver_to_self(status) if status.account.local?
render_anonymous_payload(status) render_anonymous_payload(status)
if status.direct_visibility? if status.direct_visibility?
deliver_to_mentioned_followers(status)
deliver_to_direct_timelines(status)
deliver_to_own_conversation(status) deliver_to_own_conversation(status)
else else
deliver_to_self(status) if status.account.local?
deliver_to_followers(status) deliver_to_followers(status)
deliver_to_lists(status) deliver_to_lists(status)
end end
@ -56,16 +53,6 @@ class FanOutOnWriteService < BaseService
end end
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) def render_anonymous_payload(status)
@payload = InlineRenderer.render(status, nil, :status) @payload = InlineRenderer.render(status, nil, :status)
@payload = Oj.dump(event: :update, payload: @payload) @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? Redis.current.publish('timeline:public:local:media', @payload) if status.local?
end 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) def deliver_to_own_conversation(status)
AccountConversation.add_status(status.account, status) AccountConversation.add_status(status.account, status)
end end

View file

@ -21,7 +21,6 @@ class RemoveStatusService < BaseService
remove_from_hashtags remove_from_hashtags
remove_from_public remove_from_public
remove_from_media if status.media_attachments.any? remove_from_media if status.media_attachments.any?
remove_from_direct if status.direct_visibility?
@status.destroy! @status.destroy!
@ -153,13 +152,6 @@ class RemoveStatusService < BaseService
Redis.current.publish('timeline:public:local:media', @payload) if @status.local? Redis.current.publish('timeline:public:local:media', @payload) if @status.local?
end 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 def redis
Redis.current Redis.current
end end