Skip to content

Commit

Permalink
[MM-12582 & MM-12745] Migrate PostActions.handleNewPost to redux acti…
Browse files Browse the repository at this point in the history
…ons (mattermost#1888)

* Migrate PostActions.handleNewPost to redux actions

* fix as per comment

* feedback review
  • Loading branch information
saturninoabril committed Oct 17, 2018
1 parent c594c2e commit a53fdfe
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 99 deletions.
4 changes: 2 additions & 2 deletions actions/global_actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ export function sendEphemeralPost(message, channelId, parentId) {
props: {},
};

handleNewPost(post);
dispatch(handleNewPost(post));
}

export function sendAddToChannelEphemeralPost(user, addedUsername, addedUserId, channelId, postRootId = '', timestamp) {
Expand All @@ -364,7 +364,7 @@ export function sendAddToChannelEphemeralPost(user, addedUsername, addedUserId,
},
};

handleNewPost(post);
dispatch(handleNewPost(post));
}

export function newLocalizationSelected(locale) {
Expand Down
93 changes: 25 additions & 68 deletions actions/post_actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
// See LICENSE.txt for license information.

import {batchActions} from 'redux-batched-actions';

import {PostTypes, SearchTypes} from 'mattermost-redux/action_types';
import {getMyChannelMember} from 'mattermost-redux/actions/channels';
import {getMyChannelMember as getMyChannelMemberSelector} from 'mattermost-redux/selectors/entities/channels';
import * as PostActions from 'mattermost-redux/actions/posts';
import * as Selectors from 'mattermost-redux/selectors/entities/posts';
import * as PostSelectors from 'mattermost-redux/selectors/entities/posts';
import {comparePosts} from 'mattermost-redux/utils/post_utils';

import {sendDesktopNotification} from 'actions/notification_actions.jsx';
import * as StorageActions from 'actions/storage';
import {loadNewDMIfNeeded, loadNewGMIfNeeded} from 'actions/user_actions.jsx';
import * as RhsActions from 'actions/views/rhs';
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import PostStore from 'stores/post_store.jsx';
import store from 'stores/redux_store.jsx';
import {isEmbedVisible} from 'selectors/posts';
Expand All @@ -27,76 +27,33 @@ import {
import {EMOJI_PATTERN} from 'utils/emoticons.jsx';
import * as UserAgent from 'utils/user_agent';

import {completePostReceive} from './post_utils';

const dispatch = store.dispatch;
const getState = store.getState;

export function handleNewPost(post, msg) {
let websocketMessageProps = {};
if (msg) {
websocketMessageProps = msg.data;
}

if (ChannelStore.getMyMember(post.channel_id)) {
completePostReceive(post, websocketMessageProps);
} else {
getMyChannelMember(post.channel_id)(dispatch, getState).then(() => completePostReceive(post, websocketMessageProps));
}

if (msg && msg.data) {
if (msg.data.channel_type === Constants.DM_CHANNEL) {
loadNewDMIfNeeded(post.channel_id);
} else if (msg.data.channel_type === Constants.GM_CHANNEL) {
loadNewGMIfNeeded(post.channel_id);
return async (doDispatch, doGetState) => {
let websocketMessageProps = {};
if (msg) {
websocketMessageProps = msg.data;
}
}
}

function completePostReceive(post, websocketMessageProps) {
if (post.root_id && Selectors.getPost(getState(), post.root_id) == null) {
PostActions.getPostThread(post.root_id)(dispatch, getState).then(
(data) => {
dispatchPostActions(post, websocketMessageProps);
PostActions.getProfilesAndStatusesForPosts(data.posts, dispatch, getState);
}
);

return;
}

dispatchPostActions(post, websocketMessageProps);
}

function dispatchPostActions(post, websocketMessageProps) {
const {currentChannelId} = getState().entities.channels;

if (post.channel_id === currentChannelId) {
dispatch({
type: ActionTypes.INCREASE_POST_VISIBILITY,
data: post.channel_id,
amount: 1,
});
}

// Need manual dispatch to remove pending post
dispatch({
type: PostTypes.RECEIVED_POSTS,
data: {
order: [],
posts: {
[post.id]: post,
},
},
channelId: post.channel_id,
});
const myChannelMember = getMyChannelMemberSelector(doGetState(), post.channel_id);
if (myChannelMember && Object.keys(myChannelMember).length === 0 && myChannelMember.constructor === 'Object') {
await doDispatch(getMyChannelMember(post.channel_id));
}

// Still needed to update unreads
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_POST,
post,
websocketMessageProps,
});
doDispatch(completePostReceive(post, websocketMessageProps));

dispatch(sendDesktopNotification(post, websocketMessageProps));
if (msg && msg.data) {
if (msg.data.channel_type === Constants.DM_CHANNEL) {
loadNewDMIfNeeded(post.channel_id);
} else if (msg.data.channel_type === Constants.GM_CHANNEL) {
loadNewGMIfNeeded(post.channel_id);
}
}
};
}

export async function flagPost(postId) {
Expand All @@ -113,7 +70,7 @@ export async function flagPost(postId) {

const posts = {};
results.forEach((id) => {
posts[id] = Selectors.getPost(getState(), id);
posts[id] = PostSelectors.getPost(getState(), id);
});

results.sort((a, b) => comparePosts(posts[a], posts[b]));
Expand Down Expand Up @@ -141,7 +98,7 @@ export async function unflagPost(postId) {

const posts = {};
results.forEach((id) => {
posts[id] = Selectors.getPost(getState(), id);
posts[id] = PostSelectors.getPost(getState(), id);
});

dispatch({
Expand Down Expand Up @@ -281,7 +238,7 @@ export function doPostAction(postId, actionId) {
export function setEditingPost(postId = '', commentCount = 0, refocusId = '', title = '', isRHS = false) {
return async (doDispatch, doGetState) => {
const state = doGetState();
const post = Selectors.getPost(state, postId);
const post = PostSelectors.getPost(state, postId);

if (!post || post.pending_post_id === postId) {
return {data: false};
Expand Down
105 changes: 105 additions & 0 deletions actions/post_utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {PostTypes} from 'mattermost-redux/action_types';
import {
markChannelAsRead,
markChannelAsUnread,
markChannelAsViewed,
} from 'mattermost-redux/actions/channels';
import * as PostActions from 'mattermost-redux/actions/posts';
import * as PostSelectors from 'mattermost-redux/selectors/entities/posts';
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {
isFromWebhook,
isSystemMessage,
shouldIgnorePost,
} from 'mattermost-redux/utils/post_utils';

import {sendDesktopNotification} from 'actions/notification_actions.jsx';

import {ActionTypes} from 'utils/constants';

export function completePostReceive(post, websocketMessageProps) {
return async (dispatch, getState) => {
const state = getState();

const rootPost = PostSelectors.getPost(state, post.root_id);
if (post.root_id && !rootPost) {
const {data: posts} = await dispatch(PostActions.getPostThread(post.root_id));
if (posts) {
dispatch(lastPostActions(post, websocketMessageProps));
}

return;
}

dispatch(lastPostActions(post, websocketMessageProps));
};
}

export function lastPostActions(post, websocketMessageProps) {
return (dispatch, getState) => {
const currentChannelId = getCurrentChannelId(getState());

if (post.channel_id === currentChannelId) {
dispatch({
type: ActionTypes.INCREASE_POST_VISIBILITY,
data: post.channel_id,
amount: 1,
});
}

// Need manual dispatch to remove pending post
dispatch({
type: PostTypes.RECEIVED_POSTS,
data: {
order: [],
posts: {
[post.id]: post,
},
},
channelId: post.channel_id,
});

// Still needed to update unreads

dispatch(setChannelReadAndView(post, websocketMessageProps));

dispatch(sendDesktopNotification(post, websocketMessageProps));
};
}

export function setChannelReadAndView(post, websocketMessageProps) {
return (dispatch, getState) => {
const state = getState();
if (shouldIgnorePost(post)) {
return;
}

let markAsRead = false;
let markAsReadOnServer = false;
if (
post.user_id === getCurrentUserId(state) &&
!isSystemMessage(post) &&
!isFromWebhook(post)
) {
markAsRead = true;
markAsReadOnServer = false;
} else if (
post.channel_id === getCurrentChannelId(state) &&
window.isActive
) {
markAsRead = true;
markAsReadOnServer = true;
}

if (markAsRead) {
dispatch(markChannelAsRead(post.channel_id, null, markAsReadOnServer));
dispatch(markChannelAsViewed(post.channel_id));
} else {
dispatch(markChannelAsUnread(websocketMessageProps.team_id, post.channel_id, websocketMessageProps.mentions));
}
};
}
2 changes: 1 addition & 1 deletion actions/websocket_actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ function handleChannelMemberUpdatedEvent(msg) {

function handleNewPostEvent(msg) {
const post = JSON.parse(msg.data.post);
handleNewPost(post, msg);
dispatch(handleNewPost(post, msg));

getProfilesAndStatusesForPosts([post], dispatch, getState);

Expand Down
27 changes: 0 additions & 27 deletions stores/channel_store.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import EventEmitter from 'events';

import {batchActions} from 'redux-batched-actions';
import {ChannelTypes} from 'mattermost-redux/action_types';
import {markChannelAsRead, markChannelAsUnread, markChannelAsViewed} from 'mattermost-redux/actions/channels';
import * as Selectors from 'mattermost-redux/selectors/entities/channels';
import {getMyChannelMemberships} from 'mattermost-redux/selectors/entities/common';
import {isFromWebhook, isSystemMessage, shouldIgnorePost} from 'mattermost-redux/utils/post_utils';
import {getLicense} from 'mattermost-redux/selectors/entities/general';

import UserStore from 'stores/user_store.jsx';
Expand Down Expand Up @@ -557,31 +555,6 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
});
break;

case ActionTypes.RECEIVED_POST: {
const {post, websocketMessageProps: data} = action;
const {dispatch} = store;
if (shouldIgnorePost(post)) {
return;
}

let markAsRead = false;
let markAsReadOnServer = false;
if (post.user_id === UserStore.getCurrentId() && !isSystemMessage(post) && !isFromWebhook(post)) {
markAsRead = true;
markAsReadOnServer = false;
} else if (action.post.channel_id === ChannelStore.getCurrentId() && window.isActive) {
markAsRead = true;
markAsReadOnServer = true;
}

if (markAsRead) {
dispatch(markChannelAsRead(post.channel_id, null, markAsReadOnServer));
dispatch(markChannelAsViewed(post.channel_id));
} else {
dispatch(markChannelAsUnread(data.team_id, post.channel_id, data.mentions));
}
break;
}
case ActionTypes.CREATE_POST:
ChannelStore.incrementMessages(action.post.channel_id, true);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@ import {Constants, ActionTypes} from 'utils/constants';

const mockStore = configureStore([thunk]);

const RECEIVED_POSTS = {
channelId: 'current_channel_id',
data: {order: [], posts: {new_post_id: {channel_id: 'current_channel_id', id: 'new_post_id', message: 'new message', type: ''}}},
type: 'RECEIVED_POSTS',
};
const INCREASED_POST_VISIBILITY = {amount: 1, data: 'current_channel_id', type: 'INCREASE_POST_VISIBILITY'};

function getReceivedPosts(post) {
const receivedPosts = {...RECEIVED_POSTS};
if (post) {
receivedPosts.data.posts[post.id] = post;
}

return receivedPosts;
}

describe('Actions.Posts', () => {
const latestPost = {
id: 'latest_post_id',
Expand All @@ -35,6 +51,23 @@ describe('Actions.Posts', () => {
messages: ['test message'],
},
},
channels: {
currentChannelId: 'current_channel_id',
myMembers: {[latestPost.channel_id]: {channel_id: 'current_channel_id', user_id: 'current_user_id'}},
},
teams: {
currentTeamId: 'team-1',
teams: {
team_id: {
id: 'team_id',
name: 'team-1',
displayName: 'Team 1',
},
},
},
users: {
currentUserId: 'current_user_id',
},
general: {license: {IsLicensed: 'false'}},
},
views: {
Expand All @@ -44,6 +77,15 @@ describe('Actions.Posts', () => {
},
};

test('handleNewPost', async () => {
const testStore = await mockStore(initialState);
const newPost = {id: 'new_post_id', channel_id: 'current_channel_id', message: 'new message', type: Constants.PostTypes.ADD_TO_CHANNEL};
const msg = {data: {team_id: 'team_id', mentions: ['current_user_id']}};

await testStore.dispatch(Actions.handleNewPost(newPost, msg));
expect(testStore.getActions()).toEqual([INCREASED_POST_VISIBILITY, getReceivedPosts(newPost)]);
});

test('setEditingPost', async () => {
// should allow to edit and should fire an action
let testStore = mockStore({...initialState});
Expand Down
Loading

0 comments on commit a53fdfe

Please sign in to comment.