diff options
Diffstat (limited to 'web/react')
28 files changed, 1496 insertions, 995 deletions
diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx index 1c5a1ed5e..fef60c715 100644 --- a/web/react/components/edit_post_modal.jsx +++ b/web/react/components/edit_post_modal.jsx @@ -3,13 +3,21 @@ var Client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); -var Constants = require('../utils/constants.jsx'); -var utils = require('../utils/utils.jsx'); var Textbox = require('./textbox.jsx'); var BrowserStore = require('../stores/browser_store.jsx'); -module.exports = React.createClass({ - handleEdit: function(e) { +export default class EditPostModal extends React.Component { + constructor() { + super(); + + this.handleEdit = this.handleEdit.bind(this); + this.handleEditInput = this.handleEditInput.bind(this); + this.handleEditKeyPress = this.handleEditKeyPress.bind(this); + this.handleUserInput = this.handleUserInput.bind(this); + + this.state = {editText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: ''}; + } + handleEdit() { var updatedPost = {}; updatedPost.message = this.state.editText.trim(); @@ -17,8 +25,8 @@ module.exports = React.createClass({ var tempState = this.state; delete tempState.editText; BrowserStore.setItem('edit_state_transfer', tempState); - $("#edit_post").modal('hide'); - $("#delete_post").modal('show'); + $('#edit_post').modal('hide'); + $('#delete_post').modal('show'); return; } @@ -26,79 +34,102 @@ module.exports = React.createClass({ updatedPost.channel_id = this.state.channel_id; Client.updatePost(updatedPost, - function(data) { + function success() { AsyncClient.getPosts(this.state.channel_id); window.scrollTo(0, 0); }.bind(this), - function(err) { - AsyncClient.dispatchError(err, "updatePost"); - }.bind(this) + function error(err) { + AsyncClient.dispatchError(err, 'updatePost'); + } ); - $("#edit_post").modal('hide'); + $('#edit_post').modal('hide'); $(this.state.refocusId).focus(); - }, - handleEditInput: function(editMessage) { + } + handleEditInput(editMessage) { this.setState({editText: editMessage}); - }, - handleEditKeyPress: function(e) { - if (e.which == 13 && !e.shiftKey && !e.altKey) { + } + handleEditKeyPress(e) { + if (e.which === 13 && !e.shiftKey && !e.altKey) { e.preventDefault(); - this.refs.editbox.getDOMNode().blur(); + React.findDOMNode(this.refs.editbox).blur(); this.handleEdit(e); } - }, - handleUserInput: function(e) { - this.setState({ editText: e.target.value }); - }, - componentDidMount: function() { + } + handleUserInput(e) { + this.setState({editText: e.target.value}); + } + componentDidMount() { var self = this; - $(this.refs.modal.getDOMNode()).on('hidden.bs.modal', function(e) { - self.setState({editText: "", title: "", channel_id: "", post_id: "", comments: 0, refocusId: "", error: ''}); + $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function onHidden() { + self.setState({editText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: ''}); }); - $(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) { + $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function onShow(e) { var button = e.relatedTarget; - self.setState({ editText: $(button).attr('data-message'), title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments'), refocusId: $(button).attr('data-refoucsid') }); + self.setState({editText: $(button).attr('data-message'), title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments'), refocusId: $(button).attr('data-refoucsid')}); }); - $(this.refs.modal.getDOMNode()).on('shown.bs.modal', function(e) { + $(React.findDOMNode(this.refs.modal)).on('shown.bs.modal', function onShown() { self.refs.editbox.resize(); }); - }, - getInitialState: function() { - return { editText: "", title: "", post_id: "", channel_id: "", comments: 0, refocusId: "" }; - }, - render: function() { - var error = this.state.error ? <div className='form-group has-error'><br /><label className='control-label'>{ this.state.error }</label></div> : <div className='form-group'><br /></div>; + } + render() { + var error = (<div className='form-group'><br /></div>); + if (this.state.error) { + error = (<div className='form-group has-error'><br /><label className='control-label'>{this.state.error}</label></div>); + } return ( - <div className="modal fade edit-modal" ref="modal" id="edit_post" role="dialog" tabIndex="-1" aria-hidden="true"> - <div className="modal-dialog modal-push-down"> - <div className="modal-content"> - <div className="modal-header"> - <button type="button" className="close" data-dismiss="modal" aria-label="Close" onClick={this.handleEditClose}><span aria-hidden="true">×</span></button> - <h4 className="modal-title">Edit {this.state.title}</h4> - </div> - <div className="edit-modal-body modal-body"> - <Textbox - onUserInput={this.handleEditInput} - onKeyPress={this.handleEditKeyPress} - messageText={this.state.editText} - createMessage="Edit the post..." - id="edit_textbox" - ref="editbox" - /> - { error } - </div> - <div className="modal-footer"> - <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button> - <button type="button" className="btn btn-primary" onClick={this.handleEdit}>Save</button> - </div> + <div + className='modal fade edit-modal' + ref='modal' + id='edit_post' + role='dialog' + tabIndex='-1' + aria-hidden='true' > + <div className='modal-dialog modal-push-down'> + <div className='modal-content'> + <div className='modal-header'> + <button + type='button' + className='close' + data-dismiss='modal' + aria-label='Close' + onClick={this.handleEditClose}> + <span aria-hidden='true'>×</span> + </button> + <h4 className='modal-title'>Edit {this.state.title}</h4> + </div> + <div className='edit-modal-body modal-body'> + <Textbox + onUserInput={this.handleEditInput} + onKeyPress={this.handleEditKeyPress} + messageText={this.state.editText} + createMessage='Edit the post...' + id='edit_textbox' + ref='editbox' + /> + {error} + </div> + <div className='modal-footer'> + <button + type='button' + className='btn btn-default' + data-dismiss='modal' > + Cancel + </button> + <button + type='button' + className='btn btn-primary' + onClick={this.handleEdit}> + Save + </button> + </div> + </div> </div> - </div> </div> ); } -}); +} diff --git a/web/react/components/error_bar.jsx b/web/react/components/error_bar.jsx index f7514a009..95f3e572c 100644 --- a/web/react/components/error_bar.jsx +++ b/web/react/components/error_bar.jsx @@ -7,32 +7,40 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; -function getStateFromStores() { - var error = ErrorStore.getLastError(); - if (error && error.message !== "There appears to be a problem with your internet connection") { - return { message: error.message }; - } else { - return { message: null }; - } -} +export default class ErrorBar extends React.Component { + constructor() { + super(); -module.exports = React.createClass({ - displayName: 'ErrorBar', + this.onErrorChange = this.onErrorChange.bind(this); + this.handleClose = this.handleClose.bind(this); - componentDidMount: function() { - ErrorStore.addChangeListener(this._onChange); + this.state = this.getStateFromStores(); + if (this.state.message) { + 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() { + $(window).resize(function onResize() { if (this.state.message) { $('body').css('padding-top', $(React.findDOMNode(this)).outerHeight()); } }.bind(this)); - }, - componentWillUnmount: function() { - ErrorStore.removeChangeListener(this._onChange); - }, - _onChange: function() { - var newState = getStateFromStores(); + } + componentWillUnmount() { + ErrorStore.removeChangeListener(this.onErrorChange); + } + onErrorChange() { + var newState = this.getStateFromStores(); if (!utils.areStatesEqual(newState, this.state)) { if (newState.message) { setTimeout(this.handleClose, 10000); @@ -40,9 +48,11 @@ module.exports = React.createClass({ this.setState(newState); } - }, - handleClose: function(e) { - if (e) e.preventDefault(); + } + handleClose(e) { + if (e) { + e.preventDefault(); + } AppDispatcher.handleServerAction({ type: ActionTypes.RECIEVED_ERROR, @@ -50,24 +60,22 @@ module.exports = React.createClass({ }); $('body').css('padding-top', '0'); - }, - getInitialState: function() { - var state = getStateFromStores(); - if (state.message) { - setTimeout(this.handleClose, 10000); - } - return state; - }, - render: function() { + } + render() { if (this.state.message) { return ( - <div className="error-bar"> + <div className='error-bar'> <span>{this.state.message}</span> - <a href="#" className="error-bar__close" onClick={this.handleClose}>×</a> + <a + href='#' + className='error-bar__close' + onClick={this.handleClose}> + × + </a> </div> ); - } else { - return <div/>; } + + return <div/>; } -});
\ No newline at end of file +} diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index 45e6c5e28..d07afbc2b 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -5,31 +5,24 @@ var utils = require('../utils/utils.jsx'); var Client = require('../utils/client.jsx'); var Constants = require('../utils/constants.jsx'); -module.exports = React.createClass({ - displayName: "FileAttachment", - canSetState: false, - propTypes: { - // a list of file pathes displayed by the parent FileAttachmentList - filename: React.PropTypes.string.isRequired, - // the index of this attachment preview in the parent FileAttachmentList - index: React.PropTypes.number.isRequired, - // the identifier of the modal dialog used to preview files - modalId: React.PropTypes.string.isRequired, - // handler for when the thumbnail is clicked - handleImageClick: React.PropTypes.func - }, - getInitialState: function() { - return {fileSize: -1}; - }, - componentDidMount: function() { +export default class FileAttachment extends React.Component { + constructor(props) { + super(props); + + this.loadFiles = this.loadFiles.bind(this); + + this.canSetState = false; + this.state = {fileSize: -1}; + } + componentDidMount() { this.loadFiles(); - }, - componentDidUpdate: function(prevProps) { + } + componentDidUpdate(prevProps) { if (this.props.filename !== prevProps.filename) { this.loadFiles(); } - }, - loadFiles: function() { + } + loadFiles() { this.canSetState = true; var filename = this.props.filename; @@ -39,91 +32,92 @@ module.exports = React.createClass({ var type = utils.getFileType(fileInfo.ext); // This is a temporary patch to fix issue with old files using absolute paths - if (fileInfo.path.indexOf("/api/v1/files/get") != -1) { - fileInfo.path = fileInfo.path.split("/api/v1/files/get")[1]; + if (fileInfo.path.indexOf('/api/v1/files/get') !== -1) { + fileInfo.path = fileInfo.path.split('/api/v1/files/get')[1]; } - fileInfo.path = utils.getWindowLocationOrigin() + "/api/v1/files/get" + fileInfo.path; - - if (type === "image") { - var self = this; - $('<img/>').attr('src', fileInfo.path+'_thumb.jpg').load(function(path, name){ return function() { - $(this).remove(); - if (name in self.refs) { - var imgDiv = self.refs[name].getDOMNode(); - - $(imgDiv).removeClass('post__load'); - $(imgDiv).addClass('post__image'); - - var width = this.width || $(this).width(); - var height = this.height || $(this).height(); - - if (width < Constants.THUMBNAIL_WIDTH - && height < Constants.THUMBNAIL_HEIGHT) { - $(imgDiv).addClass('small'); - } else { - $(imgDiv).addClass('normal'); + fileInfo.path = utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path; + + if (type === 'image') { + var self = this; // Need this reference since we use the given "this" + $('<img/>').attr('src', fileInfo.path + '_thumb.jpg').load(function loadWrapper(path, name) { + return function loader() { + $(this).remove(); + if (name in self.refs) { + var imgDiv = React.findDOMNode(self.refs[name]); + + $(imgDiv).removeClass('post__load'); + $(imgDiv).addClass('post__image'); + + var width = this.width || $(this).width(); + var height = this.height || $(this).height(); + + if (width < Constants.THUMBNAIL_WIDTH && + height < Constants.THUMBNAIL_HEIGHT) { + $(imgDiv).addClass('small'); + } else { + $(imgDiv).addClass('normal'); + } + + var re1 = new RegExp(' ', 'g'); + var re2 = new RegExp('\\(', 'g'); + var re3 = new RegExp('\\)', 'g'); + var url = path.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29'); + $(imgDiv).css('background-image', 'url(' + url + '_thumb.jpg)'); } - - var re1 = new RegExp(' ', 'g'); - var re2 = new RegExp('\\(', 'g'); - var re3 = new RegExp('\\)', 'g'); - var url = path.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29'); - $(imgDiv).css('background-image', 'url('+url+'_thumb.jpg)'); - } - }}(fileInfo.path, filename)); + }; }(fileInfo.path, filename)); } } - }, - componentWillUnmount: function() { + } + componentWillUnmount() { // keep track of when this component is mounted so that we can asynchronously change state without worrying about whether or not we're mounted this.canSetState = false; - }, - shouldComponentUpdate: function(nextProps, nextState) { + } + shouldComponentUpdate(nextProps, nextState) { if (!utils.areStatesEqual(nextProps, this.props)) { return true; } // the only time this object should update is when it receives an updated file size which we can usually handle without re-rendering - if (nextState.fileSize != this.state.fileSize) { + if (nextState.fileSize !== this.state.fileSize) { if (this.refs.fileSize) { // update the UI element to display the file size without re-rendering the whole component - this.refs.fileSize.getDOMNode().innerHTML = utils.fileSizeToString(nextState.fileSize); + React.findDOMNode(this.refs.fileSize).innerHTML = utils.fileSizeToString(nextState.fileSize); return false; - } else { - // we can't find the element that should hold the file size so we must not have rendered yet - return true; } - } else { + + // we can't find the element that should hold the file size so we must not have rendered yet return true; } - }, - render: function() { + + return true; + } + render() { var filename = this.props.filename; var fileInfo = utils.splitFileLocation(filename); var type = utils.getFileType(fileInfo.ext); var thumbnail; - if (type === "image") { - thumbnail = <div ref={filename} className="post__load" style={{backgroundImage: 'url(/static/images/load.gif)'}}/>; + if (type === 'image') { + thumbnail = (<div + ref={filename} + className='post__load' + style={{backgroundImage: 'url(/static/images/load.gif)'}} />); } else { - thumbnail = <div className={"file-icon "+utils.getIconClassName(type)}/>; + thumbnail = <div className={'file-icon ' + utils.getIconClassName(type)}/>; } - var fileSizeString = ""; + var fileSizeString = ''; if (this.state.fileSize < 0) { - var self = this; - Client.getFileInfo( filename, - function(data) { - if (self.canSetState) { - self.setState({fileSize: parseInt(data["size"], 10)}); + function success(data) { + if (this.canSetState) { + this.setState({fileSize: parseInt(data.size, 10)}); } - }, - function(err) { - } + }.bind(this), + function error() {} ); } else { fileSizeString = utils.fileSizeToString(this.state.fileSize); @@ -132,25 +126,51 @@ module.exports = React.createClass({ var filenameString = decodeURIComponent(utils.getFileName(filename)); var trimmedFilename; if (filenameString.length > 35) { - trimmedFilename = filenameString.substring(0, Math.min(35, filenameString.length)) + "..."; + trimmedFilename = filenameString.substring(0, Math.min(35, filenameString.length)) + '...'; } else { trimmedFilename = filenameString; } return ( - <div className="post-image__column" key={filename}> - <a className="post-image__thumbnail" href="#" onClick={this.props.handleImageClick} - data-img-id={this.props.index} data-toggle="modal" data-target={"#" + this.props.modalId }> + <div + className='post-image__column' + key={filename}> + <a className='post-image__thumbnail' + href='#' + onClick={this.props.handleImageClick} + data-img-id={this.props.index} + data-toggle='modal' + data-target={'#' + this.props.modalId} > {thumbnail} </a> - <div className="post-image__details"> - <div data-toggle="tooltip" title={filenameString} className="post-image__name">{trimmedFilename}</div> + <div className='post-image__details'> + <div + data-toggle='tooltip' + title={filenameString} + className='post-image__name' > + {trimmedFilename} + </div> <div> - <span className="post-image__type">{fileInfo.ext.toUpperCase()}</span> - <span className="post-image__size">{fileSizeString}</span> + <span className='post-image__type'>{fileInfo.ext.toUpperCase()}</span> + <span className='post-image__size'>{fileSizeString}</span> </div> </div> </div> ); } -}); +} + +FileAttachment.propTypes = { + + // a list of file pathes displayed by the parent FileAttachmentList + filename: React.PropTypes.string.isRequired, + + // the index of this attachment preview in the parent FileAttachmentList + index: React.PropTypes.number.isRequired, + + // the identifier of the modal dialog used to preview files + modalId: React.PropTypes.string.isRequired, + + // handler for when the thumbnail is clicked + handleImageClick: React.PropTypes.func +}; diff --git a/web/react/components/file_attachment_list.jsx b/web/react/components/file_attachment_list.jsx index df4424d03..33643de73 100644 --- a/web/react/components/file_attachment_list.jsx +++ b/web/react/components/file_attachment_list.jsx @@ -5,33 +5,30 @@ var ViewImageModal = require('./view_image.jsx'); var FileAttachment = require('./file_attachment.jsx'); var Constants = require('../utils/constants.jsx'); -module.exports = React.createClass({ - displayName: "FileAttachmentList", - propTypes: { - // a list of file pathes displayed by this - filenames: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, - // the identifier of the modal dialog used to preview files - modalId: React.PropTypes.string.isRequired, - // the channel that this is part of - channelId: React.PropTypes.string, - // the user that owns the post that this is attached to - userId: React.PropTypes.string - }, - getInitialState: function() { - return {startImgId: 0}; - }, - render: function() { +export default class FileAttachmentList extends React.Component { + constructor(props) { + super(props); + this.state = {startImgId: 0}; + } + render() { var filenames = this.props.filenames; var modalId = this.props.modalId; var postFiles = []; for (var i = 0; i < filenames.length && i < Constants.MAX_DISPLAY_FILES; i++) { - postFiles.push(<FileAttachment key={i} filename={filenames[i]} index={i} modalId={modalId} handleImageClick={this.handleImageClick} />); + postFiles.push( + <FileAttachment + key={i} + filename={filenames[i]} + index={i} + modalId={modalId} + handleImageClick={this.handleImageClick} /> + ); } return ( <div> - <div className="post-image__columns"> + <div className='post-image__columns'> {postFiles} </div> <ViewImageModal @@ -42,8 +39,23 @@ module.exports = React.createClass({ filenames={filenames} /> </div> ); - }, - handleImageClick: function(e) { - this.setState({startImgId: parseInt($(e.target.parentNode).attr('data-img-id'))}); } -}); + handleImageClick(e) { + this.setState({startImgId: parseInt($(e.target.parentNode).attr('data-img-id'), 10)}); + } +} + +FileAttachmentList.propTypes = { + + // a list of file pathes displayed by this + filenames: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, + + // the identifier of the modal dialog used to preview files + modalId: React.PropTypes.string.isRequired, + + // the channel that this is part of + channelId: React.PropTypes.string, + + // the user that owns the post that this is attached to + userId: React.PropTypes.string +}; diff --git a/web/react/components/member_list_item.jsx b/web/react/components/member_list_item.jsx index a5279909b..d244939f5 100644 --- a/web/react/components/member_list_item.jsx +++ b/web/react/components/member_list_item.jsx @@ -1,64 +1,116 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. -var ChannelStore = require('../stores/channel_store.jsx'); var UserStore = require('../stores/user_store.jsx'); -module.exports = React.createClass({ - displayName: 'MemberListItem', - handleInvite: function(e) { +export default class MemberListItem extends React.Component { + constructor(props) { + super(props); + + this.handleInvite = this.handleInvite.bind(this); + this.handleRemove = this.handleRemove.bind(this); + this.handleMakeAdmin = this.handleMakeAdmin.bind(this); + } + handleInvite(e) { e.preventDefault(); this.props.handleInvite(this.props.member.id); - }, - handleRemove: function(e) { + } + handleRemove(e) { e.preventDefault(); this.props.handleRemove(this.props.member.id); - }, - handleMakeAdmin: function(e) { + } + handleMakeAdmin(e) { e.preventDefault(); this.props.handleMakeAdmin(this.props.member.id); - }, - render: function() { - + } + render() { var member = this.props.member; var isAdmin = this.props.isAdmin; - var isMemberAdmin = member.roles.indexOf("admin") > -1; + var isMemberAdmin = member.roles.indexOf('admin') > -1; var timestamp = UserStore.getCurrentUser().update_at; var invite; if (member.invited && this.props.handleInvite) { - invite = <span className="member-role">Added</span>; + invite = <span className='member-role'>Added</span>; } else if (this.props.handleInvite) { - invite = <a onClick={this.handleInvite} className="btn btn-sm btn-primary member-invite"><i className="glyphicon glyphicon-envelope"/> Add</a>; - } else if (isAdmin && !isMemberAdmin && (member.id != UserStore.getCurrentId())) { + invite = ( + <a + onClick={this.handleInvite} + className='btn btn-sm btn-primary member-invite'> + <i className='glyphicon glyphicon-envelope'/> Add + </a> + ); + } else if (isAdmin && !isMemberAdmin && (member.id !== UserStore.getCurrentId())) { var self = this; + + let makeAdminOption = null; + if (makeAdminOption) { + makeAdminOption = ( + <li role='presentation'> + <a + href='' + role='menuitem' + onClick={self.handleMakeAdmin}>Make Admin + </a> + </li>); + } + + let handleRemoveOption = null; + if (handleRemoveOption) { + handleRemoveOption = ( + <li role='presentation'> + <a + href='' + role='menuitem' + onClick={self.handleRemove}>Remove Member + </a> + </li>); + } + invite = ( - <div className="dropdown member-drop"> - <a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true"> - <span className="text-capitalize">{member.roles || 'Member'} </span> - <span className="caret"></span> + <div className='dropdown member-drop'> + <a + href='#' + className='dropdown-toggle theme' + type='button' + id='channel_header_dropdown' + data-toggle='dropdown' + aria-expanded='true' > + <span className='text-capitalize'>{member.roles || 'Member'} </span> + <span className='caret'></span> </a> - <ul className="dropdown-menu member-menu" role="menu" aria-labelledby="channel_header_dropdown"> - { this.props.handleMakeAdmin ? - <li role="presentation"><a href="" role="menuitem" onClick={self.handleMakeAdmin}>Make Admin</a></li> - : null } - { this.props.handleRemove ? - <li role="presentation"><a href="" role="menuitem" onClick={self.handleRemove}>Remove Member</a></li> - : null } + <ul + className='dropdown-menu member-menu' + role='menu' + aria-labelledby='channel_header_dropdown'> + {makeAdminOption} + {handleRemoveOption} </ul> </div> ); } else { - invite = <div className="member-role text-capitalize">{member.roles || 'Member'}<span className="caret hidden"></span></div>; + invite = <div className='member-role text-capitalize'>{member.roles || 'Member'}<span className='caret hidden'></span></div>; } return ( - <div className="row member-div"> - <img className="post-profile-img pull-left" src={"/api/v1/users/" + member.id + "/image?time=" + timestamp} height="36" width="36" /> - <span className="member-name">{member.username}</span> - <span className="member-email">{member.email}</span> - { invite } + <div className='row member-div'> + <img + className='post-profile-img pull-left' + src={'/api/v1/users/' + member.id + '/image?time=' + timestamp} + height='36' + width='36' /> + <span className='member-name'>{member.username}</span> + <span className='member-email'>{member.email}</span> + {invite} </div> ); } -}); +} + +MemberListItem.propTypes = { + handleInvite: React.PropTypes.func, + handleRemove: React.PropTypes.func, + handleMakeAdmin: React.PropTypes.func, + member: React.PropTypes.object, + isAdmin: React.PropTypes.bool +}; diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index f562cfb29..afea30161 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -14,54 +14,66 @@ var MAX_HEIGHT_LIST = 292; var MAX_ITEMS_IN_LIST = 25; var ITEM_HEIGHT = 36; -module.exports = React.createClass({ - displayName: 'MentionList', - componentDidMount: function() { +export default class MentionList extends React.Component { + constructor(props) { + super(props); + + this.onListenerChange = this.onListenerChange.bind(this); + this.handleClick = this.handleClick.bind(this); + this.handleMouseEnter = this.handleMouseEnter.bind(this); + this.getSelection = this.getSelection.bind(this); + this.addCurrentMention = this.addCurrentMention.bind(this); + this.addFirstMention = this.addFirstMention.bind(this); + this.isEmpty = this.isEmpty.bind(this); + this.scrollToMention = this.scrollToMention.bind(this); + + this.state = {excludeUsers: [], mentionText: '-1', selectedMention: 0, selectedUsername: ''}; + } + componentDidMount() { PostStore.addMentionDataChangeListener(this.onListenerChange); - var self = this; - $('.post-right__scroll').scroll(function(){ - if($('.mentions--top').length){ - $('#reply_mention_tab .mentions--top').css({ bottom: $(window).height() - $('.post-right__scroll #reply_textbox').offset().top }); + $('.post-right__scroll').scroll(function onScroll() { + if ($('.mentions--top').length) { + $('#reply_mention_tab .mentions--top').css({bottom: $(window).height() - $('.post-right__scroll #reply_textbox').offset().top}); } }); $('body').on('keydown.mentionlist', '#' + this.props.id, - function(e) { - if (!self.isEmpty() && self.state.mentionText !== '-1' && (e.which === 13 || e.which === 9)) { + function onMentionListKey(e) { + if (!this.isEmpty() && this.state.mentionText !== '-1' && (e.which === 13 || e.which === 9)) { e.stopPropagation(); e.preventDefault(); - self.addCurrentMention(); - } else if (!self.isEmpty() && self.state.mentionText !== '-1' && (e.which === 38 || e.which === 40)) { + this.addCurrentMention(); + } else if (!this.isEmpty() && this.state.mentionText !== '-1' && (e.which === 38 || e.which === 40)) { e.stopPropagation(); e.preventDefault(); if (e.which === 38) { - if (self.getSelection(self.state.selectedMention - 1)) { - self.setState({selectedMention: self.state.selectedMention - 1, selectedUsername: self.refs['mention' + (self.state.selectedMention - 1)].props.username}); + if (this.getSelection(this.state.selectedMention - 1)) { + this.setState({selectedMention: this.state.selectedMention - 1, selectedUsername: this.refs['mention' + (this.state.selectedMention - 1)].props.username}); } } else if (e.which === 40) { - if (self.getSelection(self.state.selectedMention + 1)) { - self.setState({selectedMention: self.state.selectedMention + 1, selectedUsername: self.refs['mention' + (self.state.selectedMention + 1)].props.username}); + if (this.getSelection(this.state.selectedMention + 1)) { + this.setState({selectedMention: this.state.selectedMention + 1, selectedUsername: this.refs['mention' + (this.state.selectedMention + 1)].props.username}); } } - self.scrollToMention(e.which); + this.scrollToMention(e.which); } - } + }.bind(this) ); - $(document).click(function(e) { - if (!($('#' + self.props.id).is(e.target) || $('#' + self.props.id).has(e.target).length || - ('mentionlist' in self.refs && $(self.refs.mentionlist.getDOMNode()).has(e.target).length))) { - self.setState({mentionText: '-1'}); + $(document).click(function onClick(e) { + if (!($('#' + this.props.id).is(e.target) || $('#' + this.props.id).has(e.target).length || + ('mentionlist' in this.refs && $(React.findDOMNode(this.refs.mentionlist)).has(e.target).length))) { + this.setState({mentionText: '-1'}); } - }); - }, - componentWillUnmount: function() { + }.bind(this)); + } + componentWillUnmount() { PostStore.removeMentionDataChangeListener(this.onListenerChange); $('body').off('keydown.mentionlist', '#' + this.props.id); - }, - componentDidUpdate: function() { + } + componentDidUpdate() { if (this.state.mentionText !== '-1') { if (this.state.selectedUsername !== '' && (!this.getSelection(this.state.selectedMention) || this.state.selectedUsername !== this.refs['mention' + this.state.selectedMention].props.username)) { var tempSelectedMention = -1; @@ -80,8 +92,8 @@ module.exports = React.createClass({ } else if (this.state.selectedMention !== 0) { this.setState({selectedMention: 0, selectedUsername: ''}); } - }, - onListenerChange: function(id, mentionText) { + } + onListenerChange(id, mentionText) { if (id !== this.props.id) { return; } @@ -92,8 +104,8 @@ module.exports = React.createClass({ } this.setState(newState); - }, - handleClick: function(name) { + } + handleClick(name) { AppDispatcher.handleViewAction({ type: ActionTypes.RECIEVED_ADD_MENTION, id: this.props.id, @@ -101,33 +113,33 @@ module.exports = React.createClass({ }); this.setState({mentionText: '-1'}); - }, - handleMouseEnter: function(listId) { + } + handleMouseEnter(listId) { this.setState({selectedMention: listId, selectedUsername: this.refs['mention' + listId].props.username}); - }, - getSelection: function(listId) { + } + getSelection(listId) { if (!this.refs['mention' + listId]) { return false; } return true; - }, - addCurrentMention: function() { + } + addCurrentMention() { if (!this.getSelection(this.state.selectedMention)) { this.addFirstMention(); } else { this.refs['mention' + this.state.selectedMention].handleClick(); } - }, - addFirstMention: function() { + } + addFirstMention() { if (!this.refs.mention0) { return; } this.refs.mention0.handleClick(); - }, - isEmpty: function() { + } + isEmpty() { return (!this.refs.mention0); - }, - scrollToMention: function(keyPressed) { + } + scrollToMention(keyPressed) { var direction; if (keyPressed === 38) { direction = 'up'; @@ -145,12 +157,8 @@ module.exports = React.createClass({ $('#mentionsbox').animate({ scrollTop: scrollAmount }, 75); - }, - getInitialState: function() { - return {excludeUsers: [], mentionText: '-1', selectedMention: 0, selectedUsername: ''}; - }, - render: function() { - var self = this; + } + render() { var mentionText = this.state.mentionText; if (mentionText === '-1') { return null; @@ -158,8 +166,10 @@ module.exports = React.createClass({ var profiles = UserStore.getActiveOnlyProfiles(); var users = []; - for (var id in profiles) { - users.push(profiles[id]); + for (let id in profiles) { + if (profiles[id]) { + users.push(profiles[id]); + } } var all = {}; @@ -176,7 +186,7 @@ module.exports = React.createClass({ channel.id = 'channelmention'; users.push(channel); - users.sort(function(a, b) { + users.sort(function sortByUsername(a, b) { if (a.username < b.username) { return -1; } @@ -185,29 +195,34 @@ module.exports = React.createClass({ } return 0; }); - var mentions = {}; + var mentions = []; var index = 0; for (var i = 0; i < users.length && index < MAX_ITEMS_IN_LIST; i++) { if ((users[i].first_name && users[i].first_name.lastIndexOf(mentionText, 0) === 0) || (users[i].last_name && users[i].last_name.lastIndexOf(mentionText, 0) === 0) || users[i].username.lastIndexOf(mentionText, 0) === 0) { + let isFocused = ''; + if (this.state.selectedMention === index) { + isFocused = 'mentions-focus'; + } mentions[index] = ( <Mention + key={'mention_key_' + index} ref={'mention' + index} username={users[i].username} secondary_text={Utils.getFullName(users[i])} id={users[i].id} listId={index} - isFocused={this.state.selectedMention === index ? 'mentions-focus' : ''} - handleMouseEnter={function(value) { return function() { self.handleMouseEnter(value); } }(index)} + isFocused={isFocused} + handleMouseEnter={this.handleMouseEnter.bind(this, index)} handleClick={this.handleClick} /> ); index++; } } - var numMentions = Object.keys(mentions).length; + var numMentions = mentions.length; if (numMentions < 1) { return null; @@ -223,11 +238,20 @@ module.exports = React.createClass({ }; return ( - <div className='mentions--top' style={style}> - <div ref='mentionlist' className='mentions-box' id='mentionsbox'> + <div + className='mentions--top' + style={style}> + <div + ref='mentionlist' + className='mentions-box' + id='mentionsbox'> {mentions} </div> </div> ); } -}); +} + +MentionList.propTypes = { + id: React.PropTypes.string +}; diff --git a/web/react/components/new_channel.jsx b/web/react/components/new_channel.jsx index fc24a7cdc..a02a4c1c0 100644 --- a/web/react/components/new_channel.jsx +++ b/web/react/components/new_channel.jsx @@ -5,17 +5,24 @@ var utils = require('../utils/utils.jsx'); var client = require('../utils/client.jsx'); var asyncClient = require('../utils/async_client.jsx'); var UserStore = require('../stores/user_store.jsx'); -var TeamStore = require('../stores/team_store.jsx'); -module.exports = React.createClass({ - displayName: 'NewChannelModal', - handleSubmit: function(e) { +export default class NewChannelModal extends React.Component { + constructor() { + super(); + + this.handleSubmit = this.handleSubmit.bind(this); + this.displayNameKeyUp = this.displayNameKeyUp.bind(this); + this.handleClose = this.handleClose.bind(this); + + this.state = {channelType: ''}; + } + handleSubmit(e) { e.preventDefault(); var channel = {}; var state = {serverError: ''}; - channel.display_name = this.refs.display_name.getDOMNode().value.trim(); + channel.display_name = React.findDOMNode(this.refs.display_name).value.trim(); if (!channel.display_name) { state.displayNameError = 'This field is required'; state.inValid = true; @@ -26,7 +33,7 @@ module.exports = React.createClass({ state.displayNameError = ''; } - channel.name = this.refs.channel_name.getDOMNode().value.trim(); + channel.name = React.findDOMNode(this.refs.channel_name).value.trim(); if (!channel.name) { state.nameError = 'This field is required'; state.inValid = true; @@ -52,54 +59,51 @@ module.exports = React.createClass({ var cu = UserStore.getCurrentUser(); channel.team_id = cu.team_id; - channel.description = this.refs.channel_desc.getDOMNode().value.trim(); + channel.description = React.findDOMNode(this.refs.channel_desc).value.trim(); channel.type = this.state.channelType; client.createChannel(channel, - function(data) { - $(this.refs.modal.getDOMNode()).modal('hide'); + function success(data) { + $(React.findDOMNode(this.refs.modal)).modal('hide'); asyncClient.getChannel(data.id); utils.switchChannel(data); - this.refs.display_name.getDOMNode().value = ''; - this.refs.channel_name.getDOMNode().value = ''; - this.refs.channel_desc.getDOMNode().value = ''; + React.findDOMNode(this.refs.display_name).value = ''; + React.findDOMNode(this.refs.channel_name).value = ''; + React.findDOMNode(this.refs.channel_desc).value = ''; }.bind(this), - function(err) { + function error(err) { state.serverError = err.message; state.inValid = true; this.setState(state); }.bind(this) ); - }, - displayNameKeyUp: function() { - var displayName = this.refs.display_name.getDOMNode().value.trim(); + } + displayNameKeyUp() { + var displayName = React.findDOMNode(this.refs.display_name).value.trim(); var channelName = utils.cleanUpUrlable(displayName); - this.refs.channel_name.getDOMNode().value = channelName; - }, - componentDidMount: function() { + React.findDOMNode(this.refs.channel_name).value = channelName; + } + componentDidMount() { var self = this; - $(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) { + $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function onModalShow(e) { var button = e.relatedTarget; self.setState({channelType: $(button).attr('data-channeltype')}); }); - $(this.refs.modal.getDOMNode()).on('hidden.bs.modal', this.handleClose); - }, - componentWillUnmount: function() { - $(this.refs.modal.getDOMNode()).off('hidden.bs.modal', this.handleClose); - }, - handleClose: function() { - $(this.getDOMNode()).find('.form-control').each(function clearForms() { + $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose); + } + componentWillUnmount() { + $(React.findDOMNode(this.refs.modal)).off('hidden.bs.modal', this.handleClose); + } + handleClose() { + $(React.findDOMNode(this)).find('.form-control').each(function clearForms() { this.value = ''; }); this.setState({channelType: '', displayNameError: '', nameError: '', serverError: '', inValid: false}); - }, - getInitialState: function() { - return {channelType: ''}; - }, - render: function() { + } + render() { var displayNameError = null; var nameError = null; var serverError = null; @@ -124,11 +128,20 @@ module.exports = React.createClass({ } return ( - <div className='modal fade' id='new_channel' ref='modal' tabIndex='-1' role='dialog' aria-hidden='true'> + <div + className='modal fade' + id='new_channel' + ref='modal' + tabIndex='-1' + role='dialog' + aria-hidden='true'> <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <button type='button' className='close' data-dismiss='modal'> + <button + type='button' + className='close' + data-dismiss='modal'> <span aria-hidden='true'>×</span> <span className='sr-only'>Cancel</span> </button> @@ -138,23 +151,49 @@ module.exports = React.createClass({ <div className='modal-body'> <div className={displayNameClass}> <label className='control-label'>Display Name</label> - <input onKeyUp={this.displayNameKeyUp} type='text' ref='display_name' className='form-control' placeholder='Enter display name' maxLength='22' /> + <input + onKeyUp={this.displayNameKeyUp} + type='text' + ref='display_name' + className='form-control' + placeholder='Enter display name' + maxLength='22' /> {displayNameError} </div> <div className={nameClass}> <label className='control-label'>Handle</label> - <input type='text' className='form-control' ref='channel_name' placeholder="lowercase alphanumeric's only" maxLength='22' /> + <input + type='text' + className='form-control' + ref='channel_name' + placeholder="lowercase alphanumeric's only" + maxLength='22' /> {nameError} </div> <div className='form-group'> <label className='control-label'>Description</label> - <textarea className='form-control no-resize' ref='channel_desc' rows='3' placeholder='Description' maxLength='1024'></textarea> + <textarea + className='form-control no-resize' + ref='channel_desc' + rows='3' + placeholder='Description' + maxLength='1024' /> </div> {serverError} </div> <div className='modal-footer'> - <button type='button' className='btn btn-default' data-dismiss='modal'>Cancel</button> - <button onClick={this.handleSubmit} type='submit' className='btn btn-primary'>Create New {channelTerm}</button> + <button + type='button' + className='btn btn-default' + data-dismiss='modal' > + Cancel + </button> + <button + onClick={this.handleSubmit} + type='submit' + className='btn btn-primary' > + Create New {channelTerm} + </button> </div> </form> </div> @@ -162,4 +201,4 @@ module.exports = React.createClass({ </div> ); } -}); +} diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx index acc2b51d2..e284f4b6a 100644 --- a/web/react/components/post.jsx +++ b/web/react/components/post.jsx @@ -15,9 +15,17 @@ var utils = require('../utils/utils.jsx'); var PostInfo = require('./post_info.jsx'); -module.exports = React.createClass({ - displayName: 'Post', - handleCommentClick: function(e) { +export default class Post extends React.Component { + constructor(props) { + super(props); + + this.handleCommentClick = this.handleCommentClick.bind(this); + this.forceUpdateInfo = this.forceUpdateInfo.bind(this); + this.retryPost = this.retryPost.bind(this); + + this.state = {}; + } + handleCommentClick(e) { e.preventDefault(); var data = {}; @@ -33,31 +41,31 @@ module.exports = React.createClass({ type: ActionTypes.RECIEVED_SEARCH, results: null }); - }, - forceUpdateInfo: function() { + } + forceUpdateInfo() { this.refs.info.forceUpdate(); this.refs.header.forceUpdate(); - }, - retryPost: function(e) { + } + retryPost(e) { e.preventDefault(); var post = this.props.post; client.createPost(post, post.channel_id, - function(data) { + function success(data) { AsyncClient.getPosts(); var channel = ChannelStore.get(post.channel_id); var member = ChannelStore.getMember(post.channel_id); member.msg_count = channel.total_msg_count; - member.last_viewed_at = (new Date).getTime(); + member.last_viewed_at = utils.getTimestamp(); ChannelStore.setChannelMember(member); AppDispatcher.handleServerAction({ type: ActionTypes.RECIEVED_POST, post: data }); - }.bind(this), - function(err) { + }, + function error() { post.state = Constants.POST_FAILED; PostStore.updatePendingPost(post); this.forceUpdate(); @@ -67,18 +75,15 @@ module.exports = React.createClass({ post.state = Constants.POST_LOADING; PostStore.updatePendingPost(post); this.forceUpdate(); - }, - shouldComponentUpdate: function(nextProps) { + } + shouldComponentUpdate(nextProps) { if (!utils.areStatesEqual(nextProps.post, this.props.post)) { return true; } return false; - }, - getInitialState: function() { - return { }; - }, - render: function() { + } + render() { var post = this.props.post; var parentPost = this.props.parentPost; var posts = this.props.posts; @@ -89,19 +94,27 @@ module.exports = React.createClass({ } var commentCount = 0; - var commentRootId = parentPost ? post.root_id : post.id; + var commentRootId; + if (parentPost) { + commentRootId = post.root_id; + } else { + commentRootId = post.id; + } for (var postId in posts) { - if (posts[postId].root_id == commentRootId) { + if (posts[postId].root_id === commentRootId) { commentCount += 1; } } - var error = this.state.error ? <div className='form-group has-error'><label className='control-label'>{ this.state.error }</label></div> : null; - - var rootUser = this.props.sameRoot ? 'same--root' : 'other--root'; + var rootUser; + if (this.props.sameRoot) { + rootUser = 'same--root'; + } else { + rootUser = 'other--root'; + } var postType = ''; - if (type != 'Post'){ + if (type !== 'Post') { postType = 'post--comment'; } @@ -122,21 +135,60 @@ module.exports = React.createClass({ sameUserClass = 'same--user'; } + var profilePic = null; + if (this.props.hideProfilePic) { + profilePic = ( + <div className='post-profile-img__container'> + <img + className='post-profile-img' + src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp} + height='36' + width='36' /> + </div> + ); + } + return ( <div> - <div id={post.id} className={'post ' + sameUserClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss}> - { !this.props.hideProfilePic ? - <div className='post-profile-img__container'> - <img className='post-profile-img' src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp} height='36' width='36' /> - </div> - : null } + <div + id={post.id} + className={'post ' + sameUserClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss} > + {profilePic} <div className='post__content'> - <PostHeader ref='header' post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} isLastComment={this.props.isLastComment} /> - <PostBody post={post} sameRoot={this.props.sameRoot} parentPost={parentPost} posts={posts} handleCommentClick={this.handleCommentClick} retryPost={this.retryPost} /> - <PostInfo ref='info' post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} allowReply='true' /> + <PostHeader + ref='header' + post={post} + sameRoot={this.props.sameRoot} + commentCount={commentCount} + handleCommentClick={this.handleCommentClick} + isLastComment={this.props.isLastComment} /> + <PostBody + post={post} + sameRoot={this.props.sameRoot} + parentPost={parentPost} + posts={posts} + handleCommentClick={this.handleCommentClick} + retryPost={this.retryPost} /> + <PostInfo + ref='info' + post={post} + sameRoot={this.props.sameRoot} + commentCount={commentCount} + handleCommentClick={this.handleCommentClick} + allowReply='true' /> </div> </div> </div> ); } -}); +} + +Post.propTypes = { + post: React.PropTypes.object, + posts: React.PropTypes.object, + parentPost: React.PropTypes.object, + sameUser: React.PropTypes.bool, + sameRoot: React.PropTypes.bool, + hideProfilePic: React.PropTypes.bool, + isLastComment: React.PropTypes.bool +}; diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx index b11b39e9e..8da8231a2 100644 --- a/web/react/components/search_bar.jsx +++ b/web/react/components/search_bar.jsx @@ -1,7 +1,6 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. - var client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var PostStore = require('../stores/post_store.jsx'); @@ -10,36 +9,47 @@ var utils = require('../utils/utils.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; -function getSearchTermStateFromStores() { - var term = PostStore.getSearchTerm() || ''; - return { - search_term: term - }; -} +export default class SearchBar extends React.Component { + constructor() { + super(); + this.mounted = false; -module.exports = React.createClass({ - displayName: 'SearchBar', - componentDidMount: function() { - PostStore.addSearchTermChangeListener(this._onChange); - }, - componentWillUnmount: function() { - PostStore.removeSearchTermChangeListener(this._onChange); - }, - _onChange: function(doSearch, isMentionSearch) { - if (this.isMounted()) { - var newState = getSearchTermStateFromStores(); + this.onListenerChange = this.onListenerChange.bind(this); + this.handleUserInput = this.handleUserInput.bind(this); + this.performSearch = this.performSearch.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + + this.state = this.getSearchTermStateFromStores(); + } + getSearchTermStateFromStores() { + var term = PostStore.getSearchTerm() || ''; + return { + searchTerm: term + }; + } + componentDidMount() { + PostStore.addSearchTermChangeListener(this.onListenerChange); + this.mounted = true; + } + componentWillUnmount() { + PostStore.removeSearchTermChangeListener(this.onListenerChange); + this.mounted = false; + } + onListenerChange(doSearch, isMentionSearch) { + if (this.mounted) { + var newState = this.getSearchTermStateFromStores(); if (!utils.areStatesEqual(newState, this.state)) { this.setState(newState); } if (doSearch) { - this.performSearch(newState.search_term, isMentionSearch); + this.performSearch(newState.searchTerm, isMentionSearch); } } - }, - clearFocus: function(e) { + } + clearFocus() { $('.search-bar__container').removeClass('focused'); - }, - handleClose: function(e) { + } + handleClose(e) { e.preventDefault(); AppDispatcher.handleServerAction({ @@ -58,23 +68,23 @@ module.exports = React.createClass({ type: ActionTypes.RECIEVED_POST_SELECTED, results: null }); - }, - handleUserInput: function(e) { + } + handleUserInput(e) { var term = e.target.value; PostStore.storeSearchTerm(term); PostStore.emitSearchTermChange(false); - this.setState({ search_term: term }); - }, - handleUserFocus: function(e) { + this.setState({searchTerm: term}); + } + handleUserFocus(e) { e.target.select(); $('.search-bar__container').addClass('focused'); - }, - performSearch: function(terms, isMentionSearch) { + } + performSearch(terms, isMentionSearch) { if (terms.length) { this.setState({isSearching: true}); client.search( terms, - function(data) { + function success(data) { this.setState({isSearching: false}); if (utils.isMobile()) { React.findDOMNode(this.refs.search).value = ''; @@ -86,38 +96,50 @@ module.exports = React.createClass({ is_mention_search: isMentionSearch }); }.bind(this), - function(err) { + function error(err) { this.setState({isSearching: false}); - AsyncClient.dispatchError(err, "search"); + AsyncClient.dispatchError(err, 'search'); }.bind(this) ); } - }, - handleSubmit: function(e) { + } + handleSubmit(e) { e.preventDefault(); - this.performSearch(this.state.search_term.trim()); - }, - getInitialState: function() { - return getSearchTermStateFromStores(); - }, - render: function() { + this.performSearch(this.state.searchTerm.trim()); + } + render() { + var isSearching = null; + if (this.state.isSearching) { + isSearching = <span className={'glyphicon glyphicon-refresh glyphicon-refresh-animate'}></span>; + } return ( <div> - <div className="sidebar__collapse" onClick={this.handleClose}><span className="fa fa-angle-left"></span></div> - <span onClick={this.clearFocus} className="search__clear">Cancel</span> - <form role="form" className="search__form relative-div" onSubmit={this.handleSubmit}> - <span className="glyphicon glyphicon-search sidebar__search-icon"></span> + <div + className='sidebar__collapse' + onClick={this.handleClose} > + <span className='fa fa-angle-left'></span> + </div> + <span + className='search__clear' + onClick={this.clearFocus}> + Cancel + </span> + <form + role='form' + className='search__form relative-div' + onSubmit={this.handleSubmit}> + <span className='glyphicon glyphicon-search sidebar__search-icon'></span> <input - type="text" - ref="search" - className="form-control search-bar" - placeholder="Search" - value={this.state.search_term} + type='text' + ref='search' + className='form-control search-bar' + placeholder='Search' + value={this.state.searchTerm} onFocus={this.handleUserFocus} onChange={this.handleUserInput} /> - {this.state.isSearching ? <span className={"glyphicon glyphicon-refresh glyphicon-refresh-animate"}></span> : null} + {isSearching} </form> </div> ); } -}); +} diff --git a/web/react/components/setting_item_max.jsx b/web/react/components/setting_item_max.jsx index b978cdb0c..e67e458af 100644 --- a/web/react/components/setting_item_max.jsx +++ b/web/react/components/setting_item_max.jsx @@ -1,33 +1,68 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. -module.exports = React.createClass({ - render: function() { - var clientError = this.props.client_error ? <div className='form-group'><label className='col-sm-12 has-error'>{ this.props.client_error }</label></div> : null; - var server_error = this.props.server_error ? <div className='form-group'><label className='col-sm-12 has-error'>{ this.props.server_error }</label></div> : null; - var extraInfo = this.props.extraInfo ? this.props.extraInfo : null; +export default class SettingItemMax extends React.Component { + render() { + var clientError = null; + if (this.props.client_error) { + clientError = (<div className='form-group'><label className='col-sm-12 has-error'>{this.props.client_error}</label></div>); + } + + var serverError = null; + if (this.props.server_error) { + serverError = (<div className='form-group'><label className='col-sm-12 has-error'>{this.props.server_error}</label></div>); + } + + var extraInfo = null; + if (this.props.extraInfo) { + extraInfo = this.props.extraInfo; + } + + var submit = ''; + if (this.props.submit) { + submit = (<a + className='btn btn-sm btn-primary' + href='#' + onClick={this.props.submit}> + Submit</a>); + } var inputs = this.props.inputs; return ( - <ul className="section-max form-horizontal"> - <li className="col-sm-12 section-title">{this.props.title}</li> - <li className="col-sm-9 col-sm-offset-3"> - <ul className="setting-list"> - <li className="setting-list-item"> + <ul className='section-max form-horizontal'> + <li className='col-sm-12 section-title'>{this.props.title}</li> + <li className='col-sm-9 col-sm-offset-3'> + <ul className='setting-list'> + <li className='setting-list-item'> {inputs} {extraInfo} </li> - <li className="setting-list-item"> + <li className='setting-list-item'> <hr /> - { server_error } - { clientError } - { this.props.submit ? <a className="btn btn-sm btn-primary" href="#" onClick={this.props.submit}>Submit</a> : "" } - <a className="btn btn-sm theme" href="#" onClick={this.props.updateSection}>Cancel</a> + {serverError} + {clientError} + {submit} + <a + className='btn btn-sm theme' + href='#' + onClick={this.props.updateSection} > + Cancel + </a> </li> </ul> </li> </ul> ); } -}); +} + +SettingItemMax.propTypes = { + inputs: React.PropTypes.array, + client_error: React.PropTypes.string, + server_error: React.PropTypes.string, + extraInfo: React.PropTypes.element, + updateSection: React.PropTypes.func, + submit: React.PropTypes.func, + title: React.PropTypes.string +}; diff --git a/web/react/components/team_signup_display_name_page.jsx b/web/react/components/team_signup_display_name_page.jsx index b5e93de1b..de756f4d5 100644 --- a/web/react/components/team_signup_display_name_page.jsx +++ b/web/react/components/team_signup_display_name_page.jsx @@ -4,21 +4,24 @@ var utils = require('../utils/utils.jsx'); var client = require('../utils/client.jsx'); -module.exports = React.createClass({ - displayName: 'TeamSignupDisplayNamePage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { +export default class TeamSignupDisplayNamePage extends React.Component { + constructor(props) { + super(props); + + this.submitBack = this.submitBack.bind(this); + this.submitNext = this.submitNext.bind(this); + + this.state = {}; + } + submitBack(e) { e.preventDefault(); this.props.state.wizard = 'welcome'; this.props.updateParent(this.props.state); - }, - submitNext: function(e) { + } + submitNext(e) { e.preventDefault(); - var displayName = this.refs.name.getDOMNode().value.trim(); + var displayName = React.findDOMNode(this.refs.name).value.trim(); if (!displayName) { this.setState({nameError: 'This field is required'}); return; @@ -28,15 +31,12 @@ module.exports = React.createClass({ this.props.state.team.display_name = displayName; this.props.state.team.name = utils.cleanUpUrlable(displayName); this.props.updateParent(this.props.state); - }, - getInitialState: function() { - return {}; - }, - handleFocus: function(e) { + } + handleFocus(e) { e.preventDefault(); e.currentTarget.select(); - }, - render: function() { + } + render() { client.track('signup', 'signup_team_02_name'); var nameError = null; @@ -49,24 +49,48 @@ module.exports = React.createClass({ return ( <div> <form> - <img className='signup-team-logo' src='/static/images/logo.png' /> + <img + className='signup-team-logo' + src='/static/images/logo.png' /> <h2>{utils.toTitleCase(strings.Team) + ' Name'}</h2> <div className={nameDivClass}> <div className='row'> <div className='col-sm-9'> - <input type='text' ref='name' className='form-control' placeholder='' maxLength='128' defaultValue={this.props.state.team.display_name} autoFocus={true} onFocus={this.handleFocus} /> + <input + type='text' + ref='name' + className='form-control' + placeholder='' + maxLength='128' + defaultValue={this.props.state.team.display_name} + autoFocus={true} + onFocus={this.handleFocus} /> </div> </div> {nameError} </div> <div>{'Name your ' + strings.Team + ' in any language. Your ' + strings.Team + ' name shows in menus and headings.'}</div> - <button type='submit' className='btn btn-primary margin--extra' onClick={this.submitNext}>Next<i className='glyphicon glyphicon-chevron-right'></i></button> + <button + type='submit' + className='btn btn-primary margin--extra' + onClick={this.submitNext} > + Next<i className='glyphicon glyphicon-chevron-right'></i> + </button> <div className='margin--extra'> - <a href='#' onClick={this.submitBack}>Back to previous step</a> + <a + href='#' + onClick={this.submitBack}> + Back to previous step + </a> </div> </form> </div> ); } -}); +} + +TeamSignupDisplayNamePage.propTypes = { + state: React.PropTypes.object, + updateParent: React.PropTypes.func +}; diff --git a/web/react/components/user_settings_general.jsx b/web/react/components/user_settings_general.jsx index ddd2fb607..ead7ac1d5 100644 --- a/web/react/components/user_settings_general.jsx +++ b/web/react/components/user_settings_general.jsx @@ -194,7 +194,7 @@ export default class UserSettingsGeneralTab extends React.Component { this.props.updateSection(section); } handleClose() { - $(this.getDOMNode()).find('.form-control').each(function clearForms() { + $(React.findDOMNode(this)).find('.form-control').each(function clearForms() { this.value = ''; }); @@ -230,7 +230,6 @@ export default class UserSettingsGeneralTab extends React.Component { } var nameSection; - var self = this; var inputs = []; if (this.props.activeSection === 'name') { @@ -276,9 +275,9 @@ export default class UserSettingsGeneralTab extends React.Component { server_error={serverError} client_error={clientError} updateSection={function clearSection(e) { - self.updateSection(''); + this.updateSection(''); e.preventDefault(); - }} + }.bind(this)} /> ); } else { @@ -297,8 +296,8 @@ export default class UserSettingsGeneralTab extends React.Component { title='Full Name' describe={fullName} updateSection={function updateNameSection() { - self.updateSection('name'); - }} + this.updateSection('name'); + }.bind(this)} /> ); } @@ -335,9 +334,9 @@ export default class UserSettingsGeneralTab extends React.Component { server_error={serverError} client_error={clientError} updateSection={function clearSection(e) { - self.updateSection(''); + this.updateSection(''); e.preventDefault(); - }} + }.bind(this)} /> ); } else { @@ -346,8 +345,8 @@ export default class UserSettingsGeneralTab extends React.Component { title='Nickname' describe={UserStore.getCurrentUser().nickname} updateSection={function updateNicknameSection() { - self.updateSection('nickname'); - }} + this.updateSection('nickname'); + }.bind(this)} /> ); } @@ -384,9 +383,9 @@ export default class UserSettingsGeneralTab extends React.Component { server_error={serverError} client_error={clientError} updateSection={function clearSection(e) { - self.updateSection(''); + this.updateSection(''); e.preventDefault(); - }} + }.bind(this)} /> ); } else { @@ -395,8 +394,8 @@ export default class UserSettingsGeneralTab extends React.Component { title='Username' describe={UserStore.getCurrentUser().username} updateSection={function updateUsernameSection() { - self.updateSection('username'); - }} + this.updateSection('username'); + }.bind(this)} /> ); } @@ -433,9 +432,9 @@ export default class UserSettingsGeneralTab extends React.Component { server_error={serverError} client_error={emailError} updateSection={function clearSection(e) { - self.updateSection(''); + this.updateSection(''); e.preventDefault(); - }} + }.bind(this)} /> ); } else { @@ -444,8 +443,8 @@ export default class UserSettingsGeneralTab extends React.Component { title='Email' describe={UserStore.getCurrentUser().email} updateSection={function updateEmailSection() { - self.updateSection('email'); - }} + this.updateSection('email'); + }.bind(this)} /> ); } @@ -460,9 +459,9 @@ export default class UserSettingsGeneralTab extends React.Component { server_error={serverError} client_error={clientError} updateSection={function clearSection(e) { - self.updateSection(''); + this.updateSection(''); e.preventDefault(); - }} + }.bind(this)} picture={this.state.picture} pictureChange={this.updatePicture} submitActive={this.submitActive} @@ -479,8 +478,8 @@ export default class UserSettingsGeneralTab extends React.Component { title='Profile Picture' describe={minMessage} updateSection={function updatePictureSection() { - self.updateSection('picture'); - }} + this.updateSection('picture'); + }.bind(this)} /> ); } diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx index 98014ed12..d56b309fa 100644 --- a/web/react/pages/channel.jsx +++ b/web/react/pages/channel.jsx @@ -26,13 +26,13 @@ var ChannelMembersModal = require('../components/channel_members.jsx'); 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 ErrorBar = require('../components/error_bar.jsx'); var ChannelLoader = require('../components/channel_loader.jsx'); var MentionList = require('../components/mention_list.jsx'); var ChannelInfoModal = require('../components/channel_info_modal.jsx'); var AccessHistoryModal = require('../components/access_history_modal.jsx'); var ActivityLogModal = require('../components/activity_log_modal.jsx'); -var RemovedFromChannelModal = require('../components/removed_from_channel_modal.jsx') +var RemovedFromChannelModal = require('../components/removed_from_channel_modal.jsx'); var FileUploadOverlay = require('../components/file_upload_overlay.jsx'); var AsyncClient = require('../utils/async_client.jsx'); @@ -40,18 +40,18 @@ var AsyncClient = require('../utils/async_client.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; -global.window.setup_channel_page = function(team_name, team_type, team_id, channel_name, channel_id) { +function setupChannelPage(teamName, teamType, teamId, channelName, channelId) { AsyncClient.getConfig(); AppDispatcher.handleViewAction({ type: ActionTypes.CLICK_CHANNEL, - name: channel_name, - id: channel_id + name: channelName, + id: channelId }); AppDispatcher.handleViewAction({ type: ActionTypes.CLICK_TEAM, - id: team_id + id: teamId }); // ChannelLoader must be rendered first @@ -66,12 +66,14 @@ global.window.setup_channel_page = function(team_name, team_type, team_id, chann ); React.render( - <Navbar teamDisplayName={team_name} />, + <Navbar teamDisplayName={teamName} />, document.getElementById('navbar') ); React.render( - <Sidebar teamDisplayName={team_name} teamType={team_type} />, + <Sidebar + teamDisplayName={teamName} + teamType={teamType} />, document.getElementById('sidebar-left') ); @@ -86,17 +88,17 @@ global.window.setup_channel_page = function(team_name, team_type, team_id, chann ); React.render( - <TeamSettingsModal teamDisplayName={team_name} />, + <TeamSettingsModal teamDisplayName={teamName} />, document.getElementById('team_settings_modal') ); React.render( - <TeamMembersModal teamDisplayName={team_name} />, + <TeamMembersModal teamDisplayName={teamName} />, document.getElementById('team_members_modal') ); React.render( - <MemberInviteModal teamType={team_type} />, + <MemberInviteModal teamType={teamType} />, document.getElementById('invite_member_modal') ); @@ -186,7 +188,9 @@ global.window.setup_channel_page = function(team_name, team_type, team_id, chann ); React.render( - <SidebarRightMenu teamDisplayName={team_name} teamType={team_type} />, + <SidebarRightMenu + teamDisplayName={teamName} + teamType={teamType} />, document.getElementById('sidebar-menu') ); @@ -225,5 +229,6 @@ global.window.setup_channel_page = function(team_name, team_type, team_id, chann overlayType='center' />, document.getElementById('file_upload_overlay') ); +} -}; +global.window.setup_channel_page = setupChannelPage; diff --git a/web/react/pages/find_team.jsx b/web/react/pages/find_team.jsx index 5346c0cf0..dd11857ac 100644 --- a/web/react/pages/find_team.jsx +++ b/web/react/pages/find_team.jsx @@ -3,11 +3,11 @@ var FindTeam = require('../components/find_team.jsx'); -global.window.setup_find_team_page = function() { - +function setupFindTeamPage() { React.render( <FindTeam />, document.getElementById('find-team') ); +} -}; +global.window.setup_find_team_page = setupFindTeamPage; diff --git a/web/react/pages/home.jsx b/web/react/pages/home.jsx index b12fa4949..18553542c 100644 --- a/web/react/pages/home.jsx +++ b/web/react/pages/home.jsx @@ -2,14 +2,15 @@ // See License.txt for license information. var ChannelStore = require('../stores/channel_store.jsx'); -var TeamStore = require('../stores/team_store.jsx'); var Constants = require('../utils/constants.jsx'); -global.window.setup_home_page = function(teamURL) { +function setupHomePage(teamURL) { var last = ChannelStore.getLastVisitedName(); if (last == null || last.length === 0) { - window.location = teamURL + "/channels/" + Constants.DEFAULT_CHANNEL; + window.location = teamURL + '/channels/' + Constants.DEFAULT_CHANNEL; } else { - window.location = teamURL + "/channels/" + last; + window.location = teamURL + '/channels/' + last; } } + +global.window.setup_home_page = setupHomePage; diff --git a/web/react/pages/login.jsx b/web/react/pages/login.jsx index 6e7528373..e7305889d 100644 --- a/web/react/pages/login.jsx +++ b/web/react/pages/login.jsx @@ -3,9 +3,14 @@ var Login = require('../components/login.jsx'); -global.window.setup_login_page = function(team_display_name, team_name, auth_services) { +function setupLoginPage(teamDisplayName, teamName, authServices) { React.render( - <Login teamDisplayName={team_display_name} teamName={team_name} authServices={auth_services} />, + <Login + teamDisplayName={teamDisplayName} + teamName={teamName} + authServices={authServices} />, document.getElementById('login') ); -}; +} + +global.window.setup_login_page = setupLoginPage; diff --git a/web/react/pages/password_reset.jsx b/web/react/pages/password_reset.jsx index c7a208973..2ca468bea 100644 --- a/web/react/pages/password_reset.jsx +++ b/web/react/pages/password_reset.jsx @@ -3,17 +3,17 @@ var PasswordReset = require('../components/password_reset.jsx'); -global.window.setup_password_reset_page = function(is_reset, team_display_name, team_name, hash, data) { - +function setupPasswordResetPage(isReset, teamDisplayName, teamName, hash, data) { React.render( <PasswordReset - isReset={is_reset} - teamDisplayName={team_display_name} - teamName={team_name} + isReset={isReset} + teamDisplayName={teamDisplayName} + teamName={teamName} hash={hash} data={data} />, document.getElementById('reset') ); +} -}; +global.window.setup_password_reset_page = setupPasswordResetPage; diff --git a/web/react/pages/signup_team.jsx b/web/react/pages/signup_team.jsx index 4b58025ac..e9e803aa4 100644 --- a/web/react/pages/signup_team.jsx +++ b/web/react/pages/signup_team.jsx @@ -5,7 +5,7 @@ var SignupTeam = require('../components/signup_team.jsx'); var AsyncClient = require('../utils/async_client.jsx'); -global.window.setup_signup_team_page = function(authServices) { +function setupSignupTeamPage(authServices) { AsyncClient.getConfig(); var services = JSON.parse(authServices); @@ -14,4 +14,6 @@ global.window.setup_signup_team_page = function(authServices) { <SignupTeam services={services} />, document.getElementById('signup-team') ); -}; +} + +global.window.setup_signup_team_page = setupSignupTeamPage; diff --git a/web/react/pages/signup_team_complete.jsx b/web/react/pages/signup_team_complete.jsx index 71806c2ea..43e3aae65 100644 --- a/web/react/pages/signup_team_complete.jsx +++ b/web/react/pages/signup_team_complete.jsx @@ -1,11 +1,16 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. -var SignupTeamComplete =require('../components/signup_team_complete.jsx'); +var SignupTeamComplete = require('../components/signup_team_complete.jsx'); -global.window.setup_signup_team_complete_page = function(email, data, hash) { +function setupSignupTeamCompletePage(email, data, hash) { React.render( - <SignupTeamComplete email={email} hash={hash} data={data}/>, + <SignupTeamComplete + email={email} + hash={hash} + data={data}/>, document.getElementById('signup-team-complete') ); -}; +} + +global.window.setup_signup_team_complete_page = setupSignupTeamCompletePage; diff --git a/web/react/pages/signup_user_complete.jsx b/web/react/pages/signup_user_complete.jsx index 8f9be1f94..71b526e5d 100644 --- a/web/react/pages/signup_user_complete.jsx +++ b/web/react/pages/signup_user_complete.jsx @@ -3,9 +3,18 @@ var SignupUserComplete = require('../components/signup_user_complete.jsx'); -global.window.setup_signup_user_complete_page = function(email, name, ui_name, id, data, hash, auth_services) { +function setupSignupUserCompletePage(email, name, uiName, id, data, hash, authServices) { React.render( - <SignupUserComplete teamId={id} teamName={name} teamDisplayName={ui_name} email={email} hash={hash} data={data} authServices={auth_services} />, + <SignupUserComplete + teamId={id} + teamName={name} + teamDisplayName={uiName} + email={email} + hash={hash} + data={data} + authServices={authServices} />, document.getElementById('signup-user-complete') ); -}; +} + +global.window.setup_signup_user_complete_page = setupSignupUserCompletePage; diff --git a/web/react/pages/verify.jsx b/web/react/pages/verify.jsx index 96b556983..f42913315 100644 --- a/web/react/pages/verify.jsx +++ b/web/react/pages/verify.jsx @@ -5,7 +5,10 @@ var EmailVerify = require('../components/email_verify.jsx'); global.window.setupVerifyPage = function setupVerifyPage(isVerified, teamURL, userEmail) { React.render( - <EmailVerify isVerified={isVerified} teamURL={teamURL} userEmail={userEmail} />, + <EmailVerify + isVerified={isVerified} + teamURL={teamURL} + userEmail={userEmail} />, document.getElementById('verify') ); }; diff --git a/web/react/stores/browser_store.jsx b/web/react/stores/browser_store.jsx index b1f51e5f4..e1ca52746 100644 --- a/web/react/stores/browser_store.jsx +++ b/web/react/stores/browser_store.jsx @@ -12,81 +12,70 @@ function getPrefix() { // Also change model/utils.go ETAG_ROOT_VERSION var BROWSER_STORE_VERSION = '.5'; -module.exports = { - initialized: false, +class BrowserStoreClass { + constructor() { + this.getItem = this.getItem.bind(this); + this.setItem = this.setItem.bind(this); + this.removeItem = this.removeItem.bind(this); + this.setGlobalItem = this.setGlobalItem.bind(this); + this.getGlobalItem = this.getGlobalItem.bind(this); + this.removeGlobalItem = this.removeGlobalItem.bind(this); + this.clear = this.clear.bind(this); + this.actionOnItemsWithPrefix = this.actionOnItemsWithPrefix.bind(this); + this.isLocalStorageSupported = this.isLocalStorageSupported.bind(this); - initialize: function() { var currentVersion = localStorage.getItem('local_storage_version'); if (currentVersion !== BROWSER_STORE_VERSION) { this.clear(); localStorage.setItem('local_storage_version', BROWSER_STORE_VERSION); } - this.initialized = true; - }, + } - getItem: function(name, defaultValue) { + getItem(name, defaultValue) { return this.getGlobalItem(getPrefix() + name, defaultValue); - }, + } - setItem: function(name, value) { + setItem(name, value) { this.setGlobalItem(getPrefix() + name, value); - }, - - removeItem: function(name) { - if (!this.initialized) { - this.initialize(); - } + } + removeItem(name) { localStorage.removeItem(getPrefix() + name); - }, - - setGlobalItem: function(name, value) { - if (!this.initialized) { - this.initialize(); - } + } + setGlobalItem(name, value) { localStorage.setItem(name, JSON.stringify(value)); - }, - - getGlobalItem: function(name, defaultValue) { - if (!this.initialized) { - this.initialize(); - } + } + getGlobalItem(name, defaultValue) { var result = null; try { result = JSON.parse(localStorage.getItem(name)); - } catch (err) {} + } catch (err) { + result = null; + } if (result === null && typeof defaultValue !== 'undefined') { result = defaultValue; } return result; - }, - - removeGlobalItem: function(name) { - if (!this.initialized) { - this.initialize(); - } + } + removeGlobalItem(name) { localStorage.removeItem(name); - }, + } - clear: function() { + clear() { localStorage.clear(); sessionStorage.clear(); - }, + } /** * Preforms the given action on each item that has the given prefix * Signature for action is action(key, value) */ - actionOnItemsWithPrefix: function(prefix, action) { - if (!this.initialized) { - this.initialize(); - } - + actionOnItemsWithPrefix(prefix, action) { var globalPrefix = getPrefix(); var globalPrefixiLen = globalPrefix.length; for (var key in localStorage) { @@ -95,9 +84,9 @@ module.exports = { action(userkey, this.getGlobalItem(key)); } } - }, + } - isLocalStorageSupported: function() { + isLocalStorageSupported() { try { sessionStorage.setItem('testSession', '1'); sessionStorage.removeItem('testSession'); @@ -113,4 +102,7 @@ module.exports = { return false; } } -}; +} + +var BrowserStore = new BrowserStoreClass(); +export default BrowserStore; diff --git a/web/react/stores/config_store.jsx b/web/react/stores/config_store.jsx index 7ff177b35..b397937be 100644 --- a/web/react/stores/config_store.jsx +++ b/web/react/stores/config_store.jsx @@ -3,7 +3,6 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var EventEmitter = require('events').EventEmitter; -var assign = require('object-assign'); var BrowserStore = require('../stores/browser_store.jsx'); @@ -12,45 +11,59 @@ var ActionTypes = Constants.ActionTypes; var CHANGE_EVENT = 'change'; -var ConfigStore = assign({}, EventEmitter.prototype, { - emitChange: function emitChange() { +class ConfigStoreClass extends EventEmitter { + constructor() { + super(); + + this.emitChange = this.emitChange.bind(this); + this.addChangeListener = this.addChangeListener.bind(this); + this.removeChangeListener = this.removeChangeListener.bind(this); + this.getSetting = this.getSetting.bind(this); + this.getSettingAsBoolean = this.getSettingAsBoolean.bind(this); + this.updateStoredSettings = this.updateStoredSettings.bind(this); + } + emitChange() { this.emit(CHANGE_EVENT); - }, - addChangeListener: function addChangeListener(callback) { + } + addChangeListener(callback) { this.on(CHANGE_EVENT, callback); - }, - removeChangeListener: function removeChangeListener(callback) { + } + removeChangeListener(callback) { this.removeListener(CHANGE_EVENT, callback); - }, - getSetting: function getSetting(key, defaultValue) { + } + getSetting(key, defaultValue) { return BrowserStore.getItem('config_' + key, defaultValue); - }, - getSettingAsBoolean: function getSettingAsNumber(key, defaultValue) { - var value = ConfigStore.getSetting(key, defaultValue); + } + getSettingAsBoolean(key, defaultValue) { + var value = this.getSetting(key, defaultValue); if (typeof value !== 'string') { - return !!value; - } else { - return value === 'true'; + return Boolean(value); } - }, - updateStoredSettings: function updateStoredSettings(settings) { - for (var key in settings) { - BrowserStore.setItem('config_' + key, settings[key]); + + return value === 'true'; + } + updateStoredSettings(settings) { + for (let key in settings) { + if (settings.hasOwnProperty(key)) { + BrowserStore.setItem('config_' + key, settings[key]); + } } } -}); +} + +var ConfigStore = new ConfigStoreClass(); ConfigStore.dispatchToken = AppDispatcher.register(function registry(payload) { var action = payload.action; switch (action.type) { - case ActionTypes.RECIEVED_CONFIG: - ConfigStore.updateStoredSettings(action.settings); - ConfigStore.emitChange(); - break; - default: + case ActionTypes.RECIEVED_CONFIG: + ConfigStore.updateStoredSettings(action.settings); + ConfigStore.emitChange(); + break; + default: } }); -module.exports = ConfigStore; +export default ConfigStore; diff --git a/web/react/stores/error_store.jsx b/web/react/stores/error_store.jsx index 203b692ec..597c88cff 100644 --- a/web/react/stores/error_store.jsx +++ b/web/react/stores/error_store.jsx @@ -3,7 +3,6 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var EventEmitter = require('events').EventEmitter; -var assign = require('object-assign'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; @@ -12,43 +11,53 @@ var BrowserStore = require('../stores/browser_store.jsx'); var CHANGE_EVENT = 'change'; -var ErrorStore = assign({}, EventEmitter.prototype, { - - emitChange: function() { - this.emit(CHANGE_EVENT); - }, - - addChangeListener: function(callback) { - this.on(CHANGE_EVENT, callback); - }, - - removeChangeListener: function(callback) { - this.removeListener(CHANGE_EVENT, callback); - }, - handledError: function() { - BrowserStore.removeItem("last_error"); - }, - getLastError: function() { - return BrowserStore.getItem('last_error'); - }, - - _storeLastError: function(error) { - BrowserStore.setItem("last_error", error); - }, -}); - -ErrorStore.dispatchToken = AppDispatcher.register(function(payload) { - var action = payload.action; - switch(action.type) { +class ErrorStoreClass extends EventEmitter { + constructor() { + super(); + + this.emitChange = this.emitChange.bind(this); + this.addChangeListener = this.addChangeListener.bind(this); + this.removeChangeListener = this.removeChangeListener.bind(this); + this.handledError = this.handledError.bind(this); + this.getLastError = this.getLastError.bind(this); + this.storeLastError = this.storeLastError.bind(this); + } + + emitChange() { + this.emit(CHANGE_EVENT); + } + + addChangeListener(callback) { + this.on(CHANGE_EVENT, callback); + } + + removeChangeListener(callback) { + this.removeListener(CHANGE_EVENT, callback); + } + handledError() { + BrowserStore.removeItem('last_error'); + } + getLastError() { + return BrowserStore.getItem('last_error'); + } + + storeLastError(error) { + BrowserStore.setItem('last_error', error); + } +} + +var ErrorStore = new ErrorStoreClass(); + +ErrorStore.dispatchToken = AppDispatcher.register(function registry(payload) { + var action = payload.action; + switch (action.type) { case ActionTypes.RECIEVED_ERROR: - ErrorStore._storeLastError(action.err); - ErrorStore.emitChange(); - break; + ErrorStore.storeLastError(action.err); + ErrorStore.emitChange(); + break; default: - } + } }); -module.exports = ErrorStore; - - +export default ErrorStore; diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx index 4038814d2..5ffe65021 100644 --- a/web/react/stores/post_store.jsx +++ b/web/react/stores/post_store.jsx @@ -3,7 +3,6 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var EventEmitter = require('events').EventEmitter; -var assign = require('object-assign'); var ChannelStore = require('../stores/channel_store.jsx'); var BrowserStore = require('../stores/browser_store.jsx'); @@ -18,109 +17,169 @@ var SELECTED_POST_CHANGE_EVENT = 'selected_post_change'; var MENTION_DATA_CHANGE_EVENT = 'mention_data_change'; var ADD_MENTION_EVENT = 'add_mention'; -var PostStore = assign({}, EventEmitter.prototype, { - emitChange: function emitChange() { +class PostStoreClass extends EventEmitter { + constructor() { + super(); + + this.emitChange = this.emitChange.bind(this); + this.addChangeListener = this.addChangeListener.bind(this); + this.removeChangeListener = this.removeChangeListener.bind(this); + this.emitSearchChange = this.emitSearchChange.bind(this); + this.addSearchChangeListener = this.addSearchChangeListener.bind(this); + this.removeSearchChangeListener = this.removeSearchChangeListener.bind(this); + this.emitSearchTermChange = this.emitSearchTermChange.bind(this); + this.addSearchTermChangeListener = this.addSearchTermChangeListener.bind(this); + this.removeSearchTermChangeListener = this.removeSearchTermChangeListener.bind(this); + this.emitSelectedPostChange = this.emitSelectedPostChange.bind(this); + this.addSelectedPostChangeListener = this.addSelectedPostChangeListener.bind(this); + this.removeSelectedPostChangeListener = this.removeSelectedPostChangeListener.bind(this); + this.emitMentionDataChange = this.emitMentionDataChange.bind(this); + this.addMentionDataChangeListener = this.addMentionDataChangeListener.bind(this); + this.removeMentionDataChangeListener = this.removeMentionDataChangeListener.bind(this); + this.emitAddMention = this.emitAddMention.bind(this); + this.addAddMentionListener = this.addAddMentionListener.bind(this); + this.removeAddMentionListener = this.removeAddMentionListener.bind(this); + this.getCurrentPosts = this.getCurrentPosts.bind(this); + this.storePosts = this.storePosts.bind(this); + this.pStorePosts = this.pStorePosts.bind(this); + this.getPosts = this.getPosts.bind(this); + this.storePost = this.storePost.bind(this); + this.pStorePost = this.pStorePost.bind(this); + this.removePost = this.removePost.bind(this); + this.storePendingPost = this.storePendingPost.bind(this); + this.pStorePendingPosts = this.pStorePendingPosts.bind(this); + this.getPendingPosts = this.getPendingPosts.bind(this); + this.storeUnseenDeletedPost = this.storeUnseenDeletedPost.bind(this); + this.storeUnseenDeletedPosts = this.storeUnseenDeletedPosts.bind(this); + this.getUnseenDeletedPosts = this.getUnseenDeletedPosts.bind(this); + this.clearUnseenDeletedPosts = this.clearUnseenDeletedPosts.bind(this); + this.removePendingPost = this.removePendingPost.bind(this); + this.pRemovePendingPost = this.pRemovePendingPost.bind(this); + this.clearPendingPosts = this.clearPendingPosts.bind(this); + this.updatePendingPost = this.updatePendingPost.bind(this); + this.storeSearchResults = this.storeSearchResults.bind(this); + this.getSearchResults = this.getSearchResults.bind(this); + this.getIsMentionSearch = this.getIsMentionSearch.bind(this); + this.storeSelectedPost = this.storeSelectedPost.bind(this); + this.getSelectedPost = this.getSelectedPost.bind(this); + this.storeSearchTerm = this.storeSearchTerm.bind(this); + this.getSearchTerm = this.getSearchTerm.bind(this); + this.getEmptyDraft = this.getEmptyDraft.bind(this); + this.storeCurrentDraft = this.storeCurrentDraft.bind(this); + this.getCurrentDraft = this.getCurrentDraft.bind(this); + this.storeDraft = this.storeDraft.bind(this); + this.getDraft = this.getDraft.bind(this); + this.storeCommentDraft = this.storeCommentDraft.bind(this); + this.getCommentDraft = this.getCommentDraft.bind(this); + this.clearDraftUploads = this.clearDraftUploads.bind(this); + this.clearCommentDraftUploads = this.clearCommentDraftUploads.bind(this); + this.storeLatestUpdate = this.storeLatestUpdate.bind(this); + this.getLatestUpdate = this.getLatestUpdate.bind(this); + } + emitChange() { this.emit(CHANGE_EVENT); - }, + } - addChangeListener: function addChangeListener(callback) { + addChangeListener(callback) { this.on(CHANGE_EVENT, callback); - }, + } - removeChangeListener: function removeChangeListener(callback) { + removeChangeListener(callback) { this.removeListener(CHANGE_EVENT, callback); - }, + } - emitSearchChange: function emitSearchChange() { + emitSearchChange() { this.emit(SEARCH_CHANGE_EVENT); - }, + } - addSearchChangeListener: function addSearchChangeListener(callback) { + addSearchChangeListener(callback) { this.on(SEARCH_CHANGE_EVENT, callback); - }, + } - removeSearchChangeListener: function removeSearchChangeListener(callback) { + removeSearchChangeListener(callback) { this.removeListener(SEARCH_CHANGE_EVENT, callback); - }, + } - emitSearchTermChange: function emitSearchTermChange(doSearch, isMentionSearch) { + emitSearchTermChange(doSearch, isMentionSearch) { this.emit(SEARCH_TERM_CHANGE_EVENT, doSearch, isMentionSearch); - }, + } - addSearchTermChangeListener: function addSearchTermChangeListener(callback) { + addSearchTermChangeListener(callback) { this.on(SEARCH_TERM_CHANGE_EVENT, callback); - }, + } - removeSearchTermChangeListener: function removeSearchTermChangeListener(callback) { + removeSearchTermChangeListener(callback) { this.removeListener(SEARCH_TERM_CHANGE_EVENT, callback); - }, + } - emitSelectedPostChange: function emitSelectedPostChange(fromSearch) { + emitSelectedPostChange(fromSearch) { this.emit(SELECTED_POST_CHANGE_EVENT, fromSearch); - }, + } - addSelectedPostChangeListener: function addSelectedPostChangeListener(callback) { + addSelectedPostChangeListener(callback) { this.on(SELECTED_POST_CHANGE_EVENT, callback); - }, + } - removeSelectedPostChangeListener: function removeSelectedPostChangeListener(callback) { + removeSelectedPostChangeListener(callback) { this.removeListener(SELECTED_POST_CHANGE_EVENT, callback); - }, + } - emitMentionDataChange: function emitMentionDataChange(id, mentionText) { + emitMentionDataChange(id, mentionText) { this.emit(MENTION_DATA_CHANGE_EVENT, id, mentionText); - }, + } - addMentionDataChangeListener: function addMentionDataChangeListener(callback) { + addMentionDataChangeListener(callback) { this.on(MENTION_DATA_CHANGE_EVENT, callback); - }, + } - removeMentionDataChangeListener: function removeMentionDataChangeListener(callback) { + removeMentionDataChangeListener(callback) { this.removeListener(MENTION_DATA_CHANGE_EVENT, callback); - }, + } - emitAddMention: function emitAddMention(id, username) { + emitAddMention(id, username) { this.emit(ADD_MENTION_EVENT, id, username); - }, + } - addAddMentionListener: function addAddMentionListener(callback) { + addAddMentionListener(callback) { this.on(ADD_MENTION_EVENT, callback); - }, + } - removeAddMentionListener: function removeAddMentionListener(callback) { + removeAddMentionListener(callback) { this.removeListener(ADD_MENTION_EVENT, callback); - }, + } - getCurrentPosts: function getCurrentPosts() { + getCurrentPosts() { var currentId = ChannelStore.getCurrentId(); if (currentId != null) { return this.getPosts(currentId); } return null; - }, - storePosts: function storePosts(channelId, newPostList) { + } + storePosts(channelId, newPostList) { if (isPostListNull(newPostList)) { return; } - var postList = makePostListNonNull(PostStore.getPosts(channelId)); - - for (var pid in newPostList.posts) { - var np = newPostList.posts[pid]; - if (np.delete_at === 0) { - postList.posts[pid] = np; - if (postList.order.indexOf(pid) === -1) { - postList.order.push(pid); - } - } else { - if (pid in postList.posts) { - delete postList.posts[pid]; - } - - var index = postList.order.indexOf(pid); - if (index !== -1) { - postList.order.splice(index, 1); + var postList = makePostListNonNull(this.getPosts(channelId)); + + for (let pid in newPostList.posts) { + if (newPostList.posts.hasOwnProperty(pid)) { + var np = newPostList.posts[pid]; + if (np.delete_at === 0) { + postList.posts[pid] = np; + if (postList.order.indexOf(pid) === -1) { + postList.order.push(pid); + } + } else { + if (pid in postList.posts) { + delete postList.posts[pid]; + } + + var index = postList.order.indexOf(pid); + if (index !== -1) { + postList.order.splice(index, 1); + } } } } @@ -146,19 +205,19 @@ var PostStore = assign({}, EventEmitter.prototype, { this.storeLatestUpdate(channelId, latestUpdate); this.pStorePosts(channelId, postList); this.emitChange(); - }, - pStorePosts: function pStorePosts(channelId, posts) { + } + pStorePosts(channelId, posts) { BrowserStore.setItem('posts_' + channelId, posts); - }, - getPosts: function getPosts(channelId) { + } + getPosts(channelId) { return BrowserStore.getItem('posts_' + channelId); - }, - storePost: function(post) { + } + storePost(post) { this.pStorePost(post); this.emitChange(); - }, - pStorePost: function(post) { - var postList = PostStore.getPosts(post.channel_id); + } + pStorePost(post) { + var postList = this.getPosts(post.channel_id); postList = makePostListNonNull(postList); if (post.pending_post_id !== '') { @@ -173,9 +232,9 @@ var PostStore = assign({}, EventEmitter.prototype, { } this.pStorePosts(post.channel_id, postList); - }, - removePost: function(postId, channelId) { - var postList = PostStore.getPosts(channelId); + } + removePost(postId, channelId) { + var postList = this.getPosts(channelId); if (isPostListNull(postList)) { return; } @@ -190,8 +249,8 @@ var PostStore = assign({}, EventEmitter.prototype, { } this.pStorePosts(channelId, postList); - }, - storePendingPost: function(post) { + } + storePendingPost(post) { post.state = Constants.POST_LOADING; var postList = this.getPendingPosts(post.channel_id); @@ -199,10 +258,10 @@ var PostStore = assign({}, EventEmitter.prototype, { postList.posts[post.pending_post_id] = post; postList.order.unshift(post.pending_post_id); - this._storePendingPosts(post.channel_id, postList); + this.pStorePendingPosts(post.channel_id, postList); this.emitChange(); - }, - _storePendingPosts: function(channelId, postList) { + } + pStorePendingPosts(channelId, postList) { var posts = postList.posts; // sort failed posts to the bottom @@ -225,11 +284,11 @@ var PostStore = assign({}, EventEmitter.prototype, { }); BrowserStore.setItem('pending_posts_' + channelId, postList); - }, - getPendingPosts: function(channelId) { + } + getPendingPosts(channelId) { return BrowserStore.getItem('pending_posts_' + channelId); - }, - storeUnseenDeletedPost: function(post) { + } + storeUnseenDeletedPost(post) { var posts = this.getUnseenDeletedPosts(post.channel_id); if (!posts) { @@ -241,21 +300,21 @@ var PostStore = assign({}, EventEmitter.prototype, { posts[post.id] = post; this.storeUnseenDeletedPosts(post.channel_id, posts); - }, - storeUnseenDeletedPosts: function(channelId, posts) { + } + storeUnseenDeletedPosts(channelId, posts) { BrowserStore.setItem('deleted_posts_' + channelId, posts); - }, - getUnseenDeletedPosts: function(channelId) { + } + getUnseenDeletedPosts(channelId) { return BrowserStore.getItem('deleted_posts_' + channelId); - }, - clearUnseenDeletedPosts: function(channelId) { + } + clearUnseenDeletedPosts(channelId) { BrowserStore.setItem('deleted_posts_' + channelId, {}); - }, - removePendingPost: function(channelId, pendingPostId) { - this._removePendingPost(channelId, pendingPostId); + } + removePendingPost(channelId, pendingPostId) { + this.pRemovePendingPost(channelId, pendingPostId); this.emitChange(); - }, - _removePendingPost: function(channelId, pendingPostId) { + } + pRemovePendingPost(channelId, pendingPostId) { var postList = this.getPendingPosts(channelId); postList = makePostListNonNull(postList); @@ -267,14 +326,14 @@ var PostStore = assign({}, EventEmitter.prototype, { postList.order.splice(index, 1); } - this._storePendingPosts(channelId, postList); - }, - clearPendingPosts: function() { + this.pStorePendingPosts(channelId, postList); + } + clearPendingPosts() { BrowserStore.actionOnItemsWithPrefix('pending_posts_', function clearPending(key) { BrowserStore.removeItem(key); }); - }, - updatePendingPost: function(post) { + } + updatePendingPost(post) { var postList = this.getPendingPosts(post.channel_id); postList = makePostListNonNull(postList); @@ -283,112 +342,114 @@ var PostStore = assign({}, EventEmitter.prototype, { } postList.posts[post.pending_post_id] = post; - this._storePendingPosts(post.channel_id, postList); + this.pStorePendingPosts(post.channel_id, postList); this.emitChange(); - }, - storeSearchResults: function storeSearchResults(results, isMentionSearch) { + } + storeSearchResults(results, isMentionSearch) { BrowserStore.setItem('search_results', results); BrowserStore.setItem('is_mention_search', Boolean(isMentionSearch)); - }, - getSearchResults: function getSearchResults() { + } + getSearchResults() { return BrowserStore.getItem('search_results'); - }, - getIsMentionSearch: function getIsMentionSearch() { + } + getIsMentionSearch() { return BrowserStore.getItem('is_mention_search'); - }, - storeSelectedPost: function storeSelectedPost(postList) { + } + storeSelectedPost(postList) { BrowserStore.setItem('select_post', postList); - }, - getSelectedPost: function getSelectedPost() { + } + getSelectedPost() { return BrowserStore.getItem('select_post'); - }, - storeSearchTerm: function storeSearchTerm(term) { + } + storeSearchTerm(term) { BrowserStore.setItem('search_term', term); - }, - getSearchTerm: function getSearchTerm() { + } + getSearchTerm() { return BrowserStore.getItem('search_term'); - }, - getEmptyDraft: function getEmptyDraft(draft) { + } + getEmptyDraft() { return {message: '', uploadsInProgress: [], previews: []}; - }, - storeCurrentDraft: function storeCurrentDraft(draft) { + } + storeCurrentDraft(draft) { var channelId = ChannelStore.getCurrentId(); BrowserStore.setItem('draft_' + channelId, draft); - }, - getCurrentDraft: function getCurrentDraft() { + } + getCurrentDraft() { var channelId = ChannelStore.getCurrentId(); - return PostStore.getDraft(channelId); - }, - storeDraft: function storeDraft(channelId, draft) { + return this.getDraft(channelId); + } + storeDraft(channelId, draft) { BrowserStore.setItem('draft_' + channelId, draft); - }, - getDraft: function getDraft(channelId) { - return BrowserStore.getItem('draft_' + channelId, PostStore.getEmptyDraft()); - }, - storeCommentDraft: function storeCommentDraft(parentPostId, draft) { + } + getDraft(channelId) { + return BrowserStore.getItem('draft_' + channelId, this.getEmptyDraft()); + } + storeCommentDraft(parentPostId, draft) { BrowserStore.setItem('comment_draft_' + parentPostId, draft); - }, - getCommentDraft: function getCommentDraft(parentPostId) { - return BrowserStore.getItem('comment_draft_' + parentPostId, PostStore.getEmptyDraft()); - }, - clearDraftUploads: function clearDraftUploads() { + } + getCommentDraft(parentPostId) { + return BrowserStore.getItem('comment_draft_' + parentPostId, this.getEmptyDraft()); + } + clearDraftUploads() { BrowserStore.actionOnItemsWithPrefix('draft_', function clearUploads(key, value) { if (value) { value.uploadsInProgress = []; BrowserStore.setItem(key, value); } }); - }, - clearCommentDraftUploads: function clearCommentDraftUploads() { + } + clearCommentDraftUploads() { BrowserStore.actionOnItemsWithPrefix('comment_draft_', function clearUploads(key, value) { if (value) { value.uploadsInProgress = []; BrowserStore.setItem(key, value); } }); - }, - storeLatestUpdate: function(channelId, time) { + } + storeLatestUpdate(channelId, time) { BrowserStore.setItem('latest_post_' + channelId, time); - }, - getLatestUpdate: function(channelId) { + } + getLatestUpdate(channelId) { return BrowserStore.getItem('latest_post_' + channelId, 0); } -}); +} + +var PostStore = new PostStoreClass(); PostStore.dispatchToken = AppDispatcher.register(function registry(payload) { var action = payload.action; switch (action.type) { - case ActionTypes.RECIEVED_POSTS: - PostStore.storePosts(action.id, makePostListNonNull(action.post_list)); - break; - case ActionTypes.RECIEVED_POST: - PostStore.pStorePost(action.post); - PostStore.emitChange(); - break; - case ActionTypes.RECIEVED_SEARCH: - PostStore.storeSearchResults(action.results, action.is_mention_search); - PostStore.emitSearchChange(); - break; - case ActionTypes.RECIEVED_SEARCH_TERM: - PostStore.storeSearchTerm(action.term); - PostStore.emitSearchTermChange(action.do_search, action.is_mention_search); - break; - case ActionTypes.RECIEVED_POST_SELECTED: - PostStore.storeSelectedPost(action.post_list); - PostStore.emitSelectedPostChange(action.from_search); - break; - case ActionTypes.RECIEVED_MENTION_DATA: - PostStore.emitMentionDataChange(action.id, action.mention_text); - break; - case ActionTypes.RECIEVED_ADD_MENTION: - PostStore.emitAddMention(action.id, action.username); - break; - default: + case ActionTypes.RECIEVED_POSTS: + PostStore.storePosts(action.id, makePostListNonNull(action.post_list)); + break; + case ActionTypes.RECIEVED_POST: + PostStore.pStorePost(action.post); + PostStore.emitChange(); + break; + case ActionTypes.RECIEVED_SEARCH: + PostStore.storeSearchResults(action.results, action.is_mention_search); + PostStore.emitSearchChange(); + break; + case ActionTypes.RECIEVED_SEARCH_TERM: + PostStore.storeSearchTerm(action.term); + PostStore.emitSearchTermChange(action.do_search, action.is_mention_search); + break; + case ActionTypes.RECIEVED_POST_SELECTED: + PostStore.storeSelectedPost(action.post_list); + PostStore.emitSelectedPostChange(action.from_search); + break; + case ActionTypes.RECIEVED_MENTION_DATA: + PostStore.emitMentionDataChange(action.id, action.mention_text); + break; + case ActionTypes.RECIEVED_ADD_MENTION: + PostStore.emitAddMention(action.id, action.username); + break; + default: } }); -module.exports = PostStore; +export default PostStore; function makePostListNonNull(pl) { var postList = pl; diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx index e43a8f2be..ae74059d1 100644 --- a/web/react/stores/socket_store.jsx +++ b/web/react/stores/socket_store.jsx @@ -2,10 +2,8 @@ // See License.txt for license information. var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); -var UserStore = require('./user_store.jsx') +var UserStore = require('./user_store.jsx'); var EventEmitter = require('events').EventEmitter; -var assign = require('object-assign'); -var client = require('../utils/client.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; @@ -14,14 +12,24 @@ var CHANGE_EVENT = 'change'; var conn; -var SocketStore = assign({}, EventEmitter.prototype, { - initialize: function() { +class SocketStoreClass extends EventEmitter { + constructor() { + super(); + + this.initialize = this.initialize.bind(this); + this.emitChange = this.emitChange.bind(this); + this.addChangeListener = this.addChangeListener.bind(this); + this.removeChangeListener = this.removeChangeListener.bind(this); + this.sendMessage = this.sendMessage.bind(this); + + this.initialize(); + } + initialize() { if (!UserStore.getCurrentId()) { return; } - var self = this; - self.setMaxListeners(0); + this.setMaxListeners(0); if (window.WebSocket && !conn) { var protocol = 'ws://'; @@ -29,24 +37,24 @@ var SocketStore = assign({}, EventEmitter.prototype, { protocol = 'wss://'; } var connUrl = protocol + location.host + '/api/v1/websocket'; - console.log('connecting to ' + connUrl); + console.log('connecting to ' + connUrl); //eslint-disable-line no-console conn = new WebSocket(connUrl); conn.onclose = function closeConn(evt) { - console.log('websocket closed'); - console.log(evt); + console.log('websocket closed'); //eslint-disable-line no-console + console.log(evt); //eslint-disable-line no-console conn = null; setTimeout( function reconnect() { - self.initialize(); - }, + this.initialize(); + }.bind(this), 3000 ); - }; + }.bind(this); conn.onerror = function connError(evt) { - console.log('websocket error'); - console.log(evt); + console.log('websocket error'); //eslint-disable-line no-console + console.log(evt); //eslint-disable-line no-console }; conn.onmessage = function connMessage(evt) { @@ -56,17 +64,17 @@ var SocketStore = assign({}, EventEmitter.prototype, { }); }; } - }, - emitChange: function(msg) { + } + emitChange(msg) { this.emit(CHANGE_EVENT, msg); - }, - addChangeListener: function(callback) { + } + addChangeListener(callback) { this.on(CHANGE_EVENT, callback); - }, - removeChangeListener: function(callback) { + } + removeChangeListener(callback) { this.removeListener(CHANGE_EVENT, callback); - }, - sendMessage: function(msg) { + } + sendMessage(msg) { if (conn && conn.readyState === WebSocket.OPEN) { conn.send(JSON.stringify(msg)); } else if (!conn || conn.readyState === WebSocket.Closed) { @@ -74,19 +82,20 @@ var SocketStore = assign({}, EventEmitter.prototype, { this.initialize(); } } -}); +} + +var SocketStore = new SocketStoreClass(); -SocketStore.dispatchToken = AppDispatcher.register(function(payload) { +SocketStore.dispatchToken = AppDispatcher.register(function registry(payload) { var action = payload.action; switch (action.type) { - case ActionTypes.RECIEVED_MSG: + case ActionTypes.RECIEVED_MSG: SocketStore.emitChange(action.msg); break; - default: + default: } }); -SocketStore.initialize(); -module.exports = SocketStore; +export default SocketStore; diff --git a/web/react/stores/team_store.jsx b/web/react/stores/team_store.jsx index 3f2248c44..1f33fe03b 100644 --- a/web/react/stores/team_store.jsx +++ b/web/react/stores/team_store.jsx @@ -3,7 +3,6 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var EventEmitter = require('events').EventEmitter; -var assign = require('object-assign'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; @@ -19,21 +18,38 @@ function getWindowLocationOrigin() { return utils.getWindowLocationOrigin(); } -var TeamStore = assign({}, EventEmitter.prototype, { - emitChange: function() { +class TeamStoreClass extends EventEmitter { + constructor() { + super(); + + this.emitChange = this.emitChange.bind(this); + this.addChangeListener = this.addChangeListener.bind(this); + this.removeChangeListener = this.removeChangeListener.bind(this); + this.get = this.get.bind(this); + this.getByName = this.getByName.bind(this); + this.getAll = this.getAll.bind(this); + this.setCurrentId = this.setCurrentId.bind(this); + this.getCurrentId = this.getCurrentId.bind(this); + this.getCurrent = this.getCurrent.bind(this); + this.getCurrentTeamUrl = this.getCurrentTeamUrl.bind(this); + this.storeTeam = this.storeTeam.bind(this); + this.pStoreTeams = this.pStoreTeams.bind(this); + this.pGetTeams = this.pGetTeams.bind(this); + } + emitChange() { this.emit(CHANGE_EVENT); - }, - addChangeListener: function(callback) { + } + addChangeListener(callback) { this.on(CHANGE_EVENT, callback); - }, - removeChangeListener: function(callback) { + } + removeChangeListener(callback) { this.removeListener(CHANGE_EVENT, callback); - }, - get: function(id) { + } + get(id) { var c = this.pGetTeams(); return c[id]; - }, - getByName: function(name) { + } + getByName(name) { var t = this.pGetTeams(); for (var id in t) { @@ -43,64 +59,65 @@ var TeamStore = assign({}, EventEmitter.prototype, { } return null; - }, - getAll: function() { + } + getAll() { return this.pGetTeams(); - }, - setCurrentId: function(id) { + } + setCurrentId(id) { if (id === null) { BrowserStore.removeItem('current_team_id'); } else { BrowserStore.setItem('current_team_id', id); } - }, - getCurrentId: function() { + } + getCurrentId() { return BrowserStore.getItem('current_team_id'); - }, - getCurrent: function() { - var currentId = TeamStore.getCurrentId(); + } + getCurrent() { + var currentId = this.getCurrentId(); if (currentId !== null) { return this.get(currentId); } return null; - }, - getCurrentTeamUrl: function() { + } + getCurrentTeamUrl() { if (this.getCurrent()) { return getWindowLocationOrigin() + '/' + this.getCurrent().name; } return null; - }, - storeTeam: function(team) { + } + storeTeam(team) { var teams = this.pGetTeams(); teams[team.id] = team; this.pStoreTeams(teams); - }, - pStoreTeams: function(teams) { + } + pStoreTeams(teams) { BrowserStore.setItem('user_teams', teams); - }, - pGetTeams: function() { + } + pGetTeams() { return BrowserStore.getItem('user_teams', {}); } -}); +} + +var TeamStore = new TeamStoreClass(); TeamStore.dispatchToken = AppDispatcher.register(function registry(payload) { var action = payload.action; switch (action.type) { + case ActionTypes.CLICK_TEAM: + TeamStore.setCurrentId(action.id); + TeamStore.emitChange(); + break; - case ActionTypes.CLICK_TEAM: - TeamStore.setCurrentId(action.id); - TeamStore.emitChange(); - break; - - case ActionTypes.RECIEVED_TEAM: - TeamStore.storeTeam(action.team); - TeamStore.emitChange(); - break; + case ActionTypes.RECIEVED_TEAM: + TeamStore.storeTeam(action.team); + TeamStore.emitChange(); + break; - default: + default: } }); -module.exports = TeamStore; +export default TeamStore; diff --git a/web/react/stores/user_store.jsx b/web/react/stores/user_store.jsx index 248495dac..f75c1d4c3 100644 --- a/web/react/stores/user_store.jsx +++ b/web/react/stores/user_store.jsx @@ -3,7 +3,6 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var EventEmitter = require('events').EventEmitter; -var assign = require('object-assign'); var client = require('../utils/client.jsx'); var Constants = require('../utils/constants.jsx'); @@ -16,64 +15,114 @@ var CHANGE_EVENT_AUDITS = 'change_audits'; var CHANGE_EVENT_TEAMS = 'change_teams'; var CHANGE_EVENT_STATUSES = 'change_statuses'; -var UserStore = assign({}, EventEmitter.prototype, { +class UserStoreClass extends EventEmitter { + constructor() { + super(); - gCurrentId: null, + this.emitChange = this.emitChange.bind(this); + this.addChangeListener = this.addChangeListener.bind(this); + this.removeChangeListener = this.removeChangeListener.bind(this); + this.emitSessionsChange = this.emitSessionsChange.bind(this); + this.addSessionsChangeListener = this.addSessionsChangeListener.bind(this); + this.removeSessionsChangeListener = this.removeSessionsChangeListener.bind(this); + this.emitAuditsChange = this.emitAuditsChange.bind(this); + this.addAuditsChangeListener = this.addAuditsChangeListener.bind(this); + this.removeAuditsChangeListener = this.removeAuditsChangeListener.bind(this); + this.emitTeamsChange = this.emitTeamsChange.bind(this); + this.addTeamsChangeListener = this.addTeamsChangeListener.bind(this); + this.removeTeamsChangeListener = this.removeTeamsChangeListener.bind(this); + this.emitStatusesChange = this.emitStatusesChange.bind(this); + this.addStatusesChangeListener = this.addStatusesChangeListener.bind(this); + this.removeStatusesChangeListener = this.removeStatusesChangeListener.bind(this); + this.setCurrentId = this.setCurrentId.bind(this); + this.getCurrentId = this.getCurrentId.bind(this); + this.getCurrentUser = this.getCurrentUser.bind(this); + this.setCurrentUser = this.setCurrentUser.bind(this); + this.getLastEmail = this.getLastEmail.bind(this); + this.setLastEmail = this.setLastEmail.bind(this); + this.removeCurrentUser = this.removeCurrentUser.bind(this); + this.hasProfile = this.hasProfile.bind(this); + this.getProfile = this.getProfile.bind(this); + this.getProfileByUsername = this.getProfileByUsername.bind(this); + this.getProfilesUsernameMap = this.getProfilesUsernameMap.bind(this); + this.getProfiles = this.getProfiles.bind(this); + this.getActiveOnlyProfiles = this.getActiveOnlyProfiles.bind(this); + this.saveProfile = this.saveProfile.bind(this); + this.pStoreProfiles = this.pStoreProfiles.bind(this); + this.pGetProfiles = this.pGetProfiles.bind(this); + this.pGetProfilesUsernameMap = this.pGetProfilesUsernameMap.bind(this); + this.setSessions = this.setSessions.bind(this); + this.getSessions = this.getSessions.bind(this); + this.setAudits = this.setAudits.bind(this); + this.getAudits = this.getAudits.bind(this); + this.setTeams = this.setTeams.bind(this); + this.getTeams = this.getTeams.bind(this); + this.getCurrentMentionKeys = this.getCurrentMentionKeys.bind(this); + this.getLastVersion = this.getLastVersion.bind(this); + this.setLastVersion = this.setLastVersion.bind(this); + this.setStatuses = this.setStatuses.bind(this); + this.pSetStatuses = this.pSetStatuses.bind(this); + this.setStatus = this.setStatus.bind(this); + this.getStatuses = this.getStatuses.bind(this); + this.getStatus = this.getStatus.bind(this); - emitChange: function(userId) { + this.gCurrentId = null; + } + + emitChange(userId) { this.emit(CHANGE_EVENT, userId); - }, - addChangeListener: function(callback) { + } + addChangeListener(callback) { this.on(CHANGE_EVENT, callback); - }, - removeChangeListener: function(callback) { + } + removeChangeListener(callback) { this.removeListener(CHANGE_EVENT, callback); - }, - emitSessionsChange: function() { + } + emitSessionsChange() { this.emit(CHANGE_EVENT_SESSIONS); - }, - addSessionsChangeListener: function(callback) { + } + addSessionsChangeListener(callback) { this.on(CHANGE_EVENT_SESSIONS, callback); - }, - removeSessionsChangeListener: function(callback) { + } + removeSessionsChangeListener(callback) { this.removeListener(CHANGE_EVENT_SESSIONS, callback); - }, - emitAuditsChange: function() { + } + emitAuditsChange() { this.emit(CHANGE_EVENT_AUDITS); - }, - addAuditsChangeListener: function(callback) { + } + addAuditsChangeListener(callback) { this.on(CHANGE_EVENT_AUDITS, callback); - }, - removeAuditsChangeListener: function(callback) { + } + removeAuditsChangeListener(callback) { this.removeListener(CHANGE_EVENT_AUDITS, callback); - }, - emitTeamsChange: function() { + } + emitTeamsChange() { this.emit(CHANGE_EVENT_TEAMS); - }, - addTeamsChangeListener: function(callback) { + } + addTeamsChangeListener(callback) { this.on(CHANGE_EVENT_TEAMS, callback); - }, - removeTeamsChangeListener: function(callback) { + } + removeTeamsChangeListener(callback) { this.removeListener(CHANGE_EVENT_TEAMS, callback); - }, - emitStatusesChange: function() { + } + emitStatusesChange() { this.emit(CHANGE_EVENT_STATUSES); - }, - addStatusesChangeListener: function(callback) { + } + addStatusesChangeListener(callback) { this.on(CHANGE_EVENT_STATUSES, callback); - }, - removeStatusesChangeListener: function(callback) { + } + removeStatusesChangeListener(callback) { this.removeListener(CHANGE_EVENT_STATUSES, callback); - }, - setCurrentId: function(id) { + } + setCurrentId(id) { this.gCurrentId = id; if (id == null) { BrowserStore.removeGlobalItem('current_user_id'); } else { BrowserStore.setGlobalItem('current_user_id', id); } - }, - getCurrentId: function(skipFetch) { + } + getCurrentId(skipFetch) { var currentId = this.gCurrentId; if (currentId == null) { @@ -93,46 +142,45 @@ var UserStore = assign({}, EventEmitter.prototype, { } return currentId; - }, - getCurrentUser: function() { + } + getCurrentUser() { if (this.getCurrentId() == null) { return null; } - return this._getProfiles()[this.getCurrentId()]; - }, - setCurrentUser: function(user) { + return this.pGetProfiles()[this.getCurrentId()]; + } + setCurrentUser(user) { this.setCurrentId(user.id); this.saveProfile(user); - }, - getLastEmail: function() { + } + getLastEmail() { return BrowserStore.getItem('last_email', ''); - }, - setLastEmail: function(email) { + } + setLastEmail(email) { BrowserStore.setItem('last_email', email); - }, - removeCurrentUser: function() { + } + removeCurrentUser() { this.setCurrentId(null); - }, - hasProfile: function(userId) { - return this._getProfiles()[userId] != null; - }, - getProfile: function(userId) { - return this._getProfiles()[userId]; - }, - getProfileByUsername: function(username) { - return this._getProfilesUsernameMap()[username]; - }, - getProfilesUsernameMap: function() { - return this._getProfilesUsernameMap(); - }, - getProfiles: function() { - - return this._getProfiles(); - }, - getActiveOnlyProfiles: function() { + } + hasProfile(userId) { + return this.pGetProfiles()[userId] != null; + } + getProfile(userId) { + return this.pGetProfiles()[userId]; + } + getProfileByUsername(username) { + return this.pGetProfilesUsernameMap()[username]; + } + getProfilesUsernameMap() { + return this.pGetProfilesUsernameMap(); + } + getProfiles() { + return this.pGetProfiles(); + } + getActiveOnlyProfiles() { var active = {}; - var current = this._getProfiles(); + var current = this.pGetProfiles(); for (var key in current) { if (current[key].delete_at === 0) { @@ -141,45 +189,47 @@ var UserStore = assign({}, EventEmitter.prototype, { } return active; - }, - saveProfile: function(profile) { - var ps = this._getProfiles(); + } + saveProfile(profile) { + var ps = this.pGetProfiles(); ps[profile.id] = profile; - this._storeProfiles(ps); - }, - _storeProfiles: function(profiles) { + this.pStoreProfiles(ps); + } + pStoreProfiles(profiles) { BrowserStore.setItem('profiles', profiles); var profileUsernameMap = {}; for (var id in profiles) { - profileUsernameMap[profiles[id].username] = profiles[id]; + if (profiles.hasOwnProperty(id)) { + profileUsernameMap[profiles[id].username] = profiles[id]; + } } BrowserStore.setItem('profileUsernameMap', profileUsernameMap); - }, - _getProfiles: function() { + } + pGetProfiles() { return BrowserStore.getItem('profiles', {}); - }, - _getProfilesUsernameMap: function() { + } + pGetProfilesUsernameMap() { return BrowserStore.getItem('profileUsernameMap', {}); - }, - setSessions: function(sessions) { + } + setSessions(sessions) { BrowserStore.setItem('sessions', sessions); - }, - getSessions: function() { + } + getSessions() { return BrowserStore.getItem('sessions', {loading: true}); - }, - setAudits: function(audits) { + } + setAudits(audits) { BrowserStore.setItem('audits', audits); - }, - getAudits: function() { + } + getAudits() { return BrowserStore.getItem('audits', {loading: true}); - }, - setTeams: function(teams) { + } + setTeams(teams) { BrowserStore.setItem('teams', teams); - }, - getTeams: function() { + } + getTeams() { return BrowserStore.getItem('teams', []); - }, - getCurrentMentionKeys: function() { + } + getCurrentMentionKeys() { var user = this.getCurrentUser(); var keys = []; @@ -205,74 +255,76 @@ var UserStore = assign({}, EventEmitter.prototype, { } return keys; - }, - getLastVersion: function() { + } + getLastVersion() { return BrowserStore.getItem('last_version', ''); - }, - setLastVersion: function(version) { + } + setLastVersion(version) { BrowserStore.setItem('last_version', version); - }, - setStatuses: function(statuses) { - this._setStatuses(statuses); + } + setStatuses(statuses) { + this.pSetStatuses(statuses); this.emitStatusesChange(); - }, - _setStatuses: function(statuses) { + } + pSetStatuses(statuses) { BrowserStore.setItem('statuses', statuses); - }, - setStatus: function(userId, status) { + } + setStatus(userId, status) { var statuses = this.getStatuses(); statuses[userId] = status; - this._setStatuses(statuses); + this.pSetStatuses(statuses); this.emitStatusesChange(); - }, - getStatuses: function() { + } + getStatuses() { return BrowserStore.getItem('statuses', {}); - }, - getStatus: function(id) { + } + getStatus(id) { return this.getStatuses()[id]; } -}); +} + +var UserStore = new UserStoreClass(); +UserStore.setMaxListeners(0); -UserStore.dispatchToken = AppDispatcher.register(function(payload) { +UserStore.dispatchToken = AppDispatcher.register(function registry(payload) { var action = payload.action; switch (action.type) { - case ActionTypes.RECIEVED_PROFILES: - for (var id in action.profiles) { - // profiles can have incomplete data, so don't overwrite current user - if (id === UserStore.getCurrentId()) { - continue; - } - var profile = action.profiles[id]; - UserStore.saveProfile(profile); - UserStore.emitChange(profile.id); + case ActionTypes.RECIEVED_PROFILES: + for (var id in action.profiles) { + // profiles can have incomplete data, so don't overwrite current user + if (id === UserStore.getCurrentId()) { + continue; } - break; - case ActionTypes.RECIEVED_ME: - UserStore.setCurrentUser(action.me); - UserStore.emitChange(action.me.id); - break; - case ActionTypes.RECIEVED_SESSIONS: - UserStore.setSessions(action.sessions); - UserStore.emitSessionsChange(); - break; - case ActionTypes.RECIEVED_AUDITS: - UserStore.setAudits(action.audits); - UserStore.emitAuditsChange(); - break; - case ActionTypes.RECIEVED_TEAMS: - UserStore.setTeams(action.teams); - UserStore.emitTeamsChange(); - break; - case ActionTypes.RECIEVED_STATUSES: - UserStore._setStatuses(action.statuses); - UserStore.emitStatusesChange(); - break; + var profile = action.profiles[id]; + UserStore.saveProfile(profile); + UserStore.emitChange(profile.id); + } + break; + case ActionTypes.RECIEVED_ME: + UserStore.setCurrentUser(action.me); + UserStore.emitChange(action.me.id); + break; + case ActionTypes.RECIEVED_SESSIONS: + UserStore.setSessions(action.sessions); + UserStore.emitSessionsChange(); + break; + case ActionTypes.RECIEVED_AUDITS: + UserStore.setAudits(action.audits); + UserStore.emitAuditsChange(); + break; + case ActionTypes.RECIEVED_TEAMS: + UserStore.setTeams(action.teams); + UserStore.emitTeamsChange(); + break; + case ActionTypes.RECIEVED_STATUSES: + UserStore.pSetStatuses(action.statuses); + UserStore.emitStatusesChange(); + break; - default: + default: } }); -UserStore.setMaxListeners(0); global.window.UserStore = UserStore; -module.exports = UserStore; +export default UserStore; |