From ad343a0f4ad175053f7d0da12a0587bcbb396d1c Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Tue, 12 Jul 2016 09:36:27 -0400 Subject: Added infrastructure for basic WebSocket API (#3432) --- webapp/actions/global_actions.jsx | 4 +- webapp/actions/websocket_actions.jsx | 148 ++++++++--------------------------- webapp/package.json | 2 +- webapp/utils/websocket_client.jsx | 7 ++ webapp/webpack.config.js | 9 +++ 5 files changed, 53 insertions(+), 117 deletions(-) create mode 100644 webapp/utils/websocket_client.jsx (limited to 'webapp') diff --git a/webapp/actions/global_actions.jsx b/webapp/actions/global_actions.jsx index d9b89f987..8d90b226d 100644 --- a/webapp/actions/global_actions.jsx +++ b/webapp/actions/global_actions.jsx @@ -12,7 +12,6 @@ import TeamStore from 'stores/team_store.jsx'; import PreferenceStore from 'stores/preference_store.jsx'; import SearchStore from 'stores/search_store.jsx'; -import * as Websockets from 'actions/websocket_actions.jsx'; import {handleNewPost} from 'actions/post_actions.jsx'; import Constants from 'utils/constants.jsx'; @@ -20,6 +19,7 @@ const ActionTypes = Constants.ActionTypes; import Client from 'utils/web_client.jsx'; import * as AsyncClient from 'utils/async_client.jsx'; +import WebSocketClient from 'utils/websocket_client.jsx'; import * as Utils from 'utils/utils.jsx'; import en from 'i18n/en.json'; @@ -439,7 +439,7 @@ var lastTimeTypingSent = 0; export function emitLocalUserTypingEvent(channelId, parentId) { const t = Date.now(); if ((t - lastTimeTypingSent) > Constants.UPDATE_TYPING_MS) { - Websockets.sendMessage({channel_id: channelId, action: 'typing', props: {parent_id: parentId}, state: {}}); + WebSocketClient.userTyping(channelId, parentId); lastTimeTypingSent = t; } } diff --git a/webapp/actions/websocket_actions.jsx b/webapp/actions/websocket_actions.jsx index 7be9d84f3..e6997b9cc 100644 --- a/webapp/actions/websocket_actions.jsx +++ b/webapp/actions/websocket_actions.jsx @@ -11,6 +11,7 @@ import ErrorStore from 'stores/error_store.jsx'; import NotificationStore from 'stores/notification_store.jsx'; //eslint-disable-line no-unused-vars import Client from 'utils/web_client.jsx'; +import WebSocketClient from 'utils/websocket_client.jsx'; import * as Utils from 'utils/utils.jsx'; import * as AsyncClient from 'utils/async_client.jsx'; import * as GlobalActions from 'actions/global_actions.jsx'; @@ -23,16 +24,9 @@ const SocketEvents = Constants.SocketEvents; import {browserHistory} from 'react-router/es6'; const MAX_WEBSOCKET_FAILS = 7; -const MIN_WEBSOCKET_RETRY_TIME = 3000; // 3 sec -const MAX_WEBSOCKET_RETRY_TIME = 300000; // 5 mins - -var conn = null; -var connectFailCount = 0; -var pastFirstInit = false; -var manuallyClosed = false; export function initialize() { - if (window.WebSocket && !conn) { + if (window.WebSocket) { let protocol = 'ws://'; if (window.location.protocol === 'https:') { protocol = 'wss://'; @@ -40,85 +34,35 @@ export function initialize() { const connUrl = protocol + location.host + ((/:\d+/).test(location.host) ? '' : Utils.getWebsocketPort(protocol)) + Client.getUsersRoute() + '/websocket'; - if (connectFailCount === 0) { - console.log('websocket connecting to ' + connUrl); //eslint-disable-line no-console - } - - manuallyClosed = false; - - conn = new WebSocket(connUrl); - - conn.onopen = () => { - if (connectFailCount > 0) { - console.log('websocket re-established connection'); //eslint-disable-line no-console - AsyncClient.getChannels(); - AsyncClient.getPosts(ChannelStore.getCurrentId()); - } - - if (pastFirstInit) { - ErrorStore.clearLastError(); - ErrorStore.emitChange(); - } - - pastFirstInit = true; - connectFailCount = 0; - }; - - conn.onclose = () => { - conn = null; - - if (connectFailCount === 0) { - console.log('websocket closed'); //eslint-disable-line no-console - } - - if (manuallyClosed) { - return; - } - - connectFailCount = connectFailCount + 1; - - var retryTime = MIN_WEBSOCKET_RETRY_TIME; - - if (connectFailCount > MAX_WEBSOCKET_FAILS) { - ErrorStore.storeLastError({message: Utils.localizeMessage('channel_loader.socketError', 'Please check connection, Mattermost unreachable. If issue persists, ask administrator to check WebSocket port.')}); - - // If we've failed a bunch of connections then start backing off - retryTime = MIN_WEBSOCKET_RETRY_TIME * connectFailCount * connectFailCount; - if (retryTime > MAX_WEBSOCKET_RETRY_TIME) { - retryTime = MAX_WEBSOCKET_RETRY_TIME; - } - } - - ErrorStore.setConnectionErrorCount(connectFailCount); - ErrorStore.emitChange(); - - setTimeout( - () => { - initialize(); - }, - retryTime - ); - }; - - conn.onerror = (evt) => { - if (connectFailCount <= 1) { - console.log('websocket error'); //eslint-disable-line no-console - console.log(evt); //eslint-disable-line no-console - } - }; - - conn.onmessage = (evt) => { - const msg = JSON.parse(evt.data); - handleMessage(msg); - }; + WebSocketClient.initialize(connUrl); + WebSocketClient.setEventCallback(handleEvent); + WebSocketClient.setReconnectCallback(handleReconnect); + WebSocketClient.setCloseCallback(handleClose); } } -function handleMessage(msg) { - // Let the store know we are online. This probably shouldn't be here. - UserStore.setStatus(msg.user_id, 'online'); +export function close() { + WebSocketClient.close(); +} + +function handleReconnect() { + AsyncClient.getChannels(); + AsyncClient.getPosts(ChannelStore.getCurrentId()); + ErrorStore.clearLastError(); + ErrorStore.emitChange(); +} + +function handleClose(failCount) { + if (failCount > MAX_WEBSOCKET_FAILS) { + ErrorStore.storeLastError({message: Utils.localizeMessage('channel_loader.socketError', 'Please check connection, Mattermost unreachable. If issue persists, ask administrator to check WebSocket port.')}); + } + + ErrorStore.setConnectionErrorCount(failCount); + ErrorStore.emitChange(); +} - switch (msg.action) { +function handleEvent(msg) { + switch (msg.event) { case SocketEvents.POSTED: case SocketEvents.EPHEMERAL_MESSAGE: handleNewPostEvent(msg); @@ -172,36 +116,14 @@ function handleMessage(msg) { } } -export function sendMessage(msg) { - if (conn && conn.readyState === WebSocket.OPEN) { - var teamId = TeamStore.getCurrentId(); - if (teamId && teamId.length > 0) { - msg.team_id = teamId; - } - - conn.send(JSON.stringify(msg)); - } else if (!conn || conn.readyState === WebSocket.Closed) { - conn = null; - initialize(); - } -} - -export function close() { - manuallyClosed = true; - connectFailCount = 0; - if (conn && conn.readyState === WebSocket.OPEN) { - conn.close(); - } -} - function handleNewPostEvent(msg) { - const post = JSON.parse(msg.props.post); + const post = JSON.parse(msg.data.post); handleNewPost(post, msg); } function handlePostEditEvent(msg) { // Store post - const post = JSON.parse(msg.props.post); + const post = JSON.parse(msg.data.post); PostStore.storePost(post); PostStore.emitChange(); @@ -214,7 +136,7 @@ function handlePostEditEvent(msg) { } function handlePostDeleteEvent(msg) { - const post = JSON.parse(msg.props.post); + const post = JSON.parse(msg.data.post); GlobalActions.emitPostDeletedEvent(post); } @@ -257,12 +179,12 @@ function handleUserRemovedEvent(msg) { if (UserStore.getCurrentId() === msg.user_id) { AsyncClient.getChannels(); - if (msg.props.remover_id !== msg.user_id && + if (msg.data.remover_id !== msg.user_id && msg.channel_id === ChannelStore.getCurrentId() && $('#removed_from_channel').length > 0) { var sentState = {}; sentState.channelName = ChannelStore.getCurrent().display_name; - sentState.remover = UserStore.getProfile(msg.props.remover_id).username; + sentState.remover = UserStore.getProfile(msg.data.remover_id).username; BrowserStore.setItem('channel-removed-state', sentState); $('#removed_from_channel').modal('show'); @@ -290,12 +212,10 @@ function handleChannelDeletedEvent(msg) { } function handlePreferenceChangedEvent(msg) { - const preference = JSON.parse(msg.props.preference); + const preference = JSON.parse(msg.data.preference); GlobalActions.emitPreferenceChangedEvent(preference); } function handleUserTypingEvent(msg) { - if (TeamStore.getCurrentId() === msg.team_id) { - GlobalActions.emitRemoteUserTypingEvent(msg.channel_id, msg.user_id, msg.props.parent_id); - } + GlobalActions.emitRemoteUserTypingEvent(msg.channel_id, msg.user_id, msg.data.parent_id); } diff --git a/webapp/package.json b/webapp/package.json index 3db9d0794..984affd08 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -18,7 +18,7 @@ "keymirror": "0.1.1", "marked": "mattermost/marked#12d2be4cdf54d4ec95fead934e18840b6a2c1a7b", "match-at": "0.1.0", - "mattermost": "mattermost/mattermost-javascript#5815f14f0d1960aa4c99797b09d949d2959eb24f", + "mattermost": "mattermost/mattermost-javascript#4cdaeba22ff82bf93dc417af1ab4e89e3248d624", "object-assign": "4.1.0", "perfect-scrollbar": "0.6.11", "react": "15.0.2", diff --git a/webapp/utils/websocket_client.jsx b/webapp/utils/websocket_client.jsx new file mode 100644 index 000000000..135d96466 --- /dev/null +++ b/webapp/utils/websocket_client.jsx @@ -0,0 +1,7 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import WebSocketClient from 'mattermost/websocket_client.jsx'; + +var WebClient = new WebSocketClient(); +export default WebClient; diff --git a/webapp/webpack.config.js b/webapp/webpack.config.js index 2911c0c7d..88635ef03 100644 --- a/webapp/webpack.config.js +++ b/webapp/webpack.config.js @@ -52,6 +52,15 @@ var config = { cacheDirectory: DEV } }, + { + test: /node_modules\/mattermost\/websocket_client\.jsx?$/, + loader: 'babel', + query: { + presets: ['react', 'es2015-webpack', 'stage-0'], + plugins: ['transform-runtime'], + cacheDirectory: DEV + } + }, { test: /\.json$/, loader: 'json' -- cgit v1.2.3-1-g7c22