Add WebSocket support (#144)
* Add WebSocket support * fix unit tests * Add actions to websocket and fix unit tests
This commit is contained in:
parent
e8d774b332
commit
7e426b6029
23 changed files with 1186 additions and 179 deletions
|
|
@ -27,7 +27,8 @@
|
|||
"expect": true,
|
||||
"before": true,
|
||||
"beforeEach": true,
|
||||
"after": true
|
||||
"after": true,
|
||||
"afterEach": true
|
||||
},
|
||||
"rules": {
|
||||
"array-bracket-spacing": [2, "never"],
|
||||
|
|
|
|||
|
|
@ -54,14 +54,15 @@ export function setStoreFromLocalData(data) {
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO: uncomment and sorround with try/catch when PLT-4167 is merged
|
||||
// const teamMembers = Client.getMyTeamMembers();
|
||||
const teamMembers = Object.keys(teams).map((key) => {
|
||||
return {
|
||||
team_id: key,
|
||||
user_id: user.id
|
||||
};
|
||||
});
|
||||
let teamMembers;
|
||||
dispatch({type: TeamsTypes.MY_TEAM_MEMBERS_REQUEST}, getState);
|
||||
try {
|
||||
teamMembers = await Client.getMyTeamMembers();
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(error, dispatch);
|
||||
dispatch({type: TeamsTypes.MY_TEAM_MEMBERS_FAILURE, error}, getState);
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(batchActions([
|
||||
{
|
||||
|
|
|
|||
|
|
@ -41,25 +41,23 @@ export default class SelectTeam extends Component {
|
|||
for (const id of Object.keys(this.props.teams)) {
|
||||
const team = this.props.teams[id];
|
||||
|
||||
// TODO: uncomment when PLT-4167 is merged
|
||||
// if (this.props.myMembers.hasOwnProperty(id)) {
|
||||
teams.push(
|
||||
<Button
|
||||
key={id}
|
||||
onPress={() => this.onSelectTeam(team)}
|
||||
style={GlobalStyles.buttonListItemText}
|
||||
containerStyle={GlobalStyles.buttonListItem}
|
||||
>
|
||||
{team.display_name}
|
||||
<Icon
|
||||
name='keyboard-arrow-right'
|
||||
size={24}
|
||||
color='#777'
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
|
||||
// }
|
||||
if (this.props.myMembers.hasOwnProperty(id)) {
|
||||
teams.push(
|
||||
<Button
|
||||
key={id}
|
||||
onPress={() => this.onSelectTeam(team)}
|
||||
style={GlobalStyles.buttonListItemText}
|
||||
containerStyle={GlobalStyles.buttonListItem}
|
||||
>
|
||||
{team.display_name}
|
||||
<Icon
|
||||
name='keyboard-arrow-right'
|
||||
size={24}
|
||||
color='#777'
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -34,14 +34,15 @@
|
|||
"react-test-renderer": "15.3.2",
|
||||
"redux-logger": "2.7.0",
|
||||
"remote-redux-devtools": "0.5.0",
|
||||
"remote-redux-devtools-on-debugger": "0.6.2"
|
||||
"remote-redux-devtools-on-debugger": "0.6.2",
|
||||
"ws": "1.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"check": "node_modules/.bin/eslint --ext \".js\" --ignore-pattern node_modules --quiet .",
|
||||
"run-ios": "node node_modules/react-native/local-cli/cli.js run-ios",
|
||||
"run-android": "node node_modules/react-native/local-cli/cli.js run-android",
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||
"test": "mocha --compilers js:babel-register --require babel-polyfill",
|
||||
"test": "NODE_ENV=test mocha --compilers js:babel-register --require babel-polyfill",
|
||||
"postinstall": "make post-install"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,15 @@ import {forceLogoutIfNecessary} from './helpers';
|
|||
import {batchActions} from 'redux-batched-actions';
|
||||
import Client from 'service/client';
|
||||
|
||||
export function selectChannel(channelId) {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: ChannelTypes.SELECTED_CHANNEL,
|
||||
data: channelId
|
||||
}, getState);
|
||||
};
|
||||
}
|
||||
|
||||
export function createChannel(channel, userId) {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch(batchActions([
|
||||
|
|
@ -171,8 +180,10 @@ export function updateChannelNotifyProps(userId, teamId, channelId, props) {
|
|||
dispatch(batchActions([
|
||||
{
|
||||
type: ChannelTypes.RECEIVED_CHANNEL_PROPS,
|
||||
data: notifyProps,
|
||||
channel_id: channelId
|
||||
data: {
|
||||
channel_id: channelId,
|
||||
notifyProps
|
||||
}
|
||||
},
|
||||
{
|
||||
type: ChannelTypes.NOTIFY_PROPS_SUCCESS
|
||||
|
|
@ -247,14 +258,14 @@ export function fetchMyChannelsAndMembers(teamId) {
|
|||
dispatch(batchActions([
|
||||
{
|
||||
type: ChannelTypes.RECEIVED_CHANNELS,
|
||||
data: await channels
|
||||
data: channels
|
||||
},
|
||||
{
|
||||
type: ChannelTypes.CHANNELS_SUCCESS
|
||||
},
|
||||
{
|
||||
type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBERS,
|
||||
data: await channelMembers
|
||||
data: channelMembers
|
||||
},
|
||||
{
|
||||
type: ChannelTypes.CHANNEL_MEMBERS_SUCCESS
|
||||
|
|
@ -278,7 +289,7 @@ export function leaveChannel(teamId, channelId) {
|
|||
dispatch(batchActions([
|
||||
{
|
||||
type: ChannelTypes.LEAVE_CHANNEL,
|
||||
channel_id: channelId
|
||||
data: channelId
|
||||
},
|
||||
{
|
||||
type: ChannelTypes.LEAVE_CHANNEL_SUCCESS
|
||||
|
|
@ -346,7 +357,7 @@ export function deleteChannel(teamId, channelId) {
|
|||
dispatch(batchActions([
|
||||
{
|
||||
type: ChannelTypes.RECEIVED_CHANNEL_DELETED,
|
||||
channel_id: channelId
|
||||
data: channelId
|
||||
},
|
||||
{
|
||||
type: ChannelTypes.DELETE_CHANNEL_SUCCESS
|
||||
|
|
@ -371,8 +382,10 @@ export function updateLastViewedAt(teamId, channelId, active) {
|
|||
dispatch(batchActions([
|
||||
{
|
||||
type: ChannelTypes.RECEIVED_LAST_VIEWED,
|
||||
channel_id: channelId,
|
||||
last_viewed_at: new Date().getTime()
|
||||
data: {
|
||||
channel_id: channelId,
|
||||
last_viewed_at: new Date().getTime()
|
||||
}
|
||||
},
|
||||
{
|
||||
type: ChannelTypes.UPDATE_LAST_VIEWED_SUCCESS
|
||||
|
|
@ -461,7 +474,7 @@ export function removeChannelMember(teamId, channelId, userId) {
|
|||
dispatch({type: ChannelTypes.REMOVE_CHANNEL_MEMBER_REQUEST}, getState);
|
||||
|
||||
try {
|
||||
await Client.addChannelMember(teamId, channelId, userId);
|
||||
await Client.removeChannelMember(teamId, channelId, userId);
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(error, dispatch);
|
||||
dispatch({type: ChannelTypes.REMOVE_CHANNEL_MEMBER_FAILURE, error}, getState);
|
||||
|
|
@ -482,6 +495,7 @@ export function removeChannelMember(teamId, channelId, userId) {
|
|||
}
|
||||
|
||||
export default {
|
||||
selectChannel,
|
||||
createChannel,
|
||||
createDirectChannel,
|
||||
updateChannel,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,35 @@ import Client from 'service/client';
|
|||
import {batchActions} from 'redux-batched-actions';
|
||||
import {bindClientFunc, forceLogoutIfNecessary} from './helpers';
|
||||
import {Constants, PostsTypes} from 'service/constants';
|
||||
import {getProfilesByIds, getStatusesByIds} from './users';
|
||||
|
||||
async function getProfilesAndStatusesForPosts(list, dispatch, getState) {
|
||||
const {profiles, statuses} = getState().entities.users;
|
||||
const posts = list.posts;
|
||||
const profilesToLoad = [];
|
||||
const statusesToLoad = [];
|
||||
|
||||
Object.keys(posts).forEach((key) => {
|
||||
const post = posts[key];
|
||||
const userId = post.user_id;
|
||||
|
||||
if (!profiles[userId]) {
|
||||
profilesToLoad.push(userId);
|
||||
}
|
||||
|
||||
if (!statuses[userId]) {
|
||||
statusesToLoad.push(userId);
|
||||
}
|
||||
});
|
||||
|
||||
if (profilesToLoad.length) {
|
||||
await getProfilesByIds(profilesToLoad)(dispatch, getState);
|
||||
}
|
||||
|
||||
if (statusesToLoad.length) {
|
||||
await getStatusesByIds(statusesToLoad)(dispatch, getState);
|
||||
}
|
||||
}
|
||||
|
||||
export function createPost(teamId, post) {
|
||||
return bindClientFunc(
|
||||
|
|
@ -68,6 +97,7 @@ export function getPost(teamId, channelId, postId) {
|
|||
let post;
|
||||
try {
|
||||
post = await Client.getPost(teamId, channelId, postId);
|
||||
getProfilesAndStatusesForPosts(post, dispatch, getState);
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(error, dispatch);
|
||||
dispatch({type: PostsTypes.GET_POST_FAILURE, error}, getState);
|
||||
|
|
@ -94,6 +124,7 @@ export function getPosts(teamId, channelId, offset = 0, limit = Constants.POST_C
|
|||
|
||||
try {
|
||||
posts = await Client.getPosts(teamId, channelId, offset, limit);
|
||||
getProfilesAndStatusesForPosts(posts, dispatch, getState);
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(error, dispatch);
|
||||
dispatch({type: PostsTypes.GET_POSTS_FAILURE, error}, getState);
|
||||
|
|
@ -120,6 +151,7 @@ export function getPostsSince(teamId, channelId, since) {
|
|||
let posts;
|
||||
try {
|
||||
posts = await Client.getPostsSince(teamId, channelId, since);
|
||||
getProfilesAndStatusesForPosts(posts, dispatch, getState);
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(error, dispatch);
|
||||
dispatch({type: PostsTypes.GET_POSTS_SINCE_FAILURE, error}, getState);
|
||||
|
|
@ -146,6 +178,7 @@ export function getPostsBefore(teamId, channelId, postId, offset = 0, limit = Co
|
|||
let posts;
|
||||
try {
|
||||
posts = await Client.getPostsBefore(teamId, channelId, postId, offset, limit);
|
||||
getProfilesAndStatusesForPosts(posts, dispatch, getState);
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(error, dispatch);
|
||||
dispatch({type: PostsTypes.GET_POSTS_BEFORE_FAILURE, error}, getState);
|
||||
|
|
@ -172,6 +205,7 @@ export function getPostsAfter(teamId, channelId, postId, offset = 0, limit = Con
|
|||
let posts;
|
||||
try {
|
||||
posts = await Client.getPostsAfter(teamId, channelId, postId, offset, limit);
|
||||
getProfilesAndStatusesForPosts(posts, dispatch, getState);
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(error, dispatch);
|
||||
dispatch({type: PostsTypes.GET_POSTS_AFTER_FAILURE, error}, getState);
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@
|
|||
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
import Client from 'service/client';
|
||||
|
||||
// TODO: uncomment when PLT-4167 is merged
|
||||
// import {Constants, PreferencesTypes, UsersTypes, TeamsTypes} from 'constants';
|
||||
import {Constants, PreferencesTypes, UsersTypes} from 'service/constants';
|
||||
import {Constants, PreferencesTypes, UsersTypes, TeamsTypes} from 'service/constants';
|
||||
import {bindClientFunc, forceLogoutIfNecessary} from './helpers';
|
||||
|
||||
export function login(loginId, password, mfaToken = '') {
|
||||
|
|
@ -15,16 +12,13 @@ export function login(loginId, password, mfaToken = '') {
|
|||
|
||||
Client.login(loginId, password, mfaToken).
|
||||
then(async (data) => {
|
||||
// TODO: uncomment when PLT-4167 is merged
|
||||
// let teamMembers;
|
||||
let teamMembers;
|
||||
let preferences;
|
||||
try {
|
||||
// TODO: uncomment when PLT-4167 is merged
|
||||
// const teamMembersRequest = Client.getMyTeamMembers();
|
||||
const teamMembersRequest = Client.getMyTeamMembers();
|
||||
const preferencesRequest = Client.getMyPreferences();
|
||||
|
||||
// TODO: uncomment when PLT-4167 is merged
|
||||
// teamMembers = await teamMembersRequest;
|
||||
teamMembers = await teamMembersRequest;
|
||||
preferences = await preferencesRequest;
|
||||
} catch (err) {
|
||||
dispatch({type: UsersTypes.LOGIN_FAILURE, error: err}, getState);
|
||||
|
|
@ -40,12 +34,10 @@ export function login(loginId, password, mfaToken = '') {
|
|||
type: PreferencesTypes.RECEIVED_PREFERENCES,
|
||||
data: await preferences
|
||||
},
|
||||
|
||||
// TODO: uncomment when PLT-4167 is merged
|
||||
// {
|
||||
// type: TeamsTypes.RECEIVED_MY_TEAM_MEMBERS,
|
||||
// data: await teamMembers
|
||||
// },
|
||||
{
|
||||
type: TeamsTypes.RECEIVED_MY_TEAM_MEMBERS,
|
||||
data: await teamMembers
|
||||
},
|
||||
{
|
||||
type: UsersTypes.LOGIN_SUCCESS
|
||||
}
|
||||
|
|
|
|||
377
service/actions/websocket.js
Normal file
377
service/actions/websocket.js
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
// 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,
|
||||
updateLastViewedAt
|
||||
} from 'service/actions/channels';
|
||||
|
||||
import {
|
||||
getPosts,
|
||||
getPostsSince
|
||||
} from 'service/actions/posts';
|
||||
|
||||
import {getProfilesByIds, getStatusesByIds} from 'service/actions/users';
|
||||
|
||||
export function init(siteUrl, token) {
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
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) {
|
||||
updateLastViewedAt(teamId, post.channel_id)(dispatch, getState);
|
||||
} 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
|
||||
// updateLastViewedAt(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.SELECTED_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);
|
||||
} 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.SELECTED_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);
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,10 @@ export default class Client {
|
|||
this.token = token;
|
||||
}
|
||||
|
||||
getUrlVersion() {
|
||||
return this.urlVersion;
|
||||
}
|
||||
|
||||
getBaseRoute() {
|
||||
return `${this.url}${this.urlVersion}`;
|
||||
}
|
||||
|
|
@ -581,9 +585,7 @@ export default class Client {
|
|||
);
|
||||
};
|
||||
|
||||
// Client helpers
|
||||
// Preferences routes
|
||||
|
||||
getMyPreferences = async () => {
|
||||
return this.doFetch(
|
||||
`${this.getPreferencesRoute()}/`,
|
||||
|
|
@ -619,6 +621,7 @@ export default class Client {
|
|||
);
|
||||
};
|
||||
|
||||
// Client helpers
|
||||
doFetch = async (url, options) => {
|
||||
const {data} = await this.doFetchWithResponse(url, options);
|
||||
|
||||
|
|
|
|||
200
service/client/websocket_client.js
Normal file
200
service/client/websocket_client.js
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
const MAX_WEBSOCKET_FAILS = 7;
|
||||
const MIN_WEBSOCKET_RETRY_TIME = 3000; // 3 sec
|
||||
const MAX_WEBSOCKET_RETRY_TIME = 300000; // 5 mins
|
||||
|
||||
/* eslint-disable global-require, no-process-env */
|
||||
|
||||
let Socket;
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
Socket = require('ws');
|
||||
} else {
|
||||
Socket = WebSocket;
|
||||
}
|
||||
|
||||
/* eslint-enable global-require, no-process-env */
|
||||
|
||||
class WebSocketClient {
|
||||
constructor() {
|
||||
this.conn = null;
|
||||
this.connectionUrl = null;
|
||||
this.sequence = 1;
|
||||
this.connectFailCount = 0;
|
||||
this.eventCallback = null;
|
||||
this.firstConnectCallback = null;
|
||||
this.reconnectCallback = null;
|
||||
this.errorCallback = null;
|
||||
this.closeCallback = null;
|
||||
this.dispatch = null;
|
||||
this.getState = null;
|
||||
}
|
||||
|
||||
initialize(connectionUrl = this.connectionUrl, token, dispatch, getState) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.conn) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (connectionUrl == null) {
|
||||
console.log('websocket must have connection url'); //eslint-disable-line no-console
|
||||
reject('websocket must have connection url');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dispatch) {
|
||||
console.log('websocket must have a dispatch'); //eslint-disable-line no-console
|
||||
reject('websocket must have a dispatch');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.connectFailCount === 0) {
|
||||
console.log('websocket connecting to ' + connectionUrl); //eslint-disable-line no-console
|
||||
}
|
||||
|
||||
this.conn = new Socket(connectionUrl);
|
||||
this.connectionUrl = connectionUrl;
|
||||
this.dispatch = dispatch;
|
||||
this.getState = getState;
|
||||
|
||||
this.conn.onopen = () => {
|
||||
if (token) {
|
||||
this.sendMessage('authentication_challenge', {token});
|
||||
}
|
||||
|
||||
if (this.connectFailCount > 0) {
|
||||
console.log('websocket re-established connection'); //eslint-disable-line no-console
|
||||
if (this.reconnectCallback) {
|
||||
this.reconnectCallback(this.dispatch, this.getState);
|
||||
}
|
||||
} else if (this.firstConnectCallback) {
|
||||
this.firstConnectCallback(this.dispatch, this.getState);
|
||||
resolve();
|
||||
}
|
||||
|
||||
this.connectFailCount = 0;
|
||||
};
|
||||
|
||||
this.conn.onclose = () => {
|
||||
this.conn = null;
|
||||
this.sequence = 1;
|
||||
|
||||
if (this.connectFailCount === 0) {
|
||||
console.log('websocket closed'); //eslint-disable-line no-console
|
||||
}
|
||||
|
||||
this.connectFailCount++;
|
||||
|
||||
if (this.closeCallback) {
|
||||
this.closeCallback(this.connectFailCount, this.dispatch, this.getState);
|
||||
}
|
||||
|
||||
let retryTime = MIN_WEBSOCKET_RETRY_TIME;
|
||||
|
||||
// If we've failed a bunch of connections then start backing off
|
||||
if (this.connectFailCount > MAX_WEBSOCKET_FAILS) {
|
||||
retryTime = MIN_WEBSOCKET_RETRY_TIME * this.connectFailCount * this.connectFailCount;
|
||||
if (retryTime > MAX_WEBSOCKET_RETRY_TIME) {
|
||||
retryTime = MAX_WEBSOCKET_RETRY_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(
|
||||
() => {
|
||||
this.initialize(connectionUrl, token);
|
||||
},
|
||||
retryTime
|
||||
);
|
||||
};
|
||||
|
||||
this.conn.onerror = (evt) => {
|
||||
if (this.connectFailCount <= 1) {
|
||||
console.log('websocket error'); //eslint-disable-line no-console
|
||||
console.log(evt); //eslint-disable-line no-console
|
||||
}
|
||||
|
||||
if (this.errorCallback) {
|
||||
this.errorCallback(evt, this.dispatch, this.getState);
|
||||
}
|
||||
};
|
||||
|
||||
this.conn.onmessage = (evt) => {
|
||||
const msg = JSON.parse(evt.data);
|
||||
if (msg.seq_reply) {
|
||||
if (msg.error) {
|
||||
console.log(msg); //eslint-disable-line no-console
|
||||
}
|
||||
} else if (this.eventCallback) {
|
||||
this.eventCallback(msg, this.dispatch, this.getState);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
setEventCallback(callback) {
|
||||
this.eventCallback = callback;
|
||||
}
|
||||
|
||||
setFirstConnectCallback(callback) {
|
||||
this.firstConnectCallback = callback;
|
||||
}
|
||||
|
||||
setReconnectCallback(callback) {
|
||||
this.reconnectCallback = callback;
|
||||
}
|
||||
|
||||
setErrorCallback(callback) {
|
||||
this.errorCallback = callback;
|
||||
}
|
||||
|
||||
setCloseCallback(callback) {
|
||||
this.closeCallback = callback;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.connectFailCount = 0;
|
||||
this.sequence = 1;
|
||||
if (this.conn && this.conn.readyState === Socket.OPEN) {
|
||||
this.conn.onclose = () => {}; //eslint-disable-line no-empty-function
|
||||
this.conn.close();
|
||||
this.conn = null;
|
||||
console.log('websocket closed'); //eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage(action, data) {
|
||||
const msg = {
|
||||
action,
|
||||
seq: this.sequence++,
|
||||
data
|
||||
};
|
||||
|
||||
if (this.conn && this.conn.readyState === Socket.OPEN) {
|
||||
this.conn.send(JSON.stringify(msg));
|
||||
} else if (!this.conn || this.conn.readyState === Socket.CLOSED) {
|
||||
this.conn = null;
|
||||
this.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
userTyping(channelId, parentId) {
|
||||
this.sendMessage('user_typing', {
|
||||
channel_id: channelId,
|
||||
parent_id: parentId
|
||||
});
|
||||
}
|
||||
|
||||
getStatuses() {
|
||||
this.sendMessage('get_statuses', null);
|
||||
}
|
||||
|
||||
getStatusesByIds(userIds) {
|
||||
this.sendMessage('get_statuses_by_ids', {
|
||||
user_ids: userIds
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new WebSocketClient();
|
||||
|
|
@ -6,13 +6,22 @@ const Constants = {
|
|||
PROFILE_CHUNK_SIZE: 100,
|
||||
CHANNELS_CHUNK_SIZE: 50,
|
||||
|
||||
OFFLINE: 'offline',
|
||||
AWAY: 'away',
|
||||
ONLINE: 'online',
|
||||
|
||||
TEAM_USER_ROLE: 'team_user',
|
||||
TEAM_ADMIN_ROLE: 'team_admin',
|
||||
|
||||
CHANNEL_USER_ROLE: 'channel_user',
|
||||
CHANNEL_ADMIN_ROLE: 'channel_admin',
|
||||
|
||||
POST_DELETED: 'DELETED'
|
||||
DEFAULT_CHANNEL: 'town-square',
|
||||
DM_CHANNEL: 'D',
|
||||
|
||||
POST_DELETED: 'DELETED',
|
||||
|
||||
CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show'
|
||||
};
|
||||
|
||||
export default Constants;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,11 @@ const GeneralTypes = keyMirror({
|
|||
|
||||
LOG_CLIENT_ERROR_REQUEST: null,
|
||||
LOG_CLIENT_ERROR_SUCCESS: null,
|
||||
LOG_CLIENT_ERROR_FAILURE: null
|
||||
LOG_CLIENT_ERROR_FAILURE: null,
|
||||
|
||||
WEBSOCKET_REQUEST: null,
|
||||
WEBSOCKET_SUCCESS: null,
|
||||
WEBSOCKET_FAILURE: null
|
||||
});
|
||||
|
||||
export default GeneralTypes;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import PostsTypes from './posts';
|
|||
import PreferencesTypes from './preferences';
|
||||
import RequestStatus from './request_status';
|
||||
import Themes from './themes';
|
||||
import WebsocketEvents from './websocket';
|
||||
|
||||
const Preferences = {
|
||||
CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show',
|
||||
|
|
@ -26,5 +27,6 @@ export {
|
|||
PreferencesTypes,
|
||||
Preferences,
|
||||
RequestStatus,
|
||||
Themes
|
||||
Themes,
|
||||
WebsocketEvents
|
||||
};
|
||||
|
|
|
|||
25
service/constants/websocket.js
Normal file
25
service/constants/websocket.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
const WebsocketEvents = {
|
||||
POSTED: 'posted',
|
||||
POST_EDITED: 'post_edited',
|
||||
POST_DELETED: 'post_deleted',
|
||||
CHANNEL_DELETED: 'channel_deleted',
|
||||
CHANNEL_VIEWED: 'channel_viewed',
|
||||
DIRECT_ADDED: 'direct_added',
|
||||
LEAVE_TEAM: 'leave_team',
|
||||
USER_ADDED: 'user_added',
|
||||
USER_REMOVED: 'user_removed',
|
||||
USER_UPDATED: 'user_updated',
|
||||
TYPING: 'typing',
|
||||
PREFERENCE_CHANGED: 'preference_changed',
|
||||
EPHEMERAL_MESSAGE: 'ephemeral_message',
|
||||
STATUS_CHANGED: 'status_change',
|
||||
HELLO: 'hello',
|
||||
WEBRTC: 'webrtc',
|
||||
REACTION_ADDED: 'reaction_added',
|
||||
REACTION_REMOVED: 'reaction_removed'
|
||||
};
|
||||
|
||||
export default WebsocketEvents;
|
||||
|
|
@ -6,6 +6,8 @@ import {combineReducers} from 'redux';
|
|||
|
||||
function currentId(state = '', action) {
|
||||
switch (action.type) {
|
||||
case ChannelTypes.SELECTED_CHANNEL:
|
||||
return action.data;
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
default:
|
||||
|
|
@ -30,6 +32,9 @@ function channels(state = {}, action) {
|
|||
}
|
||||
return nextState;
|
||||
}
|
||||
case ChannelTypes.RECEIVED_CHANNEL_DELETED:
|
||||
Reflect.deleteProperty(nextState, action.data);
|
||||
return nextState;
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
|
|
@ -56,26 +61,26 @@ function myMembers(state = {}, action) {
|
|||
return nextState;
|
||||
}
|
||||
case ChannelTypes.RECEIVED_CHANNEL_PROPS: {
|
||||
const member = {...state[action.channel_id]};
|
||||
member.notify_props = action.data;
|
||||
const member = {...state[action.data.channel_id]};
|
||||
member.notify_props = action.data.notifyProps;
|
||||
|
||||
return {
|
||||
...state,
|
||||
[action.channel_id]: member
|
||||
[action.data.channel_id]: member
|
||||
};
|
||||
}
|
||||
case ChannelTypes.RECEIVED_LAST_VIEWED: {
|
||||
const member = {...state[action.channel_id]};
|
||||
member.last_viewed_at = action.last_viewed_at;
|
||||
const member = {...state[action.data.channel_id]};
|
||||
member.last_viewed_at = action.data.last_viewed_at;
|
||||
|
||||
return {
|
||||
...state,
|
||||
[action.channel_id]: member
|
||||
[action.data.channel_id]: member
|
||||
};
|
||||
}
|
||||
case ChannelTypes.LEAVE_CHANNEL:
|
||||
case ChannelTypes.RECEIVED_CHANNEL_DELETED:
|
||||
Reflect.deleteProperty(nextState, action.channel_id);
|
||||
Reflect.deleteProperty(nextState, action.data);
|
||||
return nextState;
|
||||
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@
|
|||
|
||||
import {Constants, PostsTypes, UsersTypes} from 'service/constants';
|
||||
|
||||
function initialState() {
|
||||
return {
|
||||
posts: {},
|
||||
postsByChannel: {}
|
||||
};
|
||||
}
|
||||
|
||||
function handleReceivedPost(posts = {}, postsByChannel = {}, action) {
|
||||
const post = action.data;
|
||||
const channelId = post.channel_id;
|
||||
|
|
@ -123,7 +130,7 @@ function handleRemovePost(posts = {}, postsByChannel = {}, action) {
|
|||
return {posts: nextPosts, postsByChannel: nextPostsByChannel};
|
||||
}
|
||||
|
||||
function handlePosts(state = {}, action) {
|
||||
function handlePosts(state = initialState(), action) {
|
||||
switch (action.type) {
|
||||
case PostsTypes.RECEIVED_POST:
|
||||
return handleReceivedPost(state.posts, state.postsByChannel, action);
|
||||
|
|
@ -135,10 +142,7 @@ function handlePosts(state = {}, action) {
|
|||
return handleRemovePost(state.posts, state.postsByChannel, action);
|
||||
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
return {
|
||||
posts: {},
|
||||
postsByChannel: {}
|
||||
};
|
||||
return initialState();
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -162,7 +166,7 @@ function currentFocusedPostId(state = '', action) {
|
|||
}
|
||||
}
|
||||
|
||||
export default function(state = {}, action) {
|
||||
export default function(state = initialState(), action) {
|
||||
const {posts, postsByChannel} = handlePosts(state, action);
|
||||
|
||||
const nextState = {
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ function myMembers(state = {}, action) {
|
|||
|
||||
case TeamsTypes.LEAVE_TEAM: {
|
||||
const nextState = {...state};
|
||||
const data = action.team;
|
||||
Reflect.deleteProperty(nextState, data.team_id);
|
||||
const data = action.data;
|
||||
Reflect.deleteProperty(nextState, data.id);
|
||||
return nextState;
|
||||
}
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
|
|
|
|||
|
|
@ -35,8 +35,19 @@ function license(state = initialRequestState(), action) {
|
|||
);
|
||||
}
|
||||
|
||||
function websocket(state = initialRequestState(), action) {
|
||||
return handleRequest(
|
||||
GeneralTypes.WEBSOCKET_REQUEST,
|
||||
GeneralTypes.WEBSOCKET_SUCCESS,
|
||||
GeneralTypes.WEBSOCKET_FAILURE,
|
||||
state,
|
||||
action
|
||||
);
|
||||
}
|
||||
|
||||
export default combineReducers({
|
||||
server,
|
||||
config,
|
||||
license
|
||||
license,
|
||||
websocket
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
import assert from 'assert';
|
||||
|
||||
import * as Actions from 'service/actions/channels';
|
||||
import {getProfiles} from 'service/actions/users';
|
||||
import Client from 'service/client';
|
||||
import configureStore from 'app/store';
|
||||
import {RequestStatus} from 'service/constants';
|
||||
|
|
@ -66,7 +65,6 @@ describe('Actions.Channels', () => {
|
|||
const state = store.getState();
|
||||
const channels = state.entities.channels.channels;
|
||||
const members = state.entities.channels.myMembers;
|
||||
const profiles = state.entities.users.profiles;
|
||||
const preferences = state.entities.preferences.myPreferences;
|
||||
|
||||
const createRequest = state.requests.channels.createChannel;
|
||||
|
|
@ -80,7 +78,6 @@ describe('Actions.Channels', () => {
|
|||
const membersCount = Object.keys(members).length;
|
||||
assert.ok(channels, 'channels is empty');
|
||||
assert.ok(members, 'members is empty');
|
||||
assert.ok(profiles[user.id], 'profiles does not have userId');
|
||||
assert.ok(Object.keys(preferences).length, 'preferences is empty');
|
||||
assert.ok(channels[Object.keys(members)[0]], 'channels should have the member');
|
||||
assert.ok(members[Object.keys(channels)[0]], 'members should belong to channel');
|
||||
|
|
@ -93,7 +90,6 @@ describe('Actions.Channels', () => {
|
|||
}
|
||||
});
|
||||
|
||||
await getProfiles(0)(store.dispatch, store.getState);
|
||||
Actions.createDirectChannel(TestHelper.basicTeam.id, TestHelper.basicUser.id, user.id)(store.dispatch, store.getState);
|
||||
});
|
||||
}).timeout(3000);
|
||||
|
|
@ -176,7 +172,7 @@ describe('Actions.Channels', () => {
|
|||
|
||||
Actions.fetchMyChannelsAndMembers(TestHelper.basicTeam.id)(store.dispatch, store.getState);
|
||||
});
|
||||
});
|
||||
}).timeout(3000);
|
||||
|
||||
it('updateChannelNotifyProps', (done) => {
|
||||
TestHelper.initBasic(Client).then(async () => {
|
||||
|
|
|
|||
|
|
@ -156,19 +156,23 @@ describe('Actions.Posts', () => {
|
|||
channelId
|
||||
)(store.dispatch, store.getState);
|
||||
|
||||
let shouldStop = false;
|
||||
store.subscribe(() => {
|
||||
const state = store.getState();
|
||||
const {posts, postsByChannel} = state.entities.posts;
|
||||
|
||||
assert.ok(posts);
|
||||
assert.ok(postsByChannel);
|
||||
assert.ok(postsByChannel[channelId]);
|
||||
if (!shouldStop) {
|
||||
assert.ok(posts);
|
||||
assert.ok(postsByChannel);
|
||||
assert.ok(postsByChannel[channelId]);
|
||||
|
||||
assert.equal(postsByChannel[channelId].length, 0);
|
||||
assert.ok(!posts[postId]);
|
||||
assert.ok(!posts[post1a.id]);
|
||||
assert.equal(postsByChannel[channelId].length, 0);
|
||||
assert.ok(!posts[postId]);
|
||||
assert.ok(!posts[post1a.id]);
|
||||
|
||||
done();
|
||||
shouldStop = true;
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
Actions.removePost(
|
||||
|
|
@ -189,30 +193,35 @@ describe('Actions.Posts', () => {
|
|||
TestHelper.fakePost(channelId)
|
||||
);
|
||||
|
||||
let shouldStop = false;
|
||||
store.subscribe(() => {
|
||||
const state = store.getState();
|
||||
const getRequest = state.requests.posts.getPost;
|
||||
const {posts, postsByChannel} = state.entities.posts;
|
||||
|
||||
if (getRequest.status === RequestStatus.SUCCESS) {
|
||||
assert.ok(posts);
|
||||
assert.ok(postsByChannel);
|
||||
assert.ok(postsByChannel[channelId]);
|
||||
if (!shouldStop &&
|
||||
(getRequest.status === RequestStatus.SUCCESS || getRequest.status === RequestStatus.FAILURE)) {
|
||||
if (getRequest.error) {
|
||||
shouldStop = true;
|
||||
done(new Error(JSON.stringify(getRequest.error)));
|
||||
} else {
|
||||
assert.ok(posts);
|
||||
assert.ok(postsByChannel);
|
||||
assert.ok(postsByChannel[channelId]);
|
||||
|
||||
assert.ok(posts[post.id]);
|
||||
assert.ok(posts[post.id]);
|
||||
|
||||
let found = false;
|
||||
for (const postIdInChannel of postsByChannel[channelId]) {
|
||||
if (postIdInChannel === post.id) {
|
||||
found = true;
|
||||
break;
|
||||
let found = false;
|
||||
for (const postIdInChannel of postsByChannel[channelId]) {
|
||||
if (postIdInChannel === post.id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert.ok(found, 'failed to find post in postsByChannel');
|
||||
shouldStop = true;
|
||||
done();
|
||||
}
|
||||
assert.ok(found, 'failed to find post in postsByChannel');
|
||||
|
||||
done();
|
||||
} else if (getRequest.status === RequestStatus.FAILURE) {
|
||||
done(new Error(JSON.stringify(getRequest.error)));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -252,30 +261,36 @@ describe('Actions.Posts', () => {
|
|||
{...TestHelper.fakePost(channelId), root_id: post3.id}
|
||||
);
|
||||
|
||||
let shouldStop = false;
|
||||
store.subscribe(() => {
|
||||
const state = store.getState();
|
||||
const getRequest = state.requests.posts.getPosts;
|
||||
const {posts, postsByChannel} = state.entities.posts;
|
||||
|
||||
if (getRequest.status === RequestStatus.SUCCESS) {
|
||||
assert.ok(posts);
|
||||
assert.ok(postsByChannel);
|
||||
if (!shouldStop &&
|
||||
(getRequest.status === RequestStatus.SUCCESS || getRequest.status === RequestStatus.FAILURE)) {
|
||||
if (getRequest.error) {
|
||||
shouldStop = true;
|
||||
done(new Error(JSON.stringify(getRequest.error)));
|
||||
} else {
|
||||
assert.ok(posts);
|
||||
assert.ok(postsByChannel);
|
||||
|
||||
const postsInChannel = postsByChannel[channelId];
|
||||
assert.ok(postsInChannel);
|
||||
assert.equal(postsInChannel[0], post3a.id, 'wrong order for post3a');
|
||||
assert.equal(postsInChannel[1], post3.id, 'wrong order for post3');
|
||||
assert.equal(postsInChannel[3], post1a.id, 'wrong order for post1a');
|
||||
const postsInChannel = postsByChannel[channelId];
|
||||
assert.ok(postsInChannel);
|
||||
assert.equal(postsInChannel[0], post3a.id, 'wrong order for post3a');
|
||||
assert.equal(postsInChannel[1], post3.id, 'wrong order for post3');
|
||||
assert.equal(postsInChannel[3], post1a.id, 'wrong order for post1a');
|
||||
|
||||
assert.ok(posts[post1.id]);
|
||||
assert.ok(posts[post1a.id]);
|
||||
assert.ok(posts[post2.id]);
|
||||
assert.ok(posts[post3.id]);
|
||||
assert.ok(posts[post3a.id]);
|
||||
assert.ok(posts[post1.id]);
|
||||
assert.ok(posts[post1a.id]);
|
||||
assert.ok(posts[post2.id]);
|
||||
assert.ok(posts[post3.id]);
|
||||
assert.ok(posts[post3a.id]);
|
||||
|
||||
done();
|
||||
} else if (getRequest.status === RequestStatus.FAILURE) {
|
||||
done(new Error(JSON.stringify(getRequest.error)));
|
||||
shouldStop = true;
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -314,24 +329,30 @@ describe('Actions.Posts', () => {
|
|||
{...TestHelper.fakePost(channelId), root_id: post3.id}
|
||||
);
|
||||
|
||||
let shouldStop = false;
|
||||
store.subscribe(() => {
|
||||
const state = store.getState();
|
||||
const getRequest = state.requests.posts.getPostsSince;
|
||||
const {posts, postsByChannel} = state.entities.posts;
|
||||
|
||||
if (getRequest.status === RequestStatus.SUCCESS) {
|
||||
assert.ok(posts);
|
||||
assert.ok(postsByChannel);
|
||||
if (!shouldStop &&
|
||||
(getRequest.status === RequestStatus.SUCCESS || getRequest.status === RequestStatus.FAILURE)) {
|
||||
if (getRequest.error) {
|
||||
shouldStop = true;
|
||||
done(new Error(JSON.stringify(getRequest.error)));
|
||||
} else {
|
||||
assert.ok(posts);
|
||||
assert.ok(postsByChannel);
|
||||
|
||||
const postsInChannel = postsByChannel[channelId];
|
||||
assert.ok(postsInChannel);
|
||||
assert.equal(postsInChannel[0], post3a.id, 'wrong order for post3a');
|
||||
assert.equal(postsInChannel[1], post3.id, 'wrong order for post3');
|
||||
assert.equal(postsInChannel.length, 2, 'wrong size');
|
||||
const postsInChannel = postsByChannel[channelId];
|
||||
assert.ok(postsInChannel);
|
||||
assert.equal(postsInChannel[0], post3a.id, 'wrong order for post3a');
|
||||
assert.equal(postsInChannel[1], post3.id, 'wrong order for post3');
|
||||
assert.equal(postsInChannel.length, 2, 'wrong size');
|
||||
|
||||
done();
|
||||
} else if (getRequest.status === RequestStatus.FAILURE) {
|
||||
done(new Error(JSON.stringify(getRequest.error)));
|
||||
shouldStop = true;
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -371,24 +392,30 @@ describe('Actions.Posts', () => {
|
|||
{...TestHelper.fakePost(channelId), root_id: post3.id}
|
||||
);
|
||||
|
||||
let shouldStop = false;
|
||||
store.subscribe(() => {
|
||||
const state = store.getState();
|
||||
const getRequest = state.requests.posts.getPostsBefore;
|
||||
const {posts, postsByChannel} = state.entities.posts;
|
||||
|
||||
if (getRequest.status === RequestStatus.SUCCESS) {
|
||||
assert.ok(posts);
|
||||
assert.ok(postsByChannel);
|
||||
if (!shouldStop &&
|
||||
(getRequest.status === RequestStatus.SUCCESS || getRequest.status === RequestStatus.FAILURE)) {
|
||||
if (getRequest.error) {
|
||||
shouldStop = true;
|
||||
done(new Error(JSON.stringify(getRequest.error)));
|
||||
} else {
|
||||
assert.ok(posts);
|
||||
assert.ok(postsByChannel);
|
||||
|
||||
const postsInChannel = postsByChannel[channelId];
|
||||
assert.ok(postsInChannel);
|
||||
assert.equal(postsInChannel[0], post1a.id, 'wrong order for post1a');
|
||||
assert.equal(postsInChannel[1], post1.id, 'wrong order for post1');
|
||||
assert.equal(postsInChannel.length, 3, 'wrong size');
|
||||
const postsInChannel = postsByChannel[channelId];
|
||||
assert.ok(postsInChannel);
|
||||
assert.equal(postsInChannel[0], post1a.id, 'wrong order for post1a');
|
||||
assert.equal(postsInChannel[1], post1.id, 'wrong order for post1');
|
||||
assert.equal(postsInChannel.length, 3, 'wrong size');
|
||||
|
||||
done();
|
||||
} else if (getRequest.status === RequestStatus.FAILURE) {
|
||||
done(new Error(JSON.stringify(getRequest.error)));
|
||||
shouldStop = true;
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -430,24 +457,30 @@ describe('Actions.Posts', () => {
|
|||
{...TestHelper.fakePost(channelId), root_id: post3.id}
|
||||
);
|
||||
|
||||
let shouldStop = false;
|
||||
store.subscribe(() => {
|
||||
const state = store.getState();
|
||||
const getRequest = state.requests.posts.getPostsAfter;
|
||||
const {posts, postsByChannel} = state.entities.posts;
|
||||
|
||||
if (getRequest.status === RequestStatus.SUCCESS) {
|
||||
assert.ok(posts);
|
||||
assert.ok(postsByChannel);
|
||||
if (!shouldStop &&
|
||||
(getRequest.status === RequestStatus.SUCCESS || getRequest.status === RequestStatus.FAILURE)) {
|
||||
if (getRequest.error) {
|
||||
shouldStop = true;
|
||||
done(new Error(JSON.stringify(getRequest.error)));
|
||||
} else {
|
||||
assert.ok(posts);
|
||||
assert.ok(postsByChannel);
|
||||
|
||||
const postsInChannel = postsByChannel[channelId];
|
||||
assert.ok(postsInChannel);
|
||||
assert.equal(postsInChannel[0], post3a.id, 'wrong order for post3a');
|
||||
assert.equal(postsInChannel[1], post3.id, 'wrong order for post3');
|
||||
assert.equal(postsInChannel.length, 2, 'wrong size');
|
||||
const postsInChannel = postsByChannel[channelId];
|
||||
assert.ok(postsInChannel);
|
||||
assert.equal(postsInChannel[0], post3a.id, 'wrong order for post3a');
|
||||
assert.equal(postsInChannel[1], post3.id, 'wrong order for post3');
|
||||
assert.equal(postsInChannel.length, 2, 'wrong size');
|
||||
|
||||
done();
|
||||
} else if (getRequest.status === RequestStatus.FAILURE) {
|
||||
done(new Error(JSON.stringify(getRequest.error)));
|
||||
shouldStop = true;
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -141,29 +141,28 @@ describe('Actions.Teams', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// TODO: uncomment when PLT-4167 is merged
|
||||
// it('getMyTeamMembers', (done) => {
|
||||
// TestHelper.initBasic(Client).then(() => {
|
||||
// const store = configureStore();
|
||||
//
|
||||
// store.subscribe(() => {
|
||||
// const membersRequest = store.getState().requests.teams.getMyTeamMembers;
|
||||
// const members = store.getState().entities.teams.myMembers;
|
||||
//
|
||||
// if (membersRequest.status === RequestStatus.SUCCESS || membersRequest.status === RequestStatus.FAILURE) {
|
||||
// if (membersRequest.error) {
|
||||
// done(new Error(JSON.stringify(membersRequest.error)));
|
||||
// } else {
|
||||
// assert.ok(members);
|
||||
// assert.ok(members[TestHelper.basicTeam.id]);
|
||||
// done();
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// Actions.getMyTeamMembers()(store.dispatch, store.getState);
|
||||
// });
|
||||
// });
|
||||
it('getMyTeamMembers', (done) => {
|
||||
TestHelper.initBasic(Client).then(() => {
|
||||
const store = configureStore();
|
||||
|
||||
store.subscribe(() => {
|
||||
const membersRequest = store.getState().requests.teams.getMyTeamMembers;
|
||||
const members = store.getState().entities.teams.myMembers;
|
||||
|
||||
if (membersRequest.status === RequestStatus.SUCCESS || membersRequest.status === RequestStatus.FAILURE) {
|
||||
if (membersRequest.error) {
|
||||
done(new Error(JSON.stringify(membersRequest.error)));
|
||||
} else {
|
||||
assert.ok(members);
|
||||
assert.ok(members[TestHelper.basicTeam.id]);
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Actions.getMyTeamMembers()(store.dispatch, store.getState);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTeamMember', (done) => {
|
||||
TestHelper.initBasic(Client).then(async () => {
|
||||
|
|
@ -193,7 +192,7 @@ describe('Actions.Teams', () => {
|
|||
|
||||
Actions.getTeamMember(TestHelper.basicTeam.id, user.id)(store.dispatch, store.getState);
|
||||
});
|
||||
});
|
||||
}).timeout(3000);
|
||||
|
||||
it('getTeamMembersByIds', (done) => {
|
||||
TestHelper.initBasic(Client).then(async () => {
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ describe('Actions.Users', () => {
|
|||
it('getProfiles', (done) => {
|
||||
TestHelper.initBasic(Client).then(async () => {
|
||||
const store = configureStore();
|
||||
const user = await TestHelper.basicClient.createUser(TestHelper.fakeUser());
|
||||
await TestHelper.basicClient.createUser(TestHelper.fakeUser());
|
||||
|
||||
store.subscribe(() => {
|
||||
const profilesRequest = store.getState().requests.users.getProfiles;
|
||||
|
|
@ -123,7 +123,7 @@ describe('Actions.Users', () => {
|
|||
if (profilesRequest.error) {
|
||||
done(new Error(JSON.stringify(profilesRequest.error)));
|
||||
} else {
|
||||
assert.ok(profiles[user.id]);
|
||||
assert.ok(Object.keys(profiles).length);
|
||||
|
||||
done();
|
||||
}
|
||||
|
|
@ -215,7 +215,7 @@ describe('Actions.Users', () => {
|
|||
}
|
||||
});
|
||||
|
||||
Actions.getProfilesNotInChannel(TestHelper.basicTeam.id, TestHelper.basicChannel.id, 0)(store.dispatch, store.getState);
|
||||
Actions.getProfilesNotInChannel(TestHelper.basicTeam.id, TestHelper.basicChannel.id, 0, 500)(store.dispatch, store.getState);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
298
test/service/actions/websocket.test.js
Normal file
298
test/service/actions/websocket.test.js
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
import assert from 'assert';
|
||||
import * as Actions from 'service/actions/websocket';
|
||||
import * as ChannelActions from 'service/actions/channels';
|
||||
import * as TeamActions from 'service/actions/teams';
|
||||
import * as RootActions from 'app/actions/views/root';
|
||||
import Client from 'service/client';
|
||||
import configureStore from 'app/store';
|
||||
import {Constants, RequestStatus} from 'service/constants';
|
||||
import TestHelper from 'test/test_helper';
|
||||
|
||||
describe('Actions.Websocket', () => {
|
||||
let store;
|
||||
beforeEach(async () => {
|
||||
store = configureStore();
|
||||
await TestHelper.initBasic(Client);
|
||||
return await Actions.init()(store.dispatch, store.getState);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
Actions.close()();
|
||||
});
|
||||
|
||||
it('WebSocket Connect', () => {
|
||||
const ws = store.getState().requests.general.websocket;
|
||||
assert.ok(ws.status === RequestStatus.SUCCESS);
|
||||
});
|
||||
|
||||
it('Websocket Handle New Post', async () => {
|
||||
const client = TestHelper.createClient();
|
||||
const user = await client.createUserWithInvite(
|
||||
TestHelper.fakeUser(),
|
||||
null,
|
||||
null,
|
||||
TestHelper.basicTeam.invite_id
|
||||
);
|
||||
await client.login(user.email, 'password1');
|
||||
|
||||
await Client.addChannelMember(
|
||||
TestHelper.basicTeam.id,
|
||||
TestHelper.basicChannel.id,
|
||||
user.id);
|
||||
|
||||
const post = {...TestHelper.fakePost(), channel_id: TestHelper.basicChannel.id};
|
||||
await client.createPost(TestHelper.basicTeam.id, post);
|
||||
|
||||
const entities = store.getState().entities;
|
||||
const {posts, postsByChannel} = entities.posts;
|
||||
const channelId = TestHelper.basicChannel.id;
|
||||
const postId = postsByChannel[channelId][0];
|
||||
|
||||
assert.ok(posts[postId].message.indexOf('Unit Test') > -1);
|
||||
});
|
||||
|
||||
it('Websocket Handle Post Edited', async () => {
|
||||
let post = {...TestHelper.fakePost(), channel_id: TestHelper.basicChannel.id};
|
||||
const client = TestHelper.createClient();
|
||||
const user = await client.createUserWithInvite(
|
||||
TestHelper.fakeUser(),
|
||||
null,
|
||||
null,
|
||||
TestHelper.basicTeam.invite_id
|
||||
);
|
||||
|
||||
await Client.addChannelMember(
|
||||
TestHelper.basicTeam.id,
|
||||
TestHelper.basicChannel.id,
|
||||
user.id);
|
||||
|
||||
await client.login(user.email, 'password1');
|
||||
|
||||
post = await client.createPost(TestHelper.basicTeam.id, post);
|
||||
post.message += ' (edited)';
|
||||
|
||||
await client.editPost(TestHelper.basicTeam.id, post);
|
||||
|
||||
store.subscribe(async () => {
|
||||
const entities = store.getState().entities;
|
||||
const {posts} = entities.posts;
|
||||
assert.ok(posts[post.id].message.indexOf('(edited)') > -1);
|
||||
});
|
||||
});
|
||||
|
||||
it('Websocket Handle Post Deleted', async () => {
|
||||
const client = TestHelper.createClient();
|
||||
const user = await client.createUserWithInvite(
|
||||
TestHelper.fakeUser(),
|
||||
null,
|
||||
null,
|
||||
TestHelper.basicTeam.invite_id
|
||||
);
|
||||
|
||||
await Client.addChannelMember(
|
||||
TestHelper.basicTeam.id,
|
||||
TestHelper.basicChannel.id,
|
||||
user.id);
|
||||
|
||||
await client.login(user.email, 'password1');
|
||||
let post = TestHelper.fakePost();
|
||||
post.channel_id = TestHelper.basicChannel.id;
|
||||
post = await client.createPost(TestHelper.basicTeam.id, post);
|
||||
|
||||
await client.deletePost(TestHelper.basicTeam.id, post.channel_id, post.id);
|
||||
|
||||
store.subscribe(async () => {
|
||||
const entities = store.getState().entities;
|
||||
const {posts} = entities.posts;
|
||||
assert.strictEqual(posts[post.id].state, Constants.POST_DELETED);
|
||||
});
|
||||
});
|
||||
|
||||
it('WebSocket Leave Team', async () => {
|
||||
const client = TestHelper.createClient();
|
||||
const user = await client.createUser(TestHelper.fakeUser());
|
||||
await client.login(user.email, 'password1');
|
||||
const team = await client.createTeam(TestHelper.fakeTeam());
|
||||
const channel = await client.createChannel(TestHelper.fakeChannel(team.id));
|
||||
await client.addUserToTeam(team.id, TestHelper.basicUser.id);
|
||||
await client.addChannelMember(team.id, channel.id, TestHelper.basicUser.id);
|
||||
|
||||
await RootActions.setStoreFromLocalData({url: Client.getUrl(), token: Client.getToken()})(store.dispatch, store.getState);
|
||||
await TeamActions.selectTeam(team)(store.dispatch, store.getState);
|
||||
await ChannelActions.selectChannel(channel.id)(store.dispatch, store.getState);
|
||||
await client.removeUserFromTeam(team.id, TestHelper.basicUser.id);
|
||||
|
||||
const {currentId} = store.getState().entities.teams;
|
||||
assert.strictEqual(currentId, '');
|
||||
});
|
||||
|
||||
it('Websocket Handle User Added', async () => {
|
||||
const client = TestHelper.createClient();
|
||||
const user = await client.createUserWithInvite(
|
||||
TestHelper.fakeUser(),
|
||||
null,
|
||||
null,
|
||||
TestHelper.basicTeam.invite_id
|
||||
);
|
||||
|
||||
await TeamActions.selectTeam(TestHelper.basicTeam)(store.dispatch, store.getState);
|
||||
await RootActions.setStoreFromLocalData({
|
||||
url: Client.getUrl(),
|
||||
token: Client.getToken()
|
||||
})(store.dispatch, store.getState);
|
||||
|
||||
await ChannelActions.addChannelMember(
|
||||
TestHelper.basicTeam.id,
|
||||
TestHelper.basicChannel.id,
|
||||
user.id
|
||||
)(store.dispatch, store.getState);
|
||||
|
||||
const entities = store.getState().entities;
|
||||
const profilesInChannel = entities.users.profilesInChannel;
|
||||
assert.ok(profilesInChannel[TestHelper.basicChannel.id].has(user.id));
|
||||
});
|
||||
|
||||
it('Websocket Handle User Removed', async () => {
|
||||
await TeamActions.selectTeam(TestHelper.basicTeam)(store.dispatch, store.getState);
|
||||
await RootActions.setStoreFromLocalData({
|
||||
url: Client.getUrl(),
|
||||
token: Client.getToken()
|
||||
})(store.dispatch, store.getState);
|
||||
|
||||
await ChannelActions.removeChannelMember(
|
||||
TestHelper.basicTeam.id,
|
||||
TestHelper.basicChannel.id,
|
||||
TestHelper.basicUser.id
|
||||
)(store.dispatch, store.getState);
|
||||
|
||||
const state = store.getState();
|
||||
const entities = state.entities;
|
||||
const profilesNotInChannel = entities.users.profilesNotInChannel;
|
||||
|
||||
assert.ok(profilesNotInChannel[TestHelper.basicChannel.id].has(TestHelper.basicUser.id));
|
||||
});
|
||||
|
||||
it('Websocket Handle User Updated', async () => {
|
||||
const client = TestHelper.createClient();
|
||||
const user = await client.createUserWithInvite(
|
||||
TestHelper.fakeUser(),
|
||||
null,
|
||||
null,
|
||||
TestHelper.basicTeam.invite_id
|
||||
);
|
||||
|
||||
await client.login(user.email, 'password1');
|
||||
await client.updateUser({...user, first_name: 'tester4'});
|
||||
|
||||
store.subscribe(() => {
|
||||
const state = store.getState();
|
||||
const entities = state.entities;
|
||||
const profiles = entities.users.profiles;
|
||||
|
||||
assert.strictEqual(profiles[user.id].first_name, 'tester4');
|
||||
});
|
||||
});
|
||||
|
||||
it('Websocket Handle Channel Viewed', (done) => {
|
||||
async function test() {
|
||||
await TeamActions.selectTeam(TestHelper.basicTeam)(store.dispatch, store.getState);
|
||||
await RootActions.setStoreFromLocalData({
|
||||
url: Client.getUrl(),
|
||||
token: Client.getToken()
|
||||
})(store.dispatch, store.getState);
|
||||
|
||||
await Client.updateLastViewedAt(
|
||||
TestHelper.basicTeam.id,
|
||||
TestHelper.basicChannel.id,
|
||||
false
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
const state = store.getState();
|
||||
const entities = state.entities;
|
||||
const {channels} = entities.channels;
|
||||
assert.ok(channels[TestHelper.basicChannel.id]);
|
||||
done();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
test();
|
||||
});
|
||||
|
||||
it('Websocket Handle Channel Deleted', (done) => {
|
||||
async function test() {
|
||||
await ChannelActions.fetchMyChannelsAndMembers(TestHelper.basicTeam.id)(store.dispatch, store.getState);
|
||||
await TeamActions.selectTeam(TestHelper.basicTeam)(store.dispatch, store.getState);
|
||||
await ChannelActions.selectChannel(TestHelper.basicChannel.id)(store.dispatch, store.getState);
|
||||
await Client.deleteChannel(
|
||||
TestHelper.basicTeam.id,
|
||||
TestHelper.basicChannel.id
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
const state = store.getState();
|
||||
const entities = state.entities;
|
||||
const {channels, currentId} = entities.channels;
|
||||
|
||||
assert.ok(channels[currentId].name === Constants.DEFAULT_CHANNEL);
|
||||
done();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
test();
|
||||
});
|
||||
|
||||
it('Websocket Handle Direct Channel', (done) => {
|
||||
// TODO: Uncomment once fixed on platform
|
||||
// const test = async () => {
|
||||
// const client = TestHelper.createClient();
|
||||
// const user = await client.createUserWithInvite(
|
||||
// TestHelper.fakeUser(),
|
||||
// null,
|
||||
// null,
|
||||
// TestHelper.basicTeam.invite_id
|
||||
// );
|
||||
//
|
||||
// await client.login(user.email, 'password1');
|
||||
// await TeamActions.selectTeam(TestHelper.basicTeam)(store.dispatch, store.getState);
|
||||
//
|
||||
// store.subscribe(() => {
|
||||
// const entities = store.getState().entities;
|
||||
// const {channels} = entities.channels;
|
||||
// assert.ok(Object.keys(channels).length);
|
||||
// done();
|
||||
// });
|
||||
//
|
||||
// await client.createDirectChannel(TestHelper.basicTeam.id, TestHelper.basicUser.id);
|
||||
// };
|
||||
// test();
|
||||
done();
|
||||
});
|
||||
|
||||
it('Websocket Handle Preferences Changed', (done) => {
|
||||
async function test() {
|
||||
const client = TestHelper.createClient();
|
||||
const user = await client.createUserWithInvite(
|
||||
TestHelper.fakeUser(),
|
||||
null,
|
||||
null,
|
||||
TestHelper.basicTeam.invite_id
|
||||
);
|
||||
await client.login(user.email, 'password1');
|
||||
const dm = await client.createDirectChannel(TestHelper.basicTeam.id, TestHelper.basicUser.id);
|
||||
const post = {...TestHelper.fakePost(), channel_id: dm.id};
|
||||
await client.createPost(TestHelper.basicTeam.id, post);
|
||||
|
||||
setTimeout(() => {
|
||||
const entities = store.getState().entities;
|
||||
const preferences = entities.preferences.myPreferences;
|
||||
assert.ok(preferences[`${Constants.CATEGORY_DIRECT_CHANNEL_SHOW}--${user.id}`]);
|
||||
done();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
test();
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue