diff options
-rw-r--r-- | web/react/components/error_bar.jsx | 85 | ||||
-rw-r--r-- | web/react/components/textbox.jsx | 53 | ||||
-rw-r--r-- | web/react/pages/channel.jsx | 6 | ||||
-rw-r--r-- | web/react/stores/error_store.jsx | 2 | ||||
-rw-r--r-- | web/react/stores/socket_store.jsx | 42 | ||||
-rw-r--r-- | web/react/utils/client.jsx | 2 |
6 files changed, 107 insertions, 83 deletions
diff --git a/web/react/components/error_bar.jsx b/web/react/components/error_bar.jsx index 87d94a41d..05726e860 100644 --- a/web/react/components/error_bar.jsx +++ b/web/react/components/error_bar.jsx @@ -2,10 +2,6 @@ // See License.txt for license information. var ErrorStore = require('../stores/error_store.jsx'); -var utils = require('../utils/utils.jsx'); -var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); -var Constants = require('../utils/constants.jsx'); -var ActionTypes = Constants.ActionTypes; export default class ErrorBar extends React.Component { constructor() { @@ -13,70 +9,79 @@ export default class ErrorBar extends React.Component { this.onErrorChange = this.onErrorChange.bind(this); this.handleClose = this.handleClose.bind(this); + this.prevTimer = null; - this.state = this.getStateFromStores(); - if (this.state.message) { - setTimeout(this.handleClose, 10000); + this.state = ErrorStore.getLastError(); + if (this.state && this.state.message) { + this.prevTimer = setTimeout(this.handleClose, 10000); } } - getStateFromStores() { - var error = ErrorStore.getLastError(); - if (!error || error.message === 'There appears to be a problem with your internet connection') { - return {message: null}; - } - return {message: error.message}; - } componentDidMount() { ErrorStore.addChangeListener(this.onErrorChange); $('body').css('padding-top', $(React.findDOMNode(this)).outerHeight()); - $(window).resize(function onResize() { - if (this.state.message) { + $(window).resize(() => { + if (this.state && this.state.message) { $('body').css('padding-top', $(React.findDOMNode(this)).outerHeight()); } - }.bind(this)); + }); } + componentWillUnmount() { ErrorStore.removeChangeListener(this.onErrorChange); } + onErrorChange() { - var newState = this.getStateFromStores(); - if (!utils.areStatesEqual(newState, this.state)) { - if (newState.message) { - setTimeout(this.handleClose, 10000); - } + var newState = ErrorStore.getLastError(); + + if (this.prevTimer != null) { + clearInterval(this.prevTimer); + this.prevTimer = null; + } + if (newState) { this.setState(newState); + this.prevTimer = setTimeout(this.handleClose, 10000); + } else { + this.setState({message: null}); } } + handleClose(e) { if (e) { e.preventDefault(); } - AppDispatcher.handleServerAction({ - type: ActionTypes.RECIEVED_ERROR, - err: null - }); + ErrorStore.storeLastError(null); + ErrorStore.emitChange(); $('body').css('padding-top', '0'); } + render() { - if (this.state.message) { - return ( - <div className='error-bar'> - <span>{this.state.message}</span> - <a - href='#' - className='error-bar__close' - onClick={this.handleClose} - > - × - </a> - </div> - ); + if (!this.state) { + return <div/>; + } + + if (!this.state.message) { + return <div/>; + } + + if (this.state.connErrorCount < 7) { + return <div/>; } - return <div/>; + return ( + <div className='error-bar'> + <span>{this.state.message}</span> + <a + href='#' + className='error-bar__close' + onClick={this.handleClose} + > + × + </a> + </div> + ); } } diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx index ea8126bec..5f5316013 100644 --- a/web/react/components/textbox.jsx +++ b/web/react/components/textbox.jsx @@ -5,7 +5,6 @@ const AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); const PostStore = require('../stores/post_store.jsx'); const CommandList = require('./command_list.jsx'); const ErrorStore = require('../stores/error_store.jsx'); -const AsyncClient = require('../utils/async_client.jsx'); const Utils = require('../utils/utils.jsx'); const Constants = require('../utils/constants.jsx'); @@ -18,7 +17,6 @@ export default class Textbox extends React.Component { this.getStateFromStores = this.getStateFromStores.bind(this); this.onListenerChange = this.onListenerChange.bind(this); this.onRecievedError = this.onRecievedError.bind(this); - this.onTimerInterrupt = this.onTimerInterrupt.bind(this); this.updateMentionTab = this.updateMentionTab.bind(this); this.handleChange = this.handleChange.bind(this); this.handleKeyPress = this.handleKeyPress.bind(this); @@ -35,8 +33,7 @@ export default class Textbox extends React.Component { this.state = { mentionText: '-1', mentions: [], - connection: '', - timerInterrupt: null + connection: '' }; this.caret = -1; @@ -44,6 +41,7 @@ export default class Textbox extends React.Component { this.doProcessMentions = false; this.mentions = []; } + getStateFromStores() { const error = ErrorStore.getLastError(); @@ -53,6 +51,7 @@ export default class Textbox extends React.Component { return {message: null}; } + componentDidMount() { PostStore.addAddMentionListener(this.onListenerChange); ErrorStore.addChangeListener(this.onRecievedError); @@ -60,46 +59,28 @@ export default class Textbox extends React.Component { this.resize(); this.updateMentionTab(null); } + componentWillUnmount() { PostStore.removeAddMentionListener(this.onListenerChange); ErrorStore.removeChangeListener(this.onRecievedError); } + onListenerChange(id, username) { if (id === this.props.id) { this.addMention(username); } } - onRecievedError() { - const errorState = this.getStateFromStores(); - if (this.state.timerInterrupt !== null) { - window.clearInterval(this.state.timerInterrupt); - this.setState({timerInterrupt: null}); - } + onRecievedError() { + const errorState = ErrorStore.getLastError(); - if (errorState.message === 'There appears to be a problem with your internet connection') { + if (errorState && errorState.connErrorCount > 0) { this.setState({connection: 'bad-connection'}); - const timerInterrupt = window.setInterval(this.onTimerInterrupt, 5000); - this.setState({timerInterrupt: timerInterrupt}); } else { this.setState({connection: ''}); } } - onTimerInterrupt() { - // Since these should only happen when you have no connection and slightly briefly after any - // performance hit should not matter - if (this.state.connection === 'bad-connection') { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECIEVED_ERROR, - err: null - }); - - AsyncClient.updateLastViewedAt(); - } - window.clearInterval(this.state.timerInterrupt); - this.setState({timerInterrupt: null}); - } componentDidUpdate() { if (this.caret >= 0) { Utils.setCaretPosition(React.findDOMNode(this.refs.message), this.caret); @@ -111,6 +92,7 @@ export default class Textbox extends React.Component { } this.resize(); } + componentWillReceiveProps(nextProps) { if (!this.addedMention) { this.checkForNewMention(nextProps.messageText); @@ -122,19 +104,22 @@ export default class Textbox extends React.Component { this.addedMention = false; this.refs.commands.getSuggestedCommands(nextProps.messageText); } + updateMentionTab(mentionText) { // using setTimeout so dispatch isn't called during an in progress dispatch - setTimeout(function updateMentionTabAfterTimeout() { + setTimeout(() => { AppDispatcher.handleViewAction({ type: ActionTypes.RECIEVED_MENTION_DATA, id: this.props.id, mention_text: mentionText }); - }.bind(this), 1); + }, 1); } + handleChange() { this.props.onUserInput(React.findDOMNode(this.refs.message).value); } + handleKeyPress(e) { const text = React.findDOMNode(this.refs.message).value; @@ -157,6 +142,7 @@ export default class Textbox extends React.Component { this.props.onKeyPress(e); } + handleKeyDown(e) { if (Utils.getSelectedText(React.findDOMNode(this.refs.message)) !== '') { this.doProcessMentions = true; @@ -166,6 +152,7 @@ export default class Textbox extends React.Component { this.handleBackspace(e); } } + handleBackspace() { const text = React.findDOMNode(this.refs.message).value; if (text.indexOf('/') === 0) { @@ -185,6 +172,7 @@ export default class Textbox extends React.Component { this.doProcessMentions = true; } } + checkForNewMention(text) { const caret = Utils.getCaretPosition(React.findDOMNode(this.refs.message)); @@ -211,6 +199,7 @@ export default class Textbox extends React.Component { const name = preText.substring(atIndex + 1, preText.length).toLowerCase(); this.updateMentionTab(name); } + addMention(name) { const caret = Utils.getCaretPosition(React.findDOMNode(this.refs.message)); @@ -233,11 +222,13 @@ export default class Textbox extends React.Component { this.props.onUserInput(`${prefix}@${name} ${suffix}`); } + addCommand(cmd) { const elm = React.findDOMNode(this.refs.message); elm.value = cmd; this.handleChange(); } + resize() { const e = React.findDOMNode(this.refs.message); const w = React.findDOMNode(this.refs.wrapper); @@ -264,21 +255,25 @@ export default class Textbox extends React.Component { this.props.onHeightChange(); } } + handleFocus() { const elm = React.findDOMNode(this.refs.message); if (elm.title === elm.value) { elm.value = ''; } } + handleBlur() { const elm = React.findDOMNode(this.refs.message); if (elm.value === '') { elm.value = elm.title; } } + handlePaste() { this.doProcessMentions = true; } + render() { return ( <div diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx index 07207c556..74259194a 100644 --- a/web/react/pages/channel.jsx +++ b/web/react/pages/channel.jsx @@ -26,6 +26,7 @@ var ChannelInviteModal = require('../components/channel_invite_modal.jsx'); var TeamMembersModal = require('../components/team_members.jsx'); var DirectChannelModal = require('../components/more_direct_channels.jsx'); var ErrorBar = require('../components/error_bar.jsx'); +var ErrorStore = require('../stores/error_store.jsx'); var ChannelLoader = require('../components/channel_loader.jsx'); var MentionList = require('../components/mention_list.jsx'); var ChannelInfoModal = require('../components/channel_info_modal.jsx'); @@ -234,6 +235,11 @@ function setupChannelPage(props) { <RegisterAppModal />, document.getElementById('register_app_modal') ); + + if (global.window.config.SendEmailNotifications === 'false') { + ErrorStore.storeLastError({message: 'Preview Mode: Email notifications have not been configured'}); + ErrorStore.emitChange(); + } } global.window.setup_channel_page = setupChannelPage; diff --git a/web/react/stores/error_store.jsx b/web/react/stores/error_store.jsx index 597c88cff..ece7d8522 100644 --- a/web/react/stores/error_store.jsx +++ b/web/react/stores/error_store.jsx @@ -48,7 +48,7 @@ class ErrorStoreClass extends EventEmitter { var ErrorStore = new ErrorStoreClass(); -ErrorStore.dispatchToken = AppDispatcher.register(function registry(payload) { +ErrorStore.dispatchToken = AppDispatcher.register((payload) => { var action = payload.action; switch (action.type) { case ActionTypes.RECIEVED_ERROR: diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx index ae74059d1..1d853f979 100644 --- a/web/react/stores/socket_store.jsx +++ b/web/react/stores/socket_store.jsx @@ -3,6 +3,7 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var UserStore = require('./user_store.jsx'); +var ErrorStore = require('./error_store.jsx'); var EventEmitter = require('events').EventEmitter; var Constants = require('../utils/constants.jsx'); @@ -21,6 +22,7 @@ class SocketStoreClass extends EventEmitter { this.addChangeListener = this.addChangeListener.bind(this); this.removeChangeListener = this.removeChangeListener.bind(this); this.sendMessage = this.sendMessage.bind(this); + this.failCount = 0; this.initialize(); } @@ -37,27 +39,43 @@ class SocketStoreClass extends EventEmitter { protocol = 'wss://'; } var connUrl = protocol + location.host + '/api/v1/websocket'; - console.log('connecting to ' + connUrl); //eslint-disable-line no-console + if (this.failCount === 0) { + console.log('websocket connecting to ' + connUrl); //eslint-disable-line no-console + } conn = new WebSocket(connUrl); - conn.onclose = function closeConn(evt) { - console.log('websocket closed'); //eslint-disable-line no-console - console.log(evt); //eslint-disable-line no-console + conn.onopen = () => { + if (this.failCount > 0) { + console.log('websocket re-established connection'); //eslint-disable-line no-console + } + + this.failCount = 0; + ErrorStore.storeLastError(null); + ErrorStore.emitChange(); + }; + + conn.onclose = () => { conn = null; setTimeout( - function reconnect() { + () => { this.initialize(); - }.bind(this), + }, 3000 ); - }.bind(this); + }; + + conn.onerror = (evt) => { + if (this.failCount === 0) { + console.log('websocket error ' + evt); //eslint-disable-line no-console + } + + this.failCount = this.failCount + 1; - conn.onerror = function connError(evt) { - console.log('websocket error'); //eslint-disable-line no-console - console.log(evt); //eslint-disable-line no-console + ErrorStore.storeLastError({connErrorCount: this.failCount, message: 'We cannot reach the Mattermost service. The service may be down or misconfigured. Please contact an administrator to make sure the WebSocket port is configured properly.'}); + ErrorStore.emitChange(); }; - conn.onmessage = function connMessage(evt) { + conn.onmessage = (evt) => { AppDispatcher.handleServerAction({ type: ActionTypes.RECIEVED_MSG, msg: JSON.parse(evt.data) @@ -86,7 +104,7 @@ class SocketStoreClass extends EventEmitter { var SocketStore = new SocketStoreClass(); -SocketStore.dispatchToken = AppDispatcher.register(function registry(payload) { +SocketStore.dispatchToken = AppDispatcher.register((payload) => { var action = payload.action; switch (action.type) { diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx index 4effa7307..715e26197 100644 --- a/web/react/utils/client.jsx +++ b/web/react/utils/client.jsx @@ -27,7 +27,7 @@ function handleError(methodName, xhr, status, err) { msg = 'error in ' + methodName + ' status=' + status + ' statusCode=' + xhr.status + ' err=' + err; if (xhr.status === 0) { - e = {message: 'There appears to be a problem with your internet connection'}; + e = {message: 'There appears to be a problem with your internet connection', connErrorCount: 1}; } else { e = {message: 'We received an unexpected status code from the server (' + xhr.status + ')'}; } |