* Update channel header/purpose when current channel * Add unit tests for update header and purpose
391 lines
13 KiB
JavaScript
391 lines
13 KiB
JavaScript
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
|
// See License.txt for license information.
|
|
|
|
import Client from 'service/client';
|
|
import websocketClient from 'service/client/websocket_client';
|
|
import {batchActions} from 'redux-batched-actions';
|
|
import {
|
|
Constants,
|
|
ChannelTypes,
|
|
GeneralTypes,
|
|
PostsTypes,
|
|
PreferencesTypes,
|
|
TeamsTypes,
|
|
UsersTypes,
|
|
WebsocketEvents
|
|
} from 'service/constants';
|
|
|
|
import {
|
|
fetchMyChannelsAndMembers,
|
|
getChannel,
|
|
getChannelStats,
|
|
viewChannel,
|
|
updateChannelHeader,
|
|
updateChannelPurpose
|
|
} from 'service/actions/channels';
|
|
|
|
import {
|
|
getPosts,
|
|
getPostsSince
|
|
} from 'service/actions/posts';
|
|
|
|
import {getProfilesByIds, getStatusesByIds} from 'service/actions/users';
|
|
|
|
export function init(siteUrl, token, optionalWebSocket) {
|
|
return async (dispatch, getState) => {
|
|
dispatch({type: GeneralTypes.WEBSOCKET_REQUEST}, getState);
|
|
const config = getState().entities.general.config;
|
|
let connUrl = siteUrl || Client.getUrl();
|
|
const authToken = token || Client.getToken();
|
|
|
|
// replace the protocol with a websocket one
|
|
if (connUrl.startsWith('https:')) {
|
|
connUrl = connUrl.replace(/^https:/, 'wss:');
|
|
} else {
|
|
connUrl = connUrl.replace(/^http:/, 'ws:');
|
|
}
|
|
|
|
// append a port number if one isn't already specified
|
|
if (!(/:\d+$/).test(connUrl)) {
|
|
if (connUrl.startsWith('wss:')) {
|
|
connUrl += ':' + (config.WebsocketSecurePort || 443);
|
|
} else {
|
|
connUrl += ':' + (config.WebsocketPort || 80);
|
|
}
|
|
}
|
|
|
|
connUrl += `${Client.getUrlVersion()}/users/websocket`;
|
|
websocketClient.setFirstConnectCallback(handleFirstConnect);
|
|
websocketClient.setEventCallback(handleEvent);
|
|
websocketClient.setReconnectCallback(handleReconnect);
|
|
websocketClient.setCloseCallback(handleClose);
|
|
return websocketClient.initialize(connUrl, authToken, dispatch, getState, optionalWebSocket);
|
|
};
|
|
}
|
|
|
|
export function close() {
|
|
return async (dispatch, getState) => {
|
|
websocketClient.close();
|
|
if (dispatch) {
|
|
dispatch({type: GeneralTypes.WEBSOCKET_FAILURE, error: 'Closed'}, getState);
|
|
}
|
|
};
|
|
}
|
|
|
|
function handleFirstConnect(dispatch, getState) {
|
|
dispatch({type: GeneralTypes.WEBSOCKET_SUCCESS}, getState);
|
|
}
|
|
|
|
function handleReconnect(dispatch, getState) {
|
|
const entities = getState().entities;
|
|
const currentTeamId = entities.teams.currentId;
|
|
const currentChannelId = entities.channels.currentId;
|
|
|
|
if (currentTeamId) {
|
|
fetchMyChannelsAndMembers(currentTeamId)(dispatch, getState);
|
|
|
|
if (currentChannelId) {
|
|
loadPostsHelper(currentTeamId, currentChannelId, dispatch, getState);
|
|
}
|
|
}
|
|
|
|
dispatch({type: GeneralTypes.WEBSOCKET_SUCCESS}, getState);
|
|
}
|
|
|
|
function handleClose(connectFailCount, dispatch, getState) {
|
|
dispatch({type: GeneralTypes.WEBSOCKET_FAILURE, error: connectFailCount}, getState);
|
|
}
|
|
|
|
function handleEvent(msg, dispatch, getState) {
|
|
switch (msg.event) {
|
|
case WebsocketEvents.POSTED:
|
|
case WebsocketEvents.EPHEMERAL_MESSAGE:
|
|
handleNewPostEvent(msg, dispatch, getState);
|
|
break;
|
|
case WebsocketEvents.POST_EDITED:
|
|
handlePostEdited(msg, dispatch, getState);
|
|
break;
|
|
case WebsocketEvents.POST_DELETED:
|
|
handlePostDeleted(msg, dispatch, getState);
|
|
break;
|
|
case WebsocketEvents.LEAVE_TEAM:
|
|
handleLeaveTeamEvent(msg, dispatch, getState);
|
|
break;
|
|
case WebsocketEvents.USER_ADDED:
|
|
handleUserAddedEvent(msg, dispatch, getState);
|
|
break;
|
|
case WebsocketEvents.USER_REMOVED:
|
|
handleUserRemovedEvent(msg, dispatch, getState);
|
|
break;
|
|
case WebsocketEvents.USER_UPDATED:
|
|
handleUserUpdatedEvent(msg, dispatch, getState);
|
|
break;
|
|
case WebsocketEvents.CHANNEL_VIEWED:
|
|
handleChannelViewedEvent(msg, dispatch, getState);
|
|
break;
|
|
case WebsocketEvents.CHANNEL_DELETED:
|
|
handleChannelDeletedEvent(msg, dispatch, getState);
|
|
break;
|
|
case WebsocketEvents.DIRECT_ADDED:
|
|
handleDirectAddedEvent(msg, dispatch, getState);
|
|
break;
|
|
case WebsocketEvents.PREFERENCE_CHANGED:
|
|
handlePreferenceChangedEvent(msg, dispatch, getState);
|
|
break;
|
|
case WebsocketEvents.STATUS_CHANGED:
|
|
handleStatusChangedEvent(msg, dispatch, getState);
|
|
break;
|
|
}
|
|
}
|
|
|
|
function handleNewPostEvent(msg, dispatch, getState) {
|
|
const state = getState();
|
|
const users = state.entities.users;
|
|
const channels = state.entities.channels;
|
|
const teams = state.entities.teams;
|
|
const {posts} = state.entities.posts;
|
|
const isActive = state.entities.general.appState;
|
|
const post = JSON.parse(msg.data.post);
|
|
const userId = post.user_id;
|
|
const teamId = msg.data.team_id;
|
|
const status = users.statuses[userId];
|
|
|
|
if (!users.profiles[userId]) {
|
|
getProfilesByIds([userId])(dispatch, getState);
|
|
}
|
|
|
|
if (status !== Constants.ONLINE) {
|
|
getStatusesByIds([userId])(dispatch, getState);
|
|
}
|
|
|
|
if (post.channel_id === channels.currentId) {
|
|
if (isActive) {
|
|
viewChannel(teamId, post.channel_id)(dispatch, getState);
|
|
switch (post.type) {
|
|
case Constants.POST_HEADER_CHANGE:
|
|
updateChannelHeader(post.channel_id, post.props.new_header)(dispatch, getState);
|
|
break;
|
|
case Constants.POST_PURPOSE_CHANGE:
|
|
updateChannelPurpose(post.channel_id, post.props.new_purpose)(dispatch, getState);
|
|
break;
|
|
}
|
|
} else {
|
|
getChannel(teamId, post.channel_id)(dispatch, getState);
|
|
}
|
|
} else if (teamId === teams.currentId || msg.data.channel_type === Constants.DM_CHANNEL) {
|
|
getChannel(teamId, post.channel_id)(dispatch, getState);
|
|
}
|
|
|
|
if (post.root_id && !posts[post.root_id]) {
|
|
Client.getPost(teamId, post.channel_id, post.root_id).then((data) => {
|
|
const rootUserId = data.posts[post.root_id].user_id;
|
|
const rootStatus = users.statuses[rootUserId];
|
|
if (!users.profiles[rootUserId]) {
|
|
getProfilesByIds([rootUserId])(dispatch, getState);
|
|
}
|
|
|
|
if (rootStatus !== Constants.ONLINE) {
|
|
getStatusesByIds([rootUserId])(dispatch, getState);
|
|
}
|
|
|
|
dispatch({
|
|
type: PostsTypes.RECEIVED_POSTS,
|
|
data,
|
|
channelId: post.channel_id
|
|
}, getState);
|
|
});
|
|
}
|
|
|
|
dispatch({
|
|
type: PostsTypes.RECEIVED_POSTS,
|
|
data: {
|
|
order: [],
|
|
posts: {
|
|
[post.id]: post
|
|
}
|
|
},
|
|
channelId: post.channel_id
|
|
}, getState);
|
|
}
|
|
|
|
function handlePostEdited(msg, dispatch, getState) {
|
|
const state = getState();
|
|
const channels = state.entities.channels;
|
|
const isActive = state.entities.general.appState;
|
|
const data = JSON.parse(msg.data.post);
|
|
|
|
dispatch({type: PostsTypes.RECEIVED_POST, data}, getState);
|
|
|
|
if (msg.broadcast.channel_id === channels.currentId && isActive) {
|
|
// FIXME: Update post should include team_id in the message cause its not always the current team
|
|
// viewChannel(msg.data.team_id, data.channel_id)(dispatch, getState);
|
|
}
|
|
}
|
|
|
|
function handlePostDeleted(msg, dispatch, getState) {
|
|
const data = JSON.parse(msg.data.post);
|
|
dispatch({type: PostsTypes.POST_DELETED, data}, getState);
|
|
}
|
|
|
|
function handleLeaveTeamEvent(msg, dispatch, getState) {
|
|
const entities = getState().entities;
|
|
const teams = entities.teams;
|
|
const users = entities.users;
|
|
|
|
if (users.currentId === msg.data.user_id) {
|
|
dispatch({type: TeamsTypes.LEAVE_TEAM, data: teams.teams[msg.data.team_id]}, getState);
|
|
|
|
// if they are on the team being removed deselect the current team and channel
|
|
if (teams.currentId === msg.data.team_id) {
|
|
dispatch(batchActions([
|
|
{
|
|
type: TeamsTypes.SELECT_TEAM,
|
|
data: ''
|
|
},
|
|
{
|
|
type: ChannelTypes.SELECT_CHANNEL,
|
|
data: ''
|
|
}
|
|
]), getState);
|
|
}
|
|
}
|
|
}
|
|
|
|
function handleUserAddedEvent(msg, dispatch, getState) {
|
|
const state = getState();
|
|
const channels = state.entities.channels;
|
|
const teams = state.entities.teams;
|
|
const users = state.entities.users;
|
|
const teamId = msg.data.team_id;
|
|
|
|
if (msg.broadcast.channel_id === channels.currentId) {
|
|
getChannelStats(teamId, channels.currentId)(dispatch, getState);
|
|
}
|
|
|
|
if (teamId === teams.currentId && msg.data.user_id === users.currentId) {
|
|
getChannel(teamId, msg.broadcast.channel_id)(dispatch, getState);
|
|
}
|
|
}
|
|
|
|
function handleUserRemovedEvent(msg, dispatch, getState) {
|
|
const state = getState();
|
|
const channels = state.entities.channels;
|
|
const teams = state.entities.teams;
|
|
const users = state.entities.users;
|
|
const teamId = teams.currentId;
|
|
|
|
if (msg.broadcast.user_id === users.currentId && teamId) {
|
|
fetchMyChannelsAndMembers(teamId)(dispatch, getState);
|
|
dispatch({
|
|
type: ChannelTypes.LEAVE_CHANNEL,
|
|
data: msg.data.channel_id
|
|
}, getState);
|
|
} else if (msg.broadcast.channel_id === channels.currentId) {
|
|
getChannelStats(teamId, channels.currentId)(dispatch, getState);
|
|
}
|
|
}
|
|
|
|
function handleUserUpdatedEvent(msg, dispatch, getState) {
|
|
const entities = getState().entities;
|
|
const users = entities.users;
|
|
const user = msg.data.user;
|
|
|
|
if (user.id !== users.currentId) {
|
|
dispatch({
|
|
type: UsersTypes.RECEIVED_PROFILES,
|
|
data: {
|
|
[user.id]: user
|
|
}
|
|
}, getState);
|
|
}
|
|
}
|
|
|
|
function handleChannelViewedEvent(msg, dispatch, getState) {
|
|
const state = getState();
|
|
const channels = state.entities.channels;
|
|
const teams = state.entities.teams;
|
|
const users = state.entities.users;
|
|
|
|
if (teams.currentId === msg.broadcast.team_id && channels.currentId !== msg.data.channel_id &&
|
|
users.currentId === msg.broadcast.user_id) {
|
|
getChannel(teams.currentId, msg.data.channel_id)(dispatch, getState);
|
|
}
|
|
}
|
|
|
|
function handleChannelDeletedEvent(msg, dispatch, getState) {
|
|
const entities = getState().entities;
|
|
const {channels, currentId} = entities.channels;
|
|
const teams = entities.teams;
|
|
|
|
if (msg.broadcast.team_id === teams.currentId) {
|
|
dispatch({type: ChannelTypes.RECEIVED_CHANNEL_DELETED, data: msg.data.channel_id}, getState);
|
|
|
|
if (msg.data.channel_id === currentId) {
|
|
let channelId = '';
|
|
const channel = Object.keys(channels).filter((key) => channels[key].name === Constants.DEFAULT_CHANNEL);
|
|
|
|
if (channel.length) {
|
|
channelId = channel[0];
|
|
}
|
|
|
|
dispatch({type: ChannelTypes.SELECT_CHANNEL, data: channelId}, getState);
|
|
}
|
|
|
|
fetchMyChannelsAndMembers(teams.currentId)(dispatch, getState);
|
|
}
|
|
}
|
|
|
|
function handleDirectAddedEvent(msg, dispatch, getState) {
|
|
const state = getState();
|
|
const teams = state.entities.teams;
|
|
|
|
getChannel(teams.currentId, msg.broadcast.channel_id)(dispatch, getState);
|
|
}
|
|
|
|
function handlePreferenceChangedEvent(msg, dispatch, getState) {
|
|
const preference = JSON.parse(msg.data.preference);
|
|
dispatch({type: PreferencesTypes.RECEIVED_PREFERENCES, data: [preference]}, getState);
|
|
|
|
if (preference.category === Constants.CATEGORY_DIRECT_CHANNEL_SHOW) {
|
|
const state = getState();
|
|
const users = state.entities.users;
|
|
const userId = preference.name;
|
|
const status = users.statuses[userId];
|
|
|
|
if (!users.profiles[userId]) {
|
|
getProfilesByIds([userId])(dispatch, getState);
|
|
}
|
|
|
|
if (status !== Constants.ONLINE) {
|
|
getStatusesByIds([userId])(dispatch, getState);
|
|
}
|
|
}
|
|
}
|
|
|
|
function handleStatusChangedEvent(msg, dispatch, getState) {
|
|
dispatch({
|
|
type: UsersTypes.RECEIVED_STATUSES,
|
|
data: {
|
|
[msg.data.user_id]: msg.data.status
|
|
}
|
|
}, getState);
|
|
}
|
|
|
|
// Helpers
|
|
|
|
function loadPostsHelper(teamId, channelId, dispatch, getState) {
|
|
const {posts, postsByChannel} = getState().entities.posts;
|
|
const postsArray = postsByChannel[channelId];
|
|
const latestPostId = postsArray[postsArray.length - 1];
|
|
|
|
let latestPostTime = 0;
|
|
if (latestPostId) {
|
|
latestPostTime = posts[latestPostId].create_at || 0;
|
|
}
|
|
|
|
if (Object.keys(posts).length === 0 || postsArray.length < Constants.POST_CHUNK_SIZE || latestPostTime === 0) {
|
|
getPosts(teamId, channelId)(dispatch, getState);
|
|
} else {
|
|
getPostsSince(teamId, channelId, latestPostTime)(dispatch, getState);
|
|
}
|
|
}
|