summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/react/components/activity_log_modal.jsx179
-rw-r--r--web/react/components/channel_header.jsx436
-rw-r--r--web/react/components/channel_members.jsx280
-rw-r--r--web/react/components/create_comment.jsx213
-rw-r--r--web/react/components/create_post.jsx267
-rw-r--r--web/react/components/delete_channel_modal.jsx127
-rw-r--r--web/react/components/edit_channel_modal.jsx181
-rw-r--r--web/react/components/file_upload_overlay.jsx14
-rw-r--r--web/react/components/login.jsx46
-rw-r--r--web/react/components/member_list_team.jsx131
-rw-r--r--web/react/components/member_list_team_item.jsx203
-rw-r--r--web/react/components/popover_list_members.jsx80
-rw-r--r--web/react/components/post_body.jsx127
-rw-r--r--web/react/components/rename_channel_modal.jsx286
-rw-r--r--web/react/components/rhs_header_post.jsx8
-rw-r--r--web/react/components/setting_item_min.jsx35
-rw-r--r--web/react/components/settings_sidebar.jsx60
-rw-r--r--web/react/components/signup_team.jsx22
-rw-r--r--web/react/components/team_general_tab.jsx37
-rw-r--r--web/react/components/team_settings_modal.jsx116
-rw-r--r--web/react/components/team_signup_email_item.jsx60
-rw-r--r--web/react/components/team_signup_url_page.jsx111
-rw-r--r--web/react/components/team_signup_with_email.jsx18
-rw-r--r--web/react/components/textbox.jsx282
-rw-r--r--web/react/components/user_settings_general.jsx2
25 files changed, 2087 insertions, 1234 deletions
diff --git a/web/react/components/activity_log_modal.jsx b/web/react/components/activity_log_modal.jsx
index 1192a72bc..2a83b3c40 100644
--- a/web/react/components/activity_log_modal.jsx
+++ b/web/react/components/activity_log_modal.jsx
@@ -1,102 +1,104 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var UserStore = require('../stores/user_store.jsx');
-var Client = require('../utils/client.jsx');
-var AsyncClient = require('../utils/async_client.jsx');
-var LoadingScreen = require('./loading_screen.jsx');
-var utils = require('../utils/utils.jsx');
+const UserStore = require('../stores/user_store.jsx');
+const Client = require('../utils/client.jsx');
+const AsyncClient = require('../utils/async_client.jsx');
+const LoadingScreen = require('./loading_screen.jsx');
+const Utils = require('../utils/utils.jsx');
-function getStateFromStoresForSessions() {
- return {
- sessions: UserStore.getSessions(),
- serverError: null,
- clientError: null
- };
-}
+export default class ActivityLogModal extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.submitRevoke = this.submitRevoke.bind(this);
+ this.onListenerChange = this.onListenerChange.bind(this);
+ this.handleMoreInfo = this.handleMoreInfo.bind(this);
-module.exports = React.createClass({
- displayName: 'ActivityLogModal',
- submitRevoke: function(altId) {
+ this.state = this.getStateFromStores();
+ this.state.moreInfo = [];
+ }
+ getStateFromStores() {
+ return {
+ sessions: UserStore.getSessions(),
+ serverError: null,
+ clientError: null
+ };
+ }
+ submitRevoke(altId) {
Client.revokeSession(altId,
- function(data) {
+ function handleRevokeSuccess() {
AsyncClient.getSessions();
- }.bind(this),
- function(err) {
- var state = getStateFromStoresForSessions();
+ },
+ function handleRevokeError(err) {
+ let state = this.getStateFromStores();
state.serverError = err;
this.setState(state);
}.bind(this)
);
- },
- componentDidMount: function() {
+ }
+ componentDidMount() {
UserStore.addSessionsChangeListener(this.onListenerChange);
- $(this.refs.modal.getDOMNode()).on('shown.bs.modal', function (e) {
+ $(React.findDOMNode(this.refs.modal)).on('shown.bs.modal', function handleShow() {
AsyncClient.getSessions();
});
- var self = this;
- $(this.refs.modal.getDOMNode()).on('hidden.bs.modal', function(e) {
+ $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function handleHide() {
$('#user_settings').modal('show');
- self.setState({moreInfo: []});
- });
- },
- componentWillUnmount: function() {
+ this.setState({moreInfo: []});
+ }.bind(this));
+ }
+ componentWillUnmount() {
UserStore.removeSessionsChangeListener(this.onListenerChange);
- },
- onListenerChange: function() {
- var newState = getStateFromStoresForSessions();
- if (!utils.areStatesEqual(newState.sessions, this.state.sessions)) {
+ }
+ onListenerChange() {
+ const newState = this.getStateFromStores();
+ if (!Utils.areStatesEqual(newState.sessions, this.state.sessions)) {
this.setState(newState);
}
- },
- handleMoreInfo: function(index) {
- var newMoreInfo = this.state.moreInfo;
+ }
+ handleMoreInfo(index) {
+ let newMoreInfo = this.state.moreInfo;
newMoreInfo[index] = true;
this.setState({moreInfo: newMoreInfo});
- },
- getInitialState: function() {
- var initialState = getStateFromStoresForSessions();
- initialState.moreInfo = [];
- return initialState;
- },
- render: function() {
- var activityList = [];
- var serverError = this.state.serverError;
-
- // Squash any false-y value for server error into null
- if (!serverError) {
- serverError = null;
- }
+ }
+ render() {
+ let activityList = [];
- for (var i = 0; i < this.state.sessions.length; i++) {
- var currentSession = this.state.sessions[i];
- var lastAccessTime = new Date(currentSession.last_activity_at);
- var firstAccessTime = new Date(currentSession.create_at);
- var devicePicture = '';
+ for (let i = 0; i < this.state.sessions.length; i++) {
+ const currentSession = this.state.sessions[i];
+ const lastAccessTime = new Date(currentSession.last_activity_at);
+ const firstAccessTime = new Date(currentSession.create_at);
+ let devicePicture = '';
if (currentSession.props.platform === 'Windows') {
devicePicture = 'fa fa-windows';
- }
- else if (currentSession.props.platform === 'Macintosh' || currentSession.props.platform === 'iPhone') {
+ } else if (currentSession.props.platform === 'Macintosh' || currentSession.props.platform === 'iPhone') {
devicePicture = 'fa fa-apple';
- }
- else if (currentSession.props.platform === 'Linux') {
+ } else if (currentSession.props.platform === 'Linux') {
devicePicture = 'fa fa-linux';
}
- var moreInfo;
+ let moreInfo;
if (this.state.moreInfo[i]) {
moreInfo = (
<div>
- <div>{'First time active: ' + firstAccessTime.toDateString() + ', ' + lastAccessTime.toLocaleTimeString()}</div>
- <div>{'OS: ' + currentSession.props.os}</div>
- <div>{'Browser: ' + currentSession.props.browser}</div>
- <div>{'Session ID: ' + currentSession.alt_id}</div>
+ <div>{`First time active: ${firstAccessTime.toDateString()}, ${lastAccessTime.toLocaleTimeString()}`}</div>
+ <div>{`OS: ${currentSession.props.os}`}</div>
+ <div>{`Browser: ${currentSession.props.browser}`}</div>
+ <div>{`Session ID: ${currentSession.alt_id}`}</div>
</div>
);
} else {
- moreInfo = (<a className='theme' href='#' onClick={this.handleMoreInfo.bind(this, i)}>More info</a>);
+ moreInfo = (
+ <a
+ className='theme'
+ href='#'
+ onClick={this.handleMoreInfo.bind(this, i)}
+ >
+ More info
+ </a>
+ );
}
activityList[i] = (
@@ -104,33 +106,62 @@ module.exports = React.createClass({
<div className='activity-log__report'>
<div className='report__platform'><i className={devicePicture} />{currentSession.props.platform}</div>
<div className='report__info'>
- <div>{'Last activity: ' + lastAccessTime.toDateString() + ', ' + lastAccessTime.toLocaleTimeString()}</div>
+ <div>{`Last activity: ${lastAccessTime.toDateString()}, ${lastAccessTime.toLocaleTimeString()}`}</div>
{moreInfo}
</div>
</div>
- <div className='activity-log__action'><button onClick={this.submitRevoke.bind(this, currentSession.alt_id)} className='btn btn-primary'>Logout</button></div>
+ <div className='activity-log__action'>
+ <button
+ onClick={this.submitRevoke.bind(this, currentSession.alt_id)}
+ className='btn btn-primary'
+ >
+ Logout
+ </button>
+ </div>
</div>
);
}
- var content;
+ let content;
if (this.state.sessions.loading) {
- content = (<LoadingScreen />);
+ content = <LoadingScreen />;
} else {
- content = (<form role='form'>{activityList}</form>);
+ content = <form role='form'>{activityList}</form>;
}
return (
<div>
- <div className='modal fade' ref='modal' id='activity-log' tabIndex='-1' role='dialog' aria-hidden='true'>
+ <div
+ className='modal fade'
+ ref='modal'
+ id='activity-log'
+ tabIndex='-1'
+ role='dialog'
+ aria-hidden='true'
+ >
<div className='modal-dialog modal-lg'>
<div className='modal-content'>
<div className='modal-header'>
- <button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button>
- <h4 className='modal-title' id='myModalLabel'>Active Sessions</h4>
+ <button
+ type='button'
+ className='close'
+ data-dismiss='modal'
+ aria-label='Close'
+ >
+ <span aria-hidden='true'>&times;</span>
+ </button>
+ <h4
+ className='modal-title'
+ id='myModalLabel'
+ >
+ Active Sessions
+ </h4>
</div>
<p className='session-help-text'>Sessions are created when you log in with your email and password to a new browser on a device. Sessions let you use Mattermost for up to 30 days without having to log in again. If you want to log out sooner, use the 'Logout' button below to end a session.</p>
- <div ref='modalBody' className='modal-body'>
+ <div
+ ref='modalBody'
+ className='modal-body'
+ >
{content}
</div>
</div>
@@ -139,4 +170,4 @@ module.exports = React.createClass({
</div>
);
}
-});
+}
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index 0254d0e82..87b9cab04 100644
--- a/web/react/components/channel_header.jsx
+++ b/web/react/components/channel_header.jsx
@@ -1,139 +1,85 @@
// 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');
-var PostStore = require('../stores/post_store.jsx');
-var SocketStore = require('../stores/socket_store.jsx');
-var NavbarSearchBox = require('./search_bar.jsx');
-var AsyncClient = require('../utils/async_client.jsx');
-var Client = require('../utils/client.jsx');
-var utils = require('../utils/utils.jsx');
-var MessageWrapper = require('./message_wrapper.jsx');
-
-var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
-var Constants = require('../utils/constants.jsx');
-var ActionTypes = Constants.ActionTypes;
-
-var PopoverListMembers = React.createClass({
- componentDidMount: function() {
- var originalLeave = $.fn.popover.Constructor.prototype.leave;
- $.fn.popover.Constructor.prototype.leave = function(obj) {
- var selfObj;
- if (obj instanceof this.constructor) {
- selfObj = obj;
- } else {
- selfObj = $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type);
- }
- originalLeave.call(this, obj);
-
- if (obj.currentTarget && selfObj.$tip) {
- selfObj.$tip.one('mouseenter', function() {
- clearTimeout(selfObj.timeout);
- selfObj.$tip.one('mouseleave', function() {
- $.fn.popover.Constructor.prototype.leave.call(selfObj, selfObj);
- });
- });
- }
- };
+const ChannelStore = require('../stores/channel_store.jsx');
+const UserStore = require('../stores/user_store.jsx');
+const PostStore = require('../stores/post_store.jsx');
+const SocketStore = require('../stores/socket_store.jsx');
+const NavbarSearchBox = require('./search_bar.jsx');
+const AsyncClient = require('../utils/async_client.jsx');
+const Client = require('../utils/client.jsx');
+const Utils = require('../utils/utils.jsx');
+const MessageWrapper = require('./message_wrapper.jsx');
+const PopoverListMembers = require('./popover_list_members.jsx');
- $('#member_popover').popover({placement: 'bottom', trigger: 'click', html: true});
- $('body').on('click', function(e) {
- if ($(e.target.parentNode.parentNode)[0] !== $('#member_popover')[0] && $(e.target).parents('.popover.in').length === 0) {
- $('#member_popover').popover('hide');
- }
- });
- },
-
- render: function() {
- var popoverHtml = '';
- var members = this.props.members;
- var count;
- if (members.length > 20) {
- count = '20+';
- } else {
- count = members.length || '-';
- }
+const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
+const Constants = require('../utils/constants.jsx');
+const ActionTypes = Constants.ActionTypes;
- if (members) {
- members.sort(function(a, b) {
- return a.username.localeCompare(b.username);
- });
+export default class ChannelHeader extends React.Component {
+ constructor(props) {
+ super(props);
- members.forEach(function(m) {
- popoverHtml += "<div class='text--nowrap'>" + m.username + '</div>';
- });
- }
+ this.onListenerChange = this.onListenerChange.bind(this);
+ this.onSocketChange = this.onSocketChange.bind(this);
+ this.handleLeave = this.handleLeave.bind(this);
+ this.searchMentions = this.searchMentions.bind(this);
- return (
- <div id='member_popover' data-toggle='popover' data-content={popoverHtml} data-original-title='Members' >
- <div id='member_tooltip' data-placement='left' data-toggle='tooltip' title='View Channel Members'>
- {count} <span className='glyphicon glyphicon-user' aria-hidden='true'></span>
- </div>
- </div>
- );
+ this.state = this.getStateFromStores();
}
-});
-
-function getStateFromStores() {
- return {
- channel: ChannelStore.getCurrent(),
- memberChannel: ChannelStore.getCurrentMember(),
- memberTeam: UserStore.getCurrentUser(),
- users: ChannelStore.getCurrentExtraInfo().members,
- searchVisible: PostStore.getSearchResults() != null
- };
-}
-
-module.exports = React.createClass({
- displayName: 'ChannelHeader',
- componentDidMount: function() {
+ getStateFromStores() {
+ return {
+ channel: ChannelStore.getCurrent(),
+ memberChannel: ChannelStore.getCurrentMember(),
+ memberTeam: UserStore.getCurrentUser(),
+ users: ChannelStore.getCurrentExtraInfo().members,
+ searchVisible: PostStore.getSearchResults() !== null
+ };
+ }
+ componentDidMount() {
ChannelStore.addChangeListener(this.onListenerChange);
ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
PostStore.addSearchChangeListener(this.onListenerChange);
UserStore.addChangeListener(this.onListenerChange);
SocketStore.addChangeListener(this.onSocketChange);
- },
- componentWillUnmount: function() {
+ }
+ componentWillUnmount() {
ChannelStore.removeChangeListener(this.onListenerChange);
ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
PostStore.removeSearchChangeListener(this.onListenerChange);
UserStore.addChangeListener(this.onListenerChange);
- },
- onListenerChange: function() {
- var newState = getStateFromStores();
- if (!utils.areStatesEqual(newState, this.state)) {
+ }
+ onListenerChange() {
+ const newState = this.getStateFromStores();
+ if (!Utils.areStatesEqual(newState, this.state)) {
this.setState(newState);
}
$('.channel-header__info .description').popover({placement: 'bottom', trigger: 'hover click', html: true, delay: {show: 500, hide: 500}});
- },
- onSocketChange: function(msg) {
+ }
+ onSocketChange(msg) {
if (msg.action === 'new_user') {
AsyncClient.getChannelExtraInfo(true);
}
- },
- getInitialState: function() {
- return getStateFromStores();
- },
- handleLeave: function() {
+ }
+ handleLeave() {
Client.leaveChannel(this.state.channel.id,
- function() {
- var townsquare = ChannelStore.getByName('town-square');
- utils.switchChannel(townsquare);
+ function handleLeaveSuccess() {
+ const townsquare = ChannelStore.getByName('town-square');
+ Utils.switchChannel(townsquare);
},
- function(err) {
+ function handleLeaveError(err) {
AsyncClient.dispatchError(err, 'handleLeave');
}
);
- },
- searchMentions: function(e) {
+ }
+ searchMentions(e) {
e.preventDefault();
- var user = UserStore.getCurrentUser();
+ const user = UserStore.getCurrentUser();
- var terms = '';
+ let terms = '';
if (user.notify_props && user.notify_props.mention_keys) {
- var termKeys = UserStore.getCurrentMentionKeys();
+ let termKeys = UserStore.getCurrentMentionKeys();
if (user.notify_props.all === 'true' && termKeys.indexOf('@all') !== -1) {
termKeys.splice(termKeys.indexOf('@all'), 1);
}
@@ -149,23 +95,23 @@ module.exports = React.createClass({
do_search: true,
is_mention_search: true
});
- },
- render: function() {
- if (this.state.channel == null) {
+ }
+ render() {
+ if (this.state.channel === null) {
return null;
}
- var channel = this.state.channel;
- var description = utils.textToJsx(channel.description, {singleline: true, noMentionHighlight: true});
- var popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
- var channelTitle = channel.display_name;
- var currentId = UserStore.getCurrentId();
- var isAdmin = this.state.memberChannel.roles.indexOf('admin') > -1 || this.state.memberTeam.roles.indexOf('admin') > -1;
- var isDirect = (this.state.channel.type === 'D');
+ const channel = this.state.channel;
+ const description = Utils.textToJsx(channel.description, {singleline: true, noMentionHighlight: true});
+ const popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
+ let channelTitle = channel.display_name;
+ const currentId = UserStore.getCurrentId();
+ const isAdmin = this.state.memberChannel.roles.indexOf('admin') > -1 || this.state.memberTeam.roles.indexOf('admin') > -1;
+ const isDirect = (this.state.channel.type === 'D');
if (isDirect) {
if (this.state.users.length > 1) {
- var contact;
+ let contact;
if (this.state.users[0].id === currentId) {
contact = this.state.users[1];
} else {
@@ -175,64 +121,244 @@ module.exports = React.createClass({
}
}
- var channelTerm = 'Channel';
+ let channelTerm = 'Channel';
if (channel.type === 'P') {
channelTerm = 'Group';
}
+ let dropdownContents = [];
+ if (!isDirect) {
+ dropdownContents.push(
+ <li
+ key='view_info'
+ role='presentation'
+ >
+ <a
+ role='menuitem'
+ data-toggle='modal'
+ data-target='#channel_info'
+ data-channelid={channel.id}
+ href='#'
+ >
+ View Info
+ </a>
+ </li>
+ );
+
+ if (!ChannelStore.isDefault(channel)) {
+ dropdownContents.push(
+ <li
+ key='add_members'
+ role='presentation'
+ >
+ <a
+ role='menuitem'
+ data-toggle='modal'
+ data-target='#channel_invite'
+ href='#'
+ >
+ Add Members
+ </a>
+ </li>
+ );
+
+ if (isAdmin) {
+ dropdownContents.push(
+ <li
+ key='manage_members'
+ role='presentation'
+ >
+ <a
+ role='menuitem'
+ data-toggle='modal'
+ data-target='#channel_members'
+ href='#'
+ >
+ Manage Members
+ </a>
+ </li>
+ );
+ }
+ }
+
+ dropdownContents.push(
+ <li
+ key='set_channel_description'
+ role='presentation'
+ >
+ <a
+ role='menuitem'
+ href='#'
+ data-toggle='modal'
+ data-target='#edit_channel'
+ data-desc={channel.description}
+ data-title={channel.display_name}
+ data-channelid={channel.id}
+ >
+ Set {channelTerm} Description...
+ </a>
+ </li>
+ );
+ dropdownContents.push(
+ <li
+ key='notification_preferences'
+ role='presentation'
+ >
+ <a
+ role='menuitem'
+ href='#'
+ data-toggle='modal'
+ data-target='#channel_notifications'
+ data-title={channel.display_name}
+ data-channelid={channel.id}
+ >
+ Notification Preferences
+ </a>
+ </li>
+ );
+
+ if (!ChannelStore.isDefault(channel)) {
+ if (isAdmin) {
+ dropdownContents.push(
+ <li
+ key='rename_channel'
+ role='presentation'
+ >
+ <a
+ role='menuitem'
+ href='#'
+ data-toggle='modal'
+ data-target='#rename_channel'
+ data-display={channel.display_name}
+ data-name={channel.name}
+ data-channelid={channel.id}
+ >
+ Rename {channelTerm}...
+ </a>
+ </li>
+ );
+ dropdownContents.push(
+ <li
+ key='delete_channel'
+ role='presentation'
+ >
+ <a
+ role='menuitem'
+ href='#'
+ data-toggle='modal'
+ data-target='#delete_channel'
+ data-title={channel.display_name}
+ data-channelid={channel.id}
+ >
+ Delete {channelTerm}...
+ </a>
+ </li>
+ );
+ }
+
+ dropdownContents.push(
+ <li
+ key='leave_channel'
+ role='presentation'
+ >
+ <a
+ role='menuitem'
+ href='#'
+ onClick={this.handleLeave}
+ >
+ Leave {channelTerm}
+ </a>
+ </li>
+ );
+ }
+ } else {
+ dropdownContents.push(
+ <li
+ key='edit_description_direct'
+ role='presentation'
+ >
+ <a
+ role='menuitem'
+ href='#'
+ data-toggle='modal'
+ data-target='#edit_channel'
+ data-desc={channel.description}
+ data-title={channel.display_name}
+ data-channelid={channel.id}
+ >
+ Set Channel Description...
+ </a>
+ </li>
+ );
+ }
+
return (
<table className='channel-header alt'>
<tr>
<th>
<div className='channel-header__info'>
<div className='dropdown'>
- <a href='#' className='dropdown-toggle theme' type='button' id='channel_header_dropdown' data-toggle='dropdown' aria-expanded='true'>
+ <a
+ href='#'
+ className='dropdown-toggle theme'
+ type='button'
+ id='channel_header_dropdown'
+ data-toggle='dropdown'
+ aria-expanded='true'
+ >
<strong className='heading'>{channelTitle} </strong>
- <span className='glyphicon glyphicon-chevron-down header-dropdown__icon'></span>
+ <span className='glyphicon glyphicon-chevron-down header-dropdown__icon' />
</a>
- {!isDirect ?
- <ul className='dropdown-menu' role='menu' aria-labelledby='channel_header_dropdown'>
- <li role='presentation'><a role='menuitem' data-toggle='modal' data-target='#channel_info' data-channelid={channel.id} href='#'>View Info</a></li>
- {!ChannelStore.isDefault(channel) ?
- <li role='presentation'><a role='menuitem' data-toggle='modal' data-target='#channel_invite' href='#'>Add Members</a></li>
- : null
- }
- {isAdmin && !ChannelStore.isDefault(channel) ?
- <li role='presentation'><a role='menuitem' data-toggle='modal' data-target='#channel_members' href='#'>Manage Members</a></li>
- : null
- }
- <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#edit_channel' data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set {channelTerm} Description...</a></li>
- <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#channel_notifications' data-title={channel.display_name} data-channelid={channel.id}>Notification Preferences</a></li>
- {isAdmin && !ChannelStore.isDefault(channel) ?
- <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#rename_channel' data-display={channel.display_name} data-name={channel.name} data-channelid={channel.id}>Rename {channelTerm}...</a></li>
- : null
- }
- {isAdmin && !ChannelStore.isDefault(channel) ?
- <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#delete_channel' data-title={channel.display_name} data-channelid={channel.id}>Delete {channelTerm}...</a></li>
- : null
- }
- {!ChannelStore.isDefault(channel) ?
- <li role='presentation'><a role='menuitem' href='#' onClick={this.handleLeave}>Leave {channelTerm}</a></li>
- : null
- }
- </ul>
- :
- <ul className='dropdown-menu' role='menu' aria-labelledby='channel_header_dropdown'>
- <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#edit_channel' data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set Channel Description...</a></li>
+ <ul
+ className='dropdown-menu'
+ role='menu'
+ aria-labelledby='channel_header_dropdown'
+ >
+ {dropdownContents}
</ul>
- }
</div>
- <div data-toggle='popover' data-content={popoverContent} className='description'>{description}</div>
+ <div
+ data-toggle='popover'
+ data-content={popoverContent}
+ className='description'
+ >
+ {description}
+ </div>
</div>
</th>
- <th><PopoverListMembers members={this.state.users} channelId={channel.id} /></th>
+ <th>
+ <PopoverListMembers
+ members={this.state.users}
+ channelId={channel.id}
+ />
+ </th>
<th className='search-bar__container'><NavbarSearchBox /></th>
<th>
<div className='dropdown channel-header__links'>
- <a href='#' className='dropdown-toggle theme' type='button' id='channel_header_right_dropdown' data-toggle='dropdown' aria-expanded='true'>
- <span dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}} /> </a>
- <ul className='dropdown-menu dropdown-menu-right' role='menu' aria-labelledby='channel_header_right_dropdown'>
- <li role='presentation'><a role='menuitem' href='#' onClick={this.searchMentions}>Recent Mentions</a></li>
+ <a
+ href='#'
+ className='dropdown-toggle theme'
+ type='button'
+ id='channel_header_right_dropdown'
+ data-toggle='dropdown'
+ aria-expanded='true'
+ >
+ <span dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}} />
+ </a>
+ <ul
+ className='dropdown-menu dropdown-menu-right'
+ role='menu'
+ aria-labelledby='channel_header_right_dropdown'
+ >
+ <li role='presentation'>
+ <a
+ role='menuitem'
+ href='#'
+ onClick={this.searchMentions}
+ >
+ Recent Mentions
+ </a>
+ </li>
</ul>
</div>
</th>
@@ -240,4 +366,4 @@ module.exports = React.createClass({
</table>
);
}
-});
+}
diff --git a/web/react/components/channel_members.jsx b/web/react/components/channel_members.jsx
index db4bec400..04fa2c7a2 100644
--- a/web/react/components/channel_members.jsx
+++ b/web/react/components/channel_members.jsx
@@ -1,154 +1,200 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var UserStore = require('../stores/user_store.jsx');
-var ChannelStore = require('../stores/channel_store.jsx');
-var AsyncClient = require('../utils/async_client.jsx');
-var MemberList = require('./member_list.jsx');
-var client = require('../utils/client.jsx');
-var utils = require('../utils/utils.jsx');
-
-function getStateFromStores() {
- var users = UserStore.getActiveOnlyProfiles();
- var member_list = ChannelStore.getCurrentExtraInfo().members;
-
- var nonmember_list = [];
- for (var id in users) {
- var found = false;
- for (var i = 0; i < member_list.length; i++) {
- if (member_list[i].id === id) {
- found = true;
- break;
+const UserStore = require('../stores/user_store.jsx');
+const ChannelStore = require('../stores/channel_store.jsx');
+const AsyncClient = require('../utils/async_client.jsx');
+const MemberList = require('./member_list.jsx');
+const Client = require('../utils/client.jsx');
+const Utils = require('../utils/utils.jsx');
+
+export default class ChannelMembers extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.getStateFromStores = this.getStateFromStores.bind(this);
+ this.onChange = this.onChange.bind(this);
+ this.handleRemove = this.handleRemove.bind(this);
+
+ this.state = this.getStateFromStores();
+ }
+ getStateFromStores() {
+ const users = UserStore.getActiveOnlyProfiles();
+ let memberList = ChannelStore.getCurrentExtraInfo().members;
+
+ let nonmemberList = [];
+ for (let id in users) {
+ if (users.hasOwnProperty(id)) {
+ let found = false;
+ for (let i = 0; i < memberList.length; i++) {
+ if (memberList[i].id === id) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ nonmemberList.push(users[id]);
+ }
}
}
- if (!found) {
- nonmember_list.push(users[id]);
+
+ function compareByUsername(a, b) {
+ if (a.username < b.username) {
+ return -1;
+ } else if (a.username > b.username) {
+ return 1;
+ }
+
+ return 0;
}
- }
- member_list.sort(function(a,b) {
- if (a.username < b.username) return -1;
- if (a.username > b.username) return 1;
- return 0;
- });
-
- nonmember_list.sort(function(a,b) {
- if (a.username < b.username) return -1;
- if (a.username > b.username) return 1;
- return 0;
- });
-
- var channel_name = ChannelStore.getCurrent() ? ChannelStore.getCurrent().display_name : "";
-
- return {
- nonmember_list: nonmember_list,
- member_list: member_list,
- channel_name: channel_name
- };
-}
+ memberList.sort(compareByUsername);
+ nonmemberList.sort(compareByUsername);
+
+ const channel = ChannelStore.getCurrent();
+ let channelName = '';
+ if (channel) {
+ channelName = channel.display_name;
+ }
-module.exports = React.createClass({
- componentDidMount: function() {
- ChannelStore.addExtraInfoChangeListener(this._onChange);
- ChannelStore.addChangeListener(this._onChange);
- var self = this;
- $(this.refs.modal.getDOMNode()).on('hidden.bs.modal', function(e) {
- self.setState({ render_members: false });
- });
-
- $(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
- self.setState({ render_members: true });
- });
- },
- componentWillUnmount: function() {
- ChannelStore.removeExtraInfoChangeListener(this._onChange);
- ChannelStore.removeChangeListener(this._onChange);
- },
- _onChange: function() {
- var new_state = getStateFromStores();
- if (!utils.areStatesEqual(this.state, new_state)) {
- this.setState(new_state);
+ return {
+ nonmemberList: nonmemberList,
+ memberList: memberList,
+ channelName: channelName
+ };
+ }
+ componentDidMount() {
+ ChannelStore.addExtraInfoChangeListener(this.onChange);
+ ChannelStore.addChangeListener(this.onChange);
+ $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function handleHide() {
+ this.setState({renderMembers: false});
+ }.bind(this));
+
+ $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function handleShow() {
+ this.setState({renderMembers: true});
+ }.bind(this));
+ }
+ componentWillUnmount() {
+ ChannelStore.removeExtraInfoChangeListener(this.onChange);
+ ChannelStore.removeChangeListener(this.onChange);
+ }
+ onChange() {
+ const newState = this.getStateFromStores();
+ if (!Utils.areStatesEqual(this.state, newState)) {
+ this.setState(newState);
}
- },
- handleRemove: function(user_id) {
+ }
+ handleRemove(userId) {
// Make sure the user is a member of the channel
- var member_list = this.state.member_list;
- var found = false;
- for (var i = 0; i < member_list.length; i++) {
- if (member_list[i].id === user_id) {
+ let memberList = this.state.memberList;
+ let found = false;
+ for (let i = 0; i < memberList.length; i++) {
+ if (memberList[i].id === userId) {
found = true;
break;
}
}
- if (!found) { return };
+ if (!found) {
+ return;
+ }
- var data = {};
- data['user_id'] = user_id;
+ let data = {};
+ data.user_id = userId;
- client.removeChannelMember(ChannelStore.getCurrentId(), data,
- function(data) {
- var old_member;
- for (var i = 0; i < member_list.length; i++) {
- if (user_id === member_list[i].id) {
- old_member = member_list[i];
- member_list.splice(i, 1);
+ Client.removeChannelMember(ChannelStore.getCurrentId(), data,
+ function handleRemoveSuccess() {
+ let oldMember;
+ for (let i = 0; i < memberList.length; i++) {
+ if (userId === memberList[i].id) {
+ oldMember = memberList[i];
+ memberList.splice(i, 1);
break;
}
}
- var nonmember_list = this.state.nonmember_list;
- if (old_member) {
- nonmember_list.push(old_member);
+ let nonmemberList = this.state.nonmemberList;
+ if (oldMember) {
+ nonmemberList.push(oldMember);
}
- this.setState({ member_list: member_list, nonmember_list: nonmember_list });
+ this.setState({memberList: memberList, nonmemberList: nonmemberList});
AsyncClient.getChannelExtraInfo(true);
}.bind(this),
- function(err) {
- this.setState({ invite_error: err.message });
+ function handleRemoveError(err) {
+ this.setState({inviteError: err.message});
}.bind(this)
);
- },
- getInitialState: function() {
- return getStateFromStores();
- },
- render: function() {
- var currentMember = ChannelStore.getCurrentMember();
- var isAdmin = false;
+ }
+ render() {
+ const currentMember = ChannelStore.getCurrentMember();
+ let isAdmin = false;
if (currentMember) {
- isAdmin = currentMember.roles.indexOf("admin") > -1 || UserStore.getCurrentUser().roles.indexOf("admin") > -1;
+ isAdmin = currentMember.roles.indexOf('admin') > -1 || UserStore.getCurrentUser().roles.indexOf('admin') > -1;
+ }
+
+ var memberList = null;
+ if (this.state.renderMembers) {
+ memberList = (
+ <MemberList
+ memberList={this.state.memberList}
+ isAdmin={isAdmin}
+ handleRemove={this.handleRemove}
+ />
+ );
}
return (
- <div className="modal fade" ref="modal" id="channel_members" 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" aria-label="Close"><span aria-hidden="true">×</span></button>
- <h4 className="modal-title"><span className="name">{this.state.channel_name}</span> Members</h4>
- <a className="btn btn-md btn-primary" data-toggle="modal" data-target="#channel_invite"><i className="glyphicon glyphicon-envelope"/> Add New Members</a>
- </div>
- <div ref="modalBody" className="modal-body">
- <div className="col-sm-12">
- <div className="team-member-list">
- { this.state.render_members ?
- <MemberList
- memberList={this.state.member_list}
- isAdmin={isAdmin}
- handleRemove={this.handleRemove}
- />
- : "" }
+ <div
+ className='modal fade'
+ ref='modal'
+ id='channel_members'
+ 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'
+ aria-label='Close'
+ >
+ <span aria-hidden='true'>×</span>
+ </button>
+ <h4 className='modal-title'><span className='name'>{this.state.channelName}</span> Members</h4>
+ <a
+ className='btn btn-md btn-primary'
+ data-toggle='modal'
+ data-target='#channel_invite'
+ >
+ <i className='glyphicon glyphicon-envelope'/> Add New Members
+ </a>
+ </div>
+ <div
+ ref='modalBody'
+ className='modal-body'
+ >
+ <div className='col-sm-12'>
+ <div className='team-member-list'>
+ {memberList}
+ </div>
</div>
</div>
+ <div className='modal-footer'>
+ <button
+ type='button'
+ className='btn btn-default'
+ data-dismiss='modal'
+ >
+ Close
+ </button>
+ </div>
</div>
- <div className="modal-footer">
- <button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
- </div>
- </div>
- </div>
+ </div>
</div>
-
);
}
-});
+}
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index c2b7e222f..c2fc0dcf3 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -1,24 +1,48 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
-var client = require('../utils/client.jsx');
-var AsyncClient = require('../utils/async_client.jsx');
-var SocketStore = require('../stores/socket_store.jsx');
-var ChannelStore = require('../stores/channel_store.jsx');
-var UserStore = require('../stores/user_store.jsx');
-var PostStore = require('../stores/post_store.jsx');
-var Textbox = require('./textbox.jsx');
-var MsgTyping = require('./msg_typing.jsx');
-var FileUpload = require('./file_upload.jsx');
-var FilePreview = require('./file_preview.jsx');
-var utils = require('../utils/utils.jsx');
-var Constants = require('../utils/constants.jsx');
-var ActionTypes = Constants.ActionTypes;
-
-module.exports = React.createClass({
- lastTime: 0,
- handleSubmit: function(e) {
+const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
+const Client = require('../utils/client.jsx');
+const AsyncClient = require('../utils/async_client.jsx');
+const SocketStore = require('../stores/socket_store.jsx');
+const ChannelStore = require('../stores/channel_store.jsx');
+const UserStore = require('../stores/user_store.jsx');
+const PostStore = require('../stores/post_store.jsx');
+const Textbox = require('./textbox.jsx');
+const MsgTyping = require('./msg_typing.jsx');
+const FileUpload = require('./file_upload.jsx');
+const FilePreview = require('./file_preview.jsx');
+const Utils = require('../utils/utils.jsx');
+const Constants = require('../utils/constants.jsx');
+const ActionTypes = Constants.ActionTypes;
+
+export default class CreateComment extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.lastTime = 0;
+
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.commentMsgKeyPress = this.commentMsgKeyPress.bind(this);
+ this.handleUserInput = this.handleUserInput.bind(this);
+ this.handleUploadStart = this.handleUploadStart.bind(this);
+ this.handleFileUploadComplete = this.handleFileUploadComplete.bind(this);
+ this.handleUploadError = this.handleUploadError.bind(this);
+ this.removePreview = this.removePreview.bind(this);
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.getFileCount = this.getFileCount.bind(this);
+
+ PostStore.clearCommentDraftUploads();
+
+ const draft = PostStore.getCommentDraft(this.props.rootId);
+ this.state = {
+ messageText: draft.message,
+ uploadsInProgress: draft.uploadsInProgress,
+ previews: draft.previews,
+ submitting: false
+ };
+ }
+ handleSubmit(e) {
e.preventDefault();
if (this.state.uploadsInProgress.length > 0) {
@@ -29,7 +53,7 @@ module.exports = React.createClass({
return;
}
- var post = {};
+ let post = {};
post.filenames = [];
post.message = this.state.messageText;
@@ -38,30 +62,30 @@ module.exports = React.createClass({
}
if (post.message.length > Constants.CHARACTER_LIMIT) {
- this.setState({postError: 'Comment length must be less than ' + Constants.CHARACTER_LIMIT + ' characters.'});
+ this.setState({postError: `Comment length must be less than ${Constants.CHARACTER_LIMIT} characters.`});
return;
}
- var user_id = UserStore.getCurrentId();
+ const userId = UserStore.getCurrentId();
post.channel_id = this.props.channelId;
post.root_id = this.props.rootId;
post.parent_id = this.props.rootId;
post.filenames = this.state.previews;
- var time = utils.getTimestamp();
- post.pending_post_id = user_id + ':'+ time;
- post.user_id = user_id;
+ const time = Utils.getTimestamp();
+ post.pending_post_id = `${userId}:${time}`;
+ post.user_id = userId;
post.create_at = time;
PostStore.storePendingPost(post);
PostStore.storeCommentDraft(this.props.rootId, null);
- client.createPost(post, ChannelStore.getCurrent(),
- function(data) {
+ Client.createPost(post, ChannelStore.getCurrent(),
+ function handlePostSuccess(data) {
AsyncClient.getPosts(this.props.channelId);
- var channel = ChannelStore.get(this.props.channelId);
- var member = ChannelStore.getMember(this.props.channelId);
+ const channel = ChannelStore.get(this.props.channelId);
+ let member = ChannelStore.getMember(this.props.channelId);
member.msg_count = channel.total_msg_count;
member.last_viewed_at = Date.now();
ChannelStore.setChannelMember(member);
@@ -71,8 +95,8 @@ module.exports = React.createClass({
post: data
});
}.bind(this),
- function(err) {
- var state = {};
+ function handlePostError(err) {
+ let state = {};
if (err.message === 'Invalid RootId parameter') {
if ($('#post_deleted').length > 0) {
@@ -90,76 +114,76 @@ module.exports = React.createClass({
);
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
- },
- commentMsgKeyPress: function(e) {
+ }
+ commentMsgKeyPress(e) {
if (e.which === 13 && !e.shiftKey && !e.altKey) {
e.preventDefault();
- this.refs.textbox.getDOMNode().blur();
+ React.findDOMNode(this.refs.textbox).blur();
this.handleSubmit(e);
}
- var t = Date.now();
+ const t = Date.now();
if ((t - this.lastTime) > 5000) {
- SocketStore.sendMessage({channel_id: this.props.channelId, action: 'typing', props: {'parent_id': this.props.rootId}});
+ SocketStore.sendMessage({channel_id: this.props.channelId, action: 'typing', props: {parent_id: this.props.rootId}});
this.lastTime = t;
}
- },
- handleUserInput: function(messageText) {
- var draft = PostStore.getCommentDraft(this.props.rootId);
+ }
+ handleUserInput(messageText) {
+ let draft = PostStore.getCommentDraft(this.props.rootId);
draft.message = messageText;
PostStore.storeCommentDraft(this.props.rootId, draft);
$('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
$('.post-right__scroll').perfectScrollbar('update');
this.setState({messageText: messageText});
- },
- handleUploadStart: function(clientIds, channelId) {
- var draft = PostStore.getCommentDraft(this.props.rootId);
+ }
+ handleUploadStart(clientIds) {
+ let draft = PostStore.getCommentDraft(this.props.rootId);
- draft['uploadsInProgress'] = draft['uploadsInProgress'].concat(clientIds);
+ draft.uploadsInProgress = draft.uploadsInProgress.concat(clientIds);
PostStore.storeCommentDraft(this.props.rootId, draft);
- this.setState({uploadsInProgress: draft['uploadsInProgress']});
- },
- handleFileUploadComplete: function(filenames, clientIds, channelId) {
- var draft = PostStore.getCommentDraft(this.props.rootId);
+ this.setState({uploadsInProgress: draft.uploadsInProgress});
+ }
+ handleFileUploadComplete(filenames, clientIds) {
+ let draft = PostStore.getCommentDraft(this.props.rootId);
// remove each finished file from uploads
- for (var i = 0; i < clientIds.length; i++) {
- var index = draft['uploadsInProgress'].indexOf(clientIds[i]);
+ for (let i = 0; i < clientIds.length; i++) {
+ const index = draft.uploadsInProgress.indexOf(clientIds[i]);
if (index !== -1) {
- draft['uploadsInProgress'].splice(index, 1);
+ draft.uploadsInProgress.splice(index, 1);
}
}
- draft['previews'] = draft['previews'].concat(filenames);
+ draft.previews = draft.previews.concat(filenames);
PostStore.storeCommentDraft(this.props.rootId, draft);
- this.setState({uploadsInProgress: draft['uploadsInProgress'], previews: draft['previews']});
- },
- handleUploadError: function(err, clientId) {
+ this.setState({uploadsInProgress: draft.uploadsInProgress, previews: draft.previews});
+ }
+ handleUploadError(err, clientId) {
if (clientId !== -1) {
- var draft = PostStore.getCommentDraft(this.props.rootId);
+ let draft = PostStore.getCommentDraft(this.props.rootId);
- var index = draft['uploadsInProgress'].indexOf(clientId);
+ const index = draft.uploadsInProgress.indexOf(clientId);
if (index !== -1) {
- draft['uploadsInProgress'].splice(index, 1);
+ draft.uploadsInProgress.splice(index, 1);
}
PostStore.storeCommentDraft(this.props.rootId, draft);
- this.setState({uploadsInProgress: draft['uploadsInProgress'], serverError: err});
+ this.setState({uploadsInProgress: draft.uploadsInProgress, serverError: err});
} else {
this.setState({serverError: err});
}
- },
- removePreview: function(id) {
- var previews = this.state.previews;
- var uploadsInProgress = this.state.uploadsInProgress;
+ }
+ removePreview(id) {
+ let previews = this.state.previews;
+ let uploadsInProgress = this.state.uploadsInProgress;
// id can either be the path of an uploaded file or the client id of an in progress upload
- var index = previews.indexOf(id);
+ let index = previews.indexOf(id);
if (index !== -1) {
previews.splice(index, 1);
} else {
@@ -171,30 +195,24 @@ module.exports = React.createClass({
}
}
- var draft = PostStore.getCommentDraft(this.props.rootId);
+ let draft = PostStore.getCommentDraft(this.props.rootId);
draft.previews = previews;
draft.uploadsInProgress = uploadsInProgress;
PostStore.storeCommentDraft(this.props.rootId, draft);
this.setState({previews: previews, uploadsInProgress: uploadsInProgress});
- },
- getInitialState: function() {
- PostStore.clearCommentDraftUploads();
-
- var draft = PostStore.getCommentDraft(this.props.rootId);
- return {messageText: draft['message'], uploadsInProgress: draft['uploadsInProgress'], previews: draft['previews'], submitting: false};
- },
- componentWillReceiveProps: function(newProps) {
+ }
+ componentWillReceiveProps(newProps) {
if (newProps.rootId !== this.props.rootId) {
- var draft = PostStore.getCommentDraft(newProps.rootId);
- this.setState({messageText: draft['message'], uploadsInProgress: draft['uploadsInProgress'], previews: draft['previews']});
+ const draft = PostStore.getCommentDraft(newProps.rootId);
+ this.setState({messageText: draft.message, uploadsInProgress: draft.uploadsInProgress, previews: draft.previews});
}
- },
- getFileCount: function(channelId) {
+ }
+ getFileCount() {
return this.state.previews.length + this.state.uploadsInProgress.length;
- },
- render: function() {
- var serverError = null;
+ }
+ render() {
+ let serverError = null;
if (this.state.serverError) {
serverError = (
<div className='form-group has-error'>
@@ -203,22 +221,23 @@ module.exports = React.createClass({
);
}
- var postError = null;
+ let postError = null;
if (this.state.postError) {
postError = <label className='control-label'>{this.state.postError}</label>;
}
- var preview = null;
+ let preview = null;
if (this.state.previews.length > 0 || this.state.uploadsInProgress.length > 0) {
preview = (
<FilePreview
files={this.state.previews}
onRemove={this.removePreview}
- uploadsInProgress={this.state.uploadsInProgress} />
+ uploadsInProgress={this.state.uploadsInProgress}
+ />
);
}
- var postFooterClassName = 'post-create-footer';
+ let postFooterClassName = 'post-create-footer';
if (postError) {
postFooterClassName += ' has-error';
}
@@ -226,7 +245,10 @@ module.exports = React.createClass({
return (
<form onSubmit={this.handleSubmit}>
<div className='post-create'>
- <div id={this.props.rootId} className='post-create-body comment-create-body'>
+ <div
+ id={this.props.rootId}
+ className='post-create-body comment-create-body'
+ >
<Textbox
onUserInput={this.handleUserInput}
onKeyPress={this.commentMsgKeyPress}
@@ -234,7 +256,8 @@ module.exports = React.createClass({
createMessage='Add a comment...'
initialText=''
id='reply_textbox'
- ref='textbox' />
+ ref='textbox'
+ />
<FileUpload
ref='fileUpload'
getFileCount={this.getFileCount}
@@ -242,11 +265,20 @@ module.exports = React.createClass({
onFileUpload={this.handleFileUploadComplete}
onUploadError={this.handleUploadError}
postType='comment'
- channelId={this.props.channelId} />
+ channelId={this.props.channelId}
+ />
</div>
- <MsgTyping channelId={this.props.channelId} parentId={this.props.rootId} />
+ <MsgTyping
+ channelId={this.props.channelId}
+ parentId={this.props.rootId}
+ />
<div className={postFooterClassName}>
- <input type='button' className='btn btn-primary comment-btn pull-right' value='Add Comment' onClick={this.handleSubmit} />
+ <input
+ type='button'
+ className='btn btn-primary comment-btn pull-right'
+ value='Add Comment'
+ onClick={this.handleSubmit}
+ />
{postError}
{serverError}
</div>
@@ -255,4 +287,9 @@ module.exports = React.createClass({
</form>
);
}
-});
+}
+
+CreateComment.propTypes = {
+ channelId: React.PropTypes.string.isRequired,
+ rootId: React.PropTypes.string.isRequired
+};
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index b9142223f..ce4ebac9e 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -1,33 +1,68 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
-var client = require('../utils/client.jsx');
-var AsyncClient = require('../utils/async_client.jsx');
-var ChannelStore = require('../stores/channel_store.jsx');
-var PostStore = require('../stores/post_store.jsx');
-var UserStore = require('../stores/user_store.jsx');
-var SocketStore = require('../stores/socket_store.jsx');
-var MsgTyping = require('./msg_typing.jsx');
-var Textbox = require('./textbox.jsx');
-var FileUpload = require('./file_upload.jsx');
-var FilePreview = require('./file_preview.jsx');
-var utils = require('../utils/utils.jsx');
-
-var Constants = require('../utils/constants.jsx');
-var ActionTypes = Constants.ActionTypes;
-
-module.exports = React.createClass({
- displayName: 'CreatePost',
- lastTime: 0,
- handleSubmit: function(e) {
+const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
+const Client = require('../utils/client.jsx');
+const AsyncClient = require('../utils/async_client.jsx');
+const ChannelStore = require('../stores/channel_store.jsx');
+const PostStore = require('../stores/post_store.jsx');
+const UserStore = require('../stores/user_store.jsx');
+const SocketStore = require('../stores/socket_store.jsx');
+const MsgTyping = require('./msg_typing.jsx');
+const Textbox = require('./textbox.jsx');
+const FileUpload = require('./file_upload.jsx');
+const FilePreview = require('./file_preview.jsx');
+const Utils = require('../utils/utils.jsx');
+
+const Constants = require('../utils/constants.jsx');
+const ActionTypes = Constants.ActionTypes;
+
+export default class CreatePost extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.lastTime = 0;
+
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.postMsgKeyPress = this.postMsgKeyPress.bind(this);
+ this.handleUserInput = this.handleUserInput.bind(this);
+ this.resizePostHolder = this.resizePostHolder.bind(this);
+ this.handleUploadStart = this.handleUploadStart.bind(this);
+ this.handleFileUploadComplete = this.handleFileUploadComplete.bind(this);
+ this.handleUploadError = this.handleUploadError.bind(this);
+ this.removePreview = this.removePreview.bind(this);
+ this.onChange = this.onChange.bind(this);
+ this.getFileCount = this.getFileCount.bind(this);
+
+ PostStore.clearDraftUploads();
+
+ const draft = PostStore.getCurrentDraft();
+ let previews = [];
+ let messageText = '';
+ let uploadsInProgress = [];
+ if (draft && draft.previews && draft.message) {
+ previews = draft.previews;
+ messageText = draft.message;
+ uploadsInProgress = draft.uploadsInProgress;
+ }
+
+ this.state = {
+ channelId: ChannelStore.getCurrentId(),
+ messageText: messageText,
+ uploadsInProgress: uploadsInProgress,
+ previews: previews,
+ submitting: false,
+ initialText: messageText
+ };
+ }
+ handleSubmit(e) {
e.preventDefault();
if (this.state.uploadsInProgress.length > 0 || this.state.submitting) {
return;
}
- var post = {};
+ let post = {};
post.filenames = [];
post.message = this.state.messageText;
@@ -36,18 +71,18 @@ module.exports = React.createClass({
}
if (post.message.length > Constants.CHARACTER_LIMIT) {
- this.setState({postError: 'Post length must be less than ' + Constants.CHARACTER_LIMIT + ' characters.'});
+ this.setState({postError: `Post length must be less than ${Constants.CHARACTER_LIMIT} characters.`});
return;
}
this.setState({submitting: true, serverError: null});
if (post.message.indexOf('/') === 0) {
- client.executeCommand(
+ Client.executeCommand(
this.state.channelId,
post.message,
false,
- function(data) {
+ function handleCommandSuccess(data) {
PostStore.storeDraft(data.channel_id, null);
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
@@ -55,8 +90,8 @@ module.exports = React.createClass({
window.location.href = data.goto_location;
}
}.bind(this),
- function(err) {
- var state = {};
+ function handleCommandError(err) {
+ let state = {};
state.serverError = err.message;
state.submitting = false;
this.setState(state);
@@ -66,26 +101,26 @@ module.exports = React.createClass({
post.channel_id = this.state.channelId;
post.filenames = this.state.previews;
- var time = utils.getTimestamp();
- var userId = UserStore.getCurrentId();
- post.pending_post_id = userId + ':' + time;
+ const time = Utils.getTimestamp();
+ const userId = UserStore.getCurrentId();
+ post.pending_post_id = `${userId}:${time}`;
post.user_id = userId;
post.create_at = time;
post.root_id = this.state.rootId;
post.parent_id = this.state.parentId;
- var channel = ChannelStore.get(this.state.channelId);
+ const channel = ChannelStore.get(this.state.channelId);
PostStore.storePendingPost(post);
PostStore.storeDraft(channel.id, null);
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
- client.createPost(post, channel,
- function(data) {
+ Client.createPost(post, channel,
+ function handlePostSuccess(data) {
this.resizePostHolder();
AsyncClient.getPosts();
- var member = ChannelStore.getMember(channel.id);
+ let member = ChannelStore.getMember(channel.id);
member.msg_count = channel.total_msg_count;
member.last_viewed_at = Date.now();
ChannelStore.setChannelMember(member);
@@ -95,8 +130,8 @@ module.exports = React.createClass({
post: data
});
}.bind(this),
- function(err) {
- var state = {};
+ function handlePostError(err) {
+ let state = {};
if (err.message === 'Invalid RootId parameter') {
if ($('#post_deleted').length > 0) {
@@ -113,83 +148,83 @@ module.exports = React.createClass({
}.bind(this)
);
}
- },
- componentDidUpdate: function() {
+ }
+ componentDidUpdate() {
this.resizePostHolder();
- },
- postMsgKeyPress: function(e) {
+ }
+ postMsgKeyPress(e) {
if (e.which === 13 && !e.shiftKey && !e.altKey) {
e.preventDefault();
- this.refs.textbox.getDOMNode().blur();
+ React.findDOMNode(this.refs.textbox).blur();
this.handleSubmit(e);
}
- var t = Date.now();
+ const t = Date.now();
if ((t - this.lastTime) > 5000) {
- SocketStore.sendMessage({channel_id: this.state.channelId, action: 'typing', props: {'parent_id': ''}, state: {}});
+ SocketStore.sendMessage({channel_id: this.state.channelId, action: 'typing', props: {parent_id: ''}, state: {}});
this.lastTime = t;
}
- },
- handleUserInput: function(messageText) {
+ }
+ handleUserInput(messageText) {
this.resizePostHolder();
this.setState({messageText: messageText});
- var draft = PostStore.getCurrentDraft();
- draft['message'] = messageText;
+ let draft = PostStore.getCurrentDraft();
+ draft.message = messageText;
PostStore.storeCurrentDraft(draft);
- },
- resizePostHolder: function() {
- var height = $(window).height() - $(this.refs.topDiv.getDOMNode()).height() - $('#error_bar').outerHeight() - 50;
- $('.post-list-holder-by-time').css('height', height + 'px');
+ }
+ resizePostHolder() {
+ const height = $(window).height() - $(React.findDOMNode(this.refs.topDiv)).height() - $('#error_bar').outerHeight() - 50;
+ $('.post-list-holder-by-time').css('height', `${height}px`);
$(window).trigger('resize');
- },
- handleUploadStart: function(clientIds, channelId) {
- var draft = PostStore.getDraft(channelId);
+ }
+ handleUploadStart(clientIds, channelId) {
+ let draft = PostStore.getDraft(channelId);
- draft['uploadsInProgress'] = draft['uploadsInProgress'].concat(clientIds);
+ draft.uploadsInProgress = draft.uploadsInProgress.concat(clientIds);
PostStore.storeDraft(channelId, draft);
- this.setState({uploadsInProgress: draft['uploadsInProgress']});
- },
- handleFileUploadComplete: function(filenames, clientIds, channelId) {
- var draft = PostStore.getDraft(channelId);
+ this.setState({uploadsInProgress: draft.uploadsInProgress});
+ }
+ handleFileUploadComplete(filenames, clientIds, channelId) {
+ let draft = PostStore.getDraft(channelId);
// remove each finished file from uploads
- for (var i = 0; i < clientIds.length; i++) {
- var index = draft['uploadsInProgress'].indexOf(clientIds[i]);
+ for (let i = 0; i < clientIds.length; i++) {
+ const index = draft.uploadsInProgress.indexOf(clientIds[i]);
if (index !== -1) {
- draft['uploadsInProgress'].splice(index, 1);
+ draft.uploadsInProgress.splice(index, 1);
}
}
- draft['previews'] = draft['previews'].concat(filenames);
+ draft.previews = draft.previews.concat(filenames);
PostStore.storeDraft(channelId, draft);
- this.setState({uploadsInProgress: draft['uploadsInProgress'], previews: draft['previews']});
- },
- handleUploadError: function(err, clientId) {
+ this.setState({uploadsInProgress: draft.uploadsInProgress, previews: draft.previews});
+ }
+ handleUploadError(err, clientId) {
if (clientId !== -1) {
- var draft = PostStore.getDraft(this.state.channelId);
+ let draft = PostStore.getDraft(this.state.channelId);
- var index = draft['uploadsInProgress'].indexOf(clientId);
+ const index = draft.uploadsInProgress.indexOf(clientId);
if (index !== -1) {
- draft['uploadsInProgress'].splice(index, 1);
+ draft.uploadsInProgress.splice(index, 1);
}
PostStore.storeDraft(this.state.channelId, draft);
- this.setState({uploadsInProgress: draft['uploadsInProgress'], serverError: err});
+ this.setState({uploadsInProgress: draft.uploadsInProgress, serverError: err});
} else {
this.setState({serverError: err});
}
- },
- removePreview: function(id) {
- var previews = this.state.previews;
- var uploadsInProgress = this.state.uploadsInProgress;
+ }
+ removePreview(id) {
+ let previews = this.state.previews;
+ let uploadsInProgress = this.state.uploadsInProgress;
// id can either be the path of an uploaded file or the client id of an in progress upload
- var index = previews.indexOf(id);
+ let index = previews.indexOf(id);
if (index !== -1) {
previews.splice(index, 1);
} else {
@@ -201,28 +236,28 @@ module.exports = React.createClass({
}
}
- var draft = PostStore.getCurrentDraft();
- draft['previews'] = previews;
- draft['uploadsInProgress'] = uploadsInProgress;
+ let draft = PostStore.getCurrentDraft();
+ draft.previews = previews;
+ draft.uploadsInProgress = uploadsInProgress;
PostStore.storeCurrentDraft(draft);
this.setState({previews: previews, uploadsInProgress: uploadsInProgress});
- },
- componentDidMount: function() {
- ChannelStore.addChangeListener(this._onChange);
+ }
+ componentDidMount() {
+ ChannelStore.addChangeListener(this.onChange);
this.resizePostHolder();
- },
- componentWillUnmount: function() {
- ChannelStore.removeChangeListener(this._onChange);
- },
- _onChange: function() {
- var channelId = ChannelStore.getCurrentId();
+ }
+ componentWillUnmount() {
+ ChannelStore.removeChangeListener(this.onChange);
+ }
+ onChange() {
+ const channelId = ChannelStore.getCurrentId();
if (this.state.channelId !== channelId) {
- var draft = PostStore.getCurrentDraft();
+ let draft = PostStore.getCurrentDraft();
- var previews = [];
- var messageText = '';
- var uploadsInProgress = [];
+ let previews = [];
+ let messageText = '';
+ let uploadsInProgress = [];
if (draft && draft.previews && draft.message) {
previews = draft.previews;
messageText = draft.message;
@@ -231,33 +266,17 @@ module.exports = React.createClass({
this.setState({channelId: channelId, messageText: messageText, initialText: messageText, submitting: false, serverError: null, postError: null, previews: previews, uploadsInProgress: uploadsInProgress});
}
- },
- getInitialState: function() {
- PostStore.clearDraftUploads();
-
- var draft = PostStore.getCurrentDraft();
- var previews = [];
- var messageText = '';
- var uploadsInProgress = [];
- if (draft && draft.previews && draft.message) {
- previews = draft.previews;
- messageText = draft.message;
- uploadsInProgress = draft.uploadsInProgress;
- }
-
- return {channelId: ChannelStore.getCurrentId(), messageText: messageText, uploadsInProgress: uploadsInProgress, previews: previews, submitting: false, initialText: messageText};
- },
- getFileCount: function(channelId) {
+ }
+ getFileCount(channelId) {
if (channelId === this.state.channelId) {
return this.state.previews.length + this.state.uploadsInProgress.length;
- } else {
- var draft = PostStore.getDraft(channelId);
-
- return draft['previews'].length + draft['uploadsInProgress'].length;
}
- },
- render: function() {
- var serverError = null;
+
+ const draft = PostStore.getDraft(channelId);
+ return draft.previews.length + draft.uploadsInProgress.length;
+ }
+ render() {
+ let serverError = null;
if (this.state.serverError) {
serverError = (
<div className='has-error'>
@@ -266,12 +285,12 @@ module.exports = React.createClass({
);
}
- var postError = null;
+ let postError = null;
if (this.state.postError) {
postError = <label className='control-label'>{this.state.postError}</label>;
}
- var preview = null;
+ let preview = null;
if (this.state.previews.length > 0 || this.state.uploadsInProgress.length > 0) {
preview = (
<FilePreview
@@ -281,13 +300,18 @@ module.exports = React.createClass({
);
}
- var postFooterClassName = 'post-create-footer';
+ let postFooterClassName = 'post-create-footer';
if (postError) {
postFooterClassName += ' has-error';
}
return (
- <form id='create_post' ref='topDiv' role='form' onSubmit={this.handleSubmit}>
+ <form
+ id='create_post'
+ ref='topDiv'
+ role='form'
+ onSubmit={this.handleSubmit}
+ >
<div className='post-create'>
<div className='post-create-body'>
<Textbox
@@ -311,10 +335,13 @@ module.exports = React.createClass({
{postError}
{serverError}
{preview}
- <MsgTyping channelId={this.state.channelId} parentId=''/>
+ <MsgTyping
+ channelId={this.state.channelId}
+ parentId=''
+ />
</div>
</div>
</form>
);
}
-});
+}
diff --git a/web/react/components/delete_channel_modal.jsx b/web/react/components/delete_channel_modal.jsx
index 589737271..4efb9cb23 100644
--- a/web/react/components/delete_channel_modal.jsx
+++ b/web/react/components/delete_channel_modal.jsx
@@ -1,58 +1,99 @@
// 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 ChannelStore =require('../stores/channel_store.jsx')
+const Client = require('../utils/client.jsx');
+const AsyncClient = require('../utils/async_client.jsx');
+const ChannelStore = require('../stores/channel_store.jsx');
-module.exports = React.createClass({
- handleDelete: function(e) {
- if (this.state.channel_id.length != 26) return;
+export default class DeleteChannelModal extends React.Component {
+ constructor(props) {
+ super(props);
- Client.deleteChannel(this.state.channel_id,
- function(data) {
+ this.handleDelete = this.handleDelete.bind(this);
+
+ this.state = {
+ title: '',
+ channelId: ''
+ };
+ }
+ handleDelete() {
+ if (this.state.channelId.length !== 26) {
+ return;
+ }
+
+ Client.deleteChannel(this.state.channelId,
+ function handleDeleteSuccess() {
AsyncClient.getChannels(true);
window.location.href = '/';
- }.bind(this),
- function(err) {
- AsyncClient.dispatchError(err, "handleDelete");
- }.bind(this)
+ },
+ function handleDeleteError(err) {
+ AsyncClient.dispatchError(err, 'handleDelete');
+ }
);
- },
- componentDidMount: function() {
- var self = this;
- $(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
+ }
+ componentDidMount() {
+ $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function handleShow(e) {
var button = $(e.relatedTarget);
- self.setState({ title: button.attr('data-title'), channel_id: button.attr('data-channelid') });
- });
- },
- getInitialState: function() {
- return { title: "", channel_id: "" };
- },
- render: function() {
-
- var channelType = ChannelStore.getCurrent() && ChannelStore.getCurrent().type === 'P' ? "private group" : "channel"
+ this.setState({
+ title: button.attr('data-title'),
+ channelId: button.attr('data-channelid')
+ });
+ }.bind(this));
+ }
+ render() {
+ const channel = ChannelStore.getCurrent();
+ let channelType = 'channel';
+ if (channel && channel.type === 'P') {
+ channelType = 'private group';
+ }
return (
- <div className="modal fade" ref="modal" id="delete_channel" role="dialog" tabIndex="-1" aria-hidden="true">
- <div className="modal-dialog">
- <div className="modal-content">
- <div className="modal-header">
- <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
- <h4 className="modal-title">Confirm DELETE Channel</h4>
- </div>
- <div className="modal-body">
- <p>
- Are you sure you wish to delete the {this.state.title} {channelType}?
- </p>
- </div>
- <div className="modal-footer">
- <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button>
- <button type="button" className="btn btn-danger" data-dismiss="modal" onClick={this.handleDelete}>Delete</button>
- </div>
+ <div
+ className='modal fade'
+ ref='modal'
+ id='delete_channel'
+ role='dialog'
+ tabIndex='-1'
+ aria-hidden='true'
+ >
+ <div className='modal-dialog'>
+ <div className='modal-content'>
+ <div className='modal-header'>
+ <button
+ type='button'
+ className='close'
+ data-dismiss='modal'
+ aria-label='Close'
+ >
+ <span aria-hidden='true'>&times;</span>
+ </button>
+ <h4 className='modal-title'>Confirm DELETE Channel</h4>
+ </div>
+ <div className='modal-body'>
+ <p>
+ Are you sure you wish to delete the {this.state.title} {channelType}?
+ </p>
+ </div>
+ <div className='modal-footer'>
+ <button
+ type='button'
+ className='btn btn-default'
+ data-dismiss='modal'
+ >
+ Cancel
+ </button>
+ <button
+ type='button'
+ className='btn btn-danger'
+ data-dismiss='modal'
+ onClick={this.handleDelete}
+ >
+ Delete
+ </button>
+ </div>
+ </div>
</div>
- </div>
</div>
);
}
-});
+}
diff --git a/web/react/components/edit_channel_modal.jsx b/web/react/components/edit_channel_modal.jsx
index 76f0c2c4d..e93bab431 100644
--- a/web/react/components/edit_channel_modal.jsx
+++ b/web/react/components/edit_channel_modal.jsx
@@ -1,79 +1,142 @@
// 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');
+const Client = require('../utils/client.jsx');
+const AsyncClient = require('../utils/async_client.jsx');
-module.exports = React.createClass({
- handleEdit: function(e) {
+export default class EditChannelModal extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleEdit = this.handleEdit.bind(this);
+ this.handleUserInput = this.handleUserInput.bind(this);
+ this.handleClose = this.handleClose.bind(this);
+
+ this.state = {
+ description: '',
+ title: '',
+ channelId: '',
+ serverError: ''
+ };
+ }
+ handleEdit() {
var data = {};
- data["channel_id"] = this.state.channel_id;
- if (data["channel_id"].length !== 26) return;
- data["channel_description"] = this.state.description.trim();
+ data.channel_id = this.state.channelId;
+
+ if (data.channel_id.length !== 26) {
+ return;
+ }
+
+ data.channel_description = this.state.description.trim();
Client.updateChannelDesc(data,
- function(data) {
- this.setState({ server_error: "" });
- AsyncClient.getChannel(this.state.channel_id);
- $(this.refs.modal.getDOMNode()).modal('hide');
+ function handleUpdateSuccess() {
+ this.setState({serverError: ''});
+ AsyncClient.getChannel(this.state.channelId);
+ $(React.findDOMNode(this.refs.modal)).modal('hide');
}.bind(this),
- function(err) {
- if (err.message === "Invalid channel_description parameter") {
- this.setState({ server_error: "This description is too long, please enter a shorter one" });
- }
- else {
- this.setState({ server_error: err.message });
+ function handleUpdateError(err) {
+ if (err.message === 'Invalid channel_description parameter') {
+ this.setState({serverError: 'This description is too long, please enter a shorter one'});
+ } else {
+ this.setState({serverError: err.message});
}
}.bind(this)
);
- },
- handleUserInput: function(e) {
- this.setState({ description: e.target.value });
- },
- handleClose: function() {
- this.setState({description: "", server_error: ""});
- },
- componentDidMount: function() {
- var self = this;
- $(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
- var button = e.relatedTarget;
- self.setState({ description: $(button).attr('data-desc'), title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), server_error: "" });
- });
- $(this.refs.modal.getDOMNode()).on('hidden.bs.modal', this.handleClose)
- },
- componentWillUnmount: function() {
- $(this.refs.modal.getDOMNode()).off('hidden.bs.modal', this.handleClose)
- },
- getInitialState: function() {
- return { description: "", title: "", channel_id: "" };
- },
- render: function() {
- var server_error = this.state.server_error ? <div className='form-group has-error'><br/><label className='control-label'>{ this.state.server_error }</label></div> : null;
+ }
+ handleUserInput(e) {
+ this.setState({description: e.target.value});
+ }
+ handleClose() {
+ this.setState({description: '', serverError: ''});
+ }
+ componentDidMount() {
+ $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function handleShow(e) {
+ const button = e.relatedTarget;
+ this.setState({description: $(button).attr('data-desc'), title: $(button).attr('data-title'), channelId: $(button).attr('data-channelid'), serverError: ''});
+ }.bind(this));
+ $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose);
+ }
+ componentWillUnmount() {
+ $(React.findDOMNode(this.refs.modal)).off('hidden.bs.modal', this.handleClose);
+ }
+ render() {
+ var serverError = null;
+ if (this.state.serverError) {
+ serverError = <div className='form-group has-error'><br/><label className='control-label'>{this.state.serverError}</label></div>;
+ }
- var editTitle = <h4 className='modal-title' ref='title'>Edit Description</h4>;
+ var editTitle = (
+ <h4
+ className='modal-title'
+ ref='title'
+ >
+ Edit Description
+ </h4>
+ );
if (this.state.title) {
- editTitle = <h4 className='modal-title' ref='title'>Edit Description for <span className='name'>{this.state.title}</span></h4>;
+ editTitle = (
+ <h4
+ className='modal-title'
+ ref='title'
+ >
+ Edit Description for <span className='name'>{this.state.title}</span>
+ </h4>
+ );
}
return (
- <div className="modal fade" ref="modal" id="edit_channel" role="dialog" tabIndex="-1" aria-hidden="true">
- <div className="modal-dialog">
- <div className="modal-content">
- <div className="modal-header">
- <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
- {editTitle}
- </div>
- <div className="modal-body">
- <textarea className="form-control no-resize" rows="6" ref="channelDesc" maxLength="1024" value={this.state.description} onChange={this.handleUserInput}></textarea>
- { server_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'
+ ref='modal'
+ id='edit_channel'
+ role='dialog'
+ tabIndex='-1'
+ aria-hidden='true'
+ >
+ <div className='modal-dialog'>
+ <div className='modal-content'>
+ <div className='modal-header'>
+ <button
+ type='button'
+ className='close'
+ data-dismiss='modal'
+ aria-label='Close'
+ >
+ <span aria-hidden='true'>&times;</span>
+ </button>
+ {editTitle}
+ </div>
+ <div className='modal-body'>
+ <textarea
+ className='form-control no-resize'
+ rows='6'
+ ref='channelDesc'
+ maxLength='1024'
+ value={this.state.description}
+ onChange={this.handleUserInput}
+ />
+ {serverError}
+ </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/file_upload_overlay.jsx b/web/react/components/file_upload_overlay.jsx
index f35556371..265924206 100644
--- a/web/react/components/file_upload_overlay.jsx
+++ b/web/react/components/file_upload_overlay.jsx
@@ -1,12 +1,8 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-module.exports = React.createClass({
- displayName: 'FileUploadOverlay',
- propTypes: {
- overlayType: React.PropTypes.string
- },
- render: function() {
+export default class FileUploadOverlay extends React.Component {
+ render() {
var overlayClass = 'file-overlay hidden';
if (this.props.overlayType === 'right') {
overlayClass += ' right-file-overlay';
@@ -23,4 +19,8 @@ module.exports = React.createClass({
</div>
);
}
-});
+}
+
+FileUploadOverlay.propTypes = {
+ overlayType: React.PropTypes.string
+};
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
index 28dd64c39..f87e77ff7 100644
--- a/web/react/components/login.jsx
+++ b/web/react/components/login.jsx
@@ -1,11 +1,11 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var utils = require('../utils/utils.jsx');
-var client = require('../utils/client.jsx');
-var UserStore = require('../stores/user_store.jsx');
-var BrowserStore = require('../stores/browser_store.jsx');
-var Constants = require('../utils/constants.jsx');
+const Utils = require('../utils/utils.jsx');
+const Client = require('../utils/client.jsx');
+const UserStore = require('../stores/user_store.jsx');
+const BrowserStore = require('../stores/browser_store.jsx');
+const Constants = require('../utils/constants.jsx');
export default class Login extends React.Component {
constructor(props) {
@@ -17,23 +17,23 @@ export default class Login extends React.Component {
}
handleSubmit(e) {
e.preventDefault();
- var state = {};
+ let state = {};
- var name = this.props.teamName;
+ const name = this.props.teamName;
if (!name) {
state.serverError = 'Bad team name';
this.setState(state);
return;
}
- var email = this.refs.email.getDOMNode().value.trim();
+ const email = React.findDOMNode(this.refs.email).value.trim();
if (!email) {
state.serverError = 'An email is required';
this.setState(state);
return;
}
- var password = this.refs.password.getDOMNode().value.trim();
+ const password = React.findDOMNode(this.refs.password).value.trim();
if (!password) {
state.serverError = 'A password is required';
this.setState(state);
@@ -49,12 +49,12 @@ export default class Login extends React.Component {
state.serverError = '';
this.setState(state);
- client.loginByEmail(name, email, password,
+ Client.loginByEmail(name, email, password,
function loggedIn(data) {
UserStore.setCurrentUser(data);
UserStore.setLastEmail(email);
- var redirect = utils.getUrlParameter('redirect');
+ const redirect = Utils.getUrlParameter('redirect');
if (redirect) {
window.location.href = decodeURIComponent(redirect);
} else {
@@ -73,31 +73,31 @@ export default class Login extends React.Component {
);
}
render() {
- var serverError;
+ let serverError;
if (this.state.serverError) {
serverError = <label className='control-label'>{this.state.serverError}</label>;
}
- var priorEmail = UserStore.getLastEmail();
+ let priorEmail = UserStore.getLastEmail();
- var emailParam = utils.getUrlParameter('email');
+ const emailParam = Utils.getUrlParameter('email');
if (emailParam) {
priorEmail = decodeURIComponent(emailParam);
}
- var teamDisplayName = this.props.teamDisplayName;
- var teamName = this.props.teamName;
+ const teamDisplayName = this.props.teamDisplayName;
+ const teamName = this.props.teamName;
- var focusEmail = false;
- var focusPassword = false;
+ let focusEmail = false;
+ let focusPassword = false;
if (priorEmail !== '') {
focusPassword = true;
} else {
focusEmail = true;
}
- var authServices = JSON.parse(this.props.authServices);
+ const authServices = JSON.parse(this.props.authServices);
- var loginMessage = [];
+ let loginMessage = [];
if (authServices.indexOf(Constants.GITLAB_SERVICE) !== -1) {
loginMessage.push(
<a
@@ -110,12 +110,12 @@ export default class Login extends React.Component {
);
}
- var errorClass = '';
+ let errorClass = '';
if (serverError) {
errorClass = ' has-error';
}
- var emailSignup;
+ let emailSignup;
if (authServices.indexOf(Constants.EMAIL_SERVICE) !== -1) {
emailSignup = (
<div>
@@ -163,7 +163,7 @@ export default class Login extends React.Component {
);
}
- var forgotPassword;
+ let forgotPassword;
if (emailSignup) {
forgotPassword = (
<div className='form-group'>
diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx
index cb48e5cc5..064330c8d 100644
--- a/web/react/components/member_list_team.jsx
+++ b/web/react/components/member_list_team.jsx
@@ -1,122 +1,27 @@
// 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');
-var Client = require('../utils/client.jsx');
-var AsyncClient = require('../utils/async_client.jsx');
-var utils = require('../utils/utils.jsx');
-
-var MemberListTeamItem = React.createClass({
- handleMakeMember: function() {
- var data = {};
- data["user_id"] = this.props.user.id;
- data["new_roles"] = "";
-
- Client.updateRoles(data,
- function(data) {
- AsyncClient.getProfiles();
- }.bind(this),
- function(err) {
- this.setState({ server_error: err.message });
- }.bind(this)
- );
- },
- handleMakeActive: function() {
- Client.updateActive(this.props.user.id, true,
- function(data) {
- AsyncClient.getProfiles();
- }.bind(this),
- function(err) {
- this.setState({ server_error: err.message });
- }.bind(this)
- );
- },
- handleMakeNotActive: function() {
- Client.updateActive(this.props.user.id, false,
- function(data) {
- AsyncClient.getProfiles();
- }.bind(this),
- function(err) {
- this.setState({ server_error: err.message });
- }.bind(this)
- );
- },
- handleMakeAdmin: function() {
- var data = {};
- data["user_id"] = this.props.user.id;
- data["new_roles"] = "admin";
-
- Client.updateRoles(data,
- function(data) {
- AsyncClient.getProfiles();
- }.bind(this),
- function(err) {
- this.setState({ server_error: err.message });
- }.bind(this)
- );
- },
- getInitialState: function() {
- return {};
- },
- render: function() {
- var server_error = this.state.server_error ? <div className="has-error"><label className='has-error control-label'>{this.state.server_error}</label></div> : null;
- var user = this.props.user;
- var currentRoles = "Member";
- var timestamp = UserStore.getCurrentUser().update_at;
-
- if (user.roles.length > 0) {
- currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
- }
-
- var email = user.email.length > 0 ? user.email : "";
- var showMakeMember = user.roles == "admin";
- var showMakeAdmin = user.roles == "";
- var showMakeActive = false;
- var showMakeNotActive = true;
-
- if (user.delete_at > 0) {
- currentRoles = "Inactive";
- showMakeMember = false;
- showMakeAdmin = false;
- showMakeActive = true;
- showMakeNotActive = false;
- }
+const MemberListTeamItem = require('./member_list_team_item.jsx');
+
+export default class MemberListTeam extends React.Component {
+ render() {
+ const memberList = this.props.users.map(function makeListItem(user) {
+ return (
+ <MemberListTeamItem
+ key={user.id}
+ user={user}
+ />
+ );
+ }, this);
return (
- <div className="row member-div">
- <img className="post-profile-img pull-left" src={"/api/v1/users/" + user.id + "/image?time=" + timestamp} height="36" width="36" />
- <span className="member-name">{utils.getDisplayName(user)}</span>
- <span className="member-email">{email}</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>{currentRoles} </span>
- <span className="caret"></span>
- </a>
- <ul className="dropdown-menu member-menu" role="menu" aria-labelledby="channel_header_dropdown">
- { showMakeAdmin ? <li role="presentation"><a role="menuitem" href="#" onClick={this.handleMakeAdmin}>Make Admin</a></li> : "" }
- { showMakeMember ? <li role="presentation"><a role="menuitem" href="#" onClick={this.handleMakeMember}>Make Member</a></li> : "" }
- { showMakeActive ? <li role="presentation"><a role="menuitem" href="#" onClick={this.handleMakeActive}>Make Active</a></li> : "" }
- { showMakeNotActive ? <li role="presentation"><a role="menuitem" href="#" onClick={this.handleMakeNotActive}>Make Inactive</a></li> : "" }
- </ul>
- </div>
- { server_error }
+ <div className='member-list-holder'>
+ {memberList}
</div>
);
}
-});
+}
-
-module.exports = React.createClass({
- render: function() {
- return (
- <div className="member-list-holder">
- {
- this.props.users.map(function(user) {
- return <MemberListTeamItem key={user.id} user={user} />;
- }, this)
- }
- </div>
- );
- }
-});
+MemberListTeam.propTypes = {
+ users: React.PropTypes.arrayOf(React.PropTypes.object).isRequired
+};
diff --git a/web/react/components/member_list_team_item.jsx b/web/react/components/member_list_team_item.jsx
new file mode 100644
index 000000000..b7e81f843
--- /dev/null
+++ b/web/react/components/member_list_team_item.jsx
@@ -0,0 +1,203 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+const UserStore = require('../stores/user_store.jsx');
+const Client = require('../utils/client.jsx');
+const AsyncClient = require('../utils/async_client.jsx');
+const Utils = require('../utils/utils.jsx');
+
+export default class MemberListTeamItem extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleMakeMember = this.handleMakeMember.bind(this);
+ this.handleMakeActive = this.handleMakeActive.bind(this);
+ this.handleMakeNotActive = this.handleMakeNotActive.bind(this);
+ this.handleMakeAdmin = this.handleMakeAdmin.bind(this);
+
+ this.state = {};
+ }
+ handleMakeMember() {
+ const data = {
+ user_id: this.props.user.id,
+ new_roles: ''
+ };
+
+ Client.updateRoles(data,
+ function handleMakeMemberSuccess() {
+ AsyncClient.getProfiles();
+ },
+ function handleMakeMemberError(err) {
+ this.setState({serverError: err.message});
+ }.bind(this)
+ );
+ }
+ handleMakeActive() {
+ Client.updateActive(this.props.user.id, true,
+ function handleMakeActiveSuccess() {
+ AsyncClient.getProfiles();
+ },
+ function handleMakeActiveError(err) {
+ this.setState({serverError: err.message});
+ }.bind(this)
+ );
+ }
+ handleMakeNotActive() {
+ Client.updateActive(this.props.user.id, false,
+ function handleMakeNotActiveSuccess() {
+ AsyncClient.getProfiles();
+ },
+ function handleMakeNotActiveError(err) {
+ this.setState({serverError: err.message});
+ }.bind(this)
+ );
+ }
+ handleMakeAdmin() {
+ const data = {
+ user_id: this.props.user.id,
+ new_roles: 'admin'
+ };
+
+ Client.updateRoles(data,
+ function handleMakeAdminSuccess() {
+ AsyncClient.getProfiles();
+ },
+ function handleMakeAdmitError(err) {
+ this.setState({serverError: err.message});
+ }.bind(this)
+ );
+ }
+ render() {
+ let serverError = null;
+ if (this.state.serverError) {
+ serverError = (
+ <div className='has-error'>
+ <label className='has-error control-label'>{this.state.serverError}</label>
+ </div>
+ );
+ }
+
+ const user = this.props.user;
+ let currentRoles = 'Member';
+ const timestamp = UserStore.getCurrentUser().update_at;
+
+ if (user.roles.length > 0) {
+ currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
+ }
+
+ const email = user.email;
+ let showMakeMember = user.roles === 'admin';
+ let showMakeAdmin = user.roles === '';
+ let showMakeActive = false;
+ let showMakeNotActive = true;
+
+ if (user.delete_at > 0) {
+ currentRoles = 'Inactive';
+ showMakeMember = false;
+ showMakeAdmin = false;
+ showMakeActive = true;
+ showMakeNotActive = false;
+ }
+
+ let makeAdmin = null;
+ if (showMakeAdmin) {
+ makeAdmin = (
+ <li role='presentation'>
+ <a
+ role='menuitem'
+ href='#'
+ onClick={this.handleMakeAdmin}
+ >
+ Make Admin
+ </a>
+ </li>
+ );
+ }
+
+ let makeMember = null;
+ if (showMakeMember) {
+ makeMember = (
+ <li role='presentation'>
+ <a
+ role='menuitem'
+ href='#'
+ onClick={this.handleMakeMember}
+ >
+ Make Member
+ </a>
+ </li>
+ );
+ }
+
+ let makeActive = null;
+ if (showMakeActive) {
+ makeActive = (
+ <li role='presentation'>
+ <a
+ role='menuitem'
+ href='#'
+ onClick={this.handleMakeActive}
+ >
+ Make Active
+ </a>
+ </li>
+ );
+ }
+
+ let makeNotActive = null;
+ if (showMakeNotActive) {
+ makeNotActive = (
+ <li role='presentation'>
+ <a
+ role='menuitem'
+ href='#'
+ onClick={this.handleMakeNotActive}
+ >
+ Make Inactive
+ </a>
+ </li>
+ );
+ }
+
+ return (
+ <div className='row member-div'>
+ <img
+ className='post-profile-img pull-left'
+ src={`/api/v1/users/${user.id}/image?time=${timestamp}`}
+ height='36'
+ width='36'
+ />
+ <span className='member-name'>{Utils.getDisplayName(user)}</span>
+ <span className='member-email'>{email}</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>{currentRoles} </span>
+ <span className='caret'></span>
+ </a>
+ <ul
+ className='dropdown-menu member-menu'
+ role='menu'
+ aria-labelledby='channel_header_dropdown'
+ >
+ {makeAdmin}
+ {makeMember}
+ {makeActive}
+ {makeNotActive}
+ </ul>
+ </div>
+ {serverError}
+ </div>
+ );
+ }
+}
+
+MemberListTeamItem.propTypes = {
+ user: React.PropTypes.object.isRequired
+};
diff --git a/web/react/components/popover_list_members.jsx b/web/react/components/popover_list_members.jsx
new file mode 100644
index 000000000..fb9522afb
--- /dev/null
+++ b/web/react/components/popover_list_members.jsx
@@ -0,0 +1,80 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+export default class PopoverListMembers extends React.Component {
+ componentDidMount() {
+ const originalLeave = $.fn.popover.Constructor.prototype.leave;
+ $.fn.popover.Constructor.prototype.leave = function onLeave(obj) {
+ let selfObj;
+ if (obj instanceof this.constructor) {
+ selfObj = obj;
+ } else {
+ selfObj = $(obj.currentTarget)[this.type](this.getDelegateOptions()).data(`bs.${this.type}`);
+ }
+ originalLeave.call(this, obj);
+
+ if (obj.currentTarget && selfObj.$tip) {
+ selfObj.$tip.one('mouseenter', function onMouseEnter() {
+ clearTimeout(selfObj.timeout);
+ selfObj.$tip.one('mouseleave', function onMouseLeave() {
+ $.fn.popover.Constructor.prototype.leave.call(selfObj, selfObj);
+ });
+ });
+ }
+ };
+
+ $('#member_popover').popover({placement: 'bottom', trigger: 'click', html: true});
+ $('body').on('click', function onClick(e) {
+ if ($(e.target.parentNode.parentNode)[0] !== $('#member_popover')[0] && $(e.target).parents('.popover.in').length === 0) {
+ $('#member_popover').popover('hide');
+ }
+ });
+ }
+ render() {
+ let popoverHtml = '';
+ const members = this.props.members;
+ let count;
+ if (members.length > 20) {
+ count = '20+';
+ } else {
+ count = members.length || '-';
+ }
+
+ if (members) {
+ members.sort(function compareByLocal(a, b) {
+ return a.username.localeCompare(b.username);
+ });
+
+ members.forEach(function addMemberElement(m) {
+ popoverHtml += `<div class='text--nowrap'>${m.username}</div>`;
+ });
+ }
+
+ return (
+ <div
+ id='member_popover'
+ data-toggle='popover'
+ data-content={popoverHtml}
+ data-original-title='Members'
+ >
+ <div
+ id='member_tooltip'
+ data-placement='left'
+ data-toggle='tooltip'
+ title='View Channel Members'
+ >
+ {count}
+ <span
+ className='glyphicon glyphicon-user'
+ aria-hidden='true'
+ />
+ </div>
+ </div>
+ );
+ }
+}
+
+PopoverListMembers.propTypes = {
+ members: React.PropTypes.array.isRequired,
+ channelId: React.PropTypes.string.isRequired
+};
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index e5ab5b624..88fb9aec8 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -1,95 +1,140 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var FileAttachmentList = require('./file_attachment_list.jsx');
-var UserStore = require('../stores/user_store.jsx');
-var utils = require('../utils/utils.jsx');
-var Constants = require('../utils/constants.jsx');
+const FileAttachmentList = require('./file_attachment_list.jsx');
+const UserStore = require('../stores/user_store.jsx');
+const Utils = require('../utils/utils.jsx');
+const Constants = require('../utils/constants.jsx');
-module.exports = React.createClass({
- componentWillReceiveProps: function(nextProps) {
- var linkData = utils.extractLinks(nextProps.post.message);
+export default class PostBody extends React.Component {
+ constructor(props) {
+ super(props);
+
+ const linkData = Utils.extractLinks(this.props.post.message);
+ this.state = {links: linkData.links, message: linkData.text};
+ }
+ componentWillReceiveProps(nextProps) {
+ const linkData = Utils.extractLinks(nextProps.post.message);
this.setState({links: linkData.links, message: linkData.text});
- },
- getInitialState: function() {
- var linkData = utils.extractLinks(this.props.post.message);
- return {links: linkData.links, message: linkData.text};
- },
- render: function() {
- var post = this.props.post;
- var filenames = this.props.post.filenames;
- var parentPost = this.props.parentPost;
- var inner = utils.textToJsx(this.state.message);
+ }
+ render() {
+ const post = this.props.post;
+ const filenames = this.props.post.filenames;
+ const parentPost = this.props.parentPost;
+ const inner = Utils.textToJsx(this.state.message);
- var comment = '';
- var reply = '';
- var postClass = '';
+ let comment = '';
+ let postClass = '';
if (parentPost) {
- var profile = UserStore.getProfile(parentPost.user_id);
- var apostrophe = '';
- var name = '...';
+ const profile = UserStore.getProfile(parentPost.user_id);
+
+ let apostrophe = '';
+ let name = '...';
if (profile != null) {
if (profile.username.slice(-1) === 's') {
apostrophe = '\'';
} else {
apostrophe = '\'s';
}
- name = <a className='theme' onClick={function searchName() { utils.searchForTerm(profile.username); }}>{profile.username}</a>;
+ name = (
+ <a
+ className='theme'
+ onClick={Utils.searchForTerm.bind(null, profile.username)}
+ >
+ {profile.username}
+ </a>
+ );
}
- var message = '';
+ let message = '';
if (parentPost.message) {
- message = utils.replaceHtmlEntities(parentPost.message);
+ message = Utils.replaceHtmlEntities(parentPost.message);
} else if (parentPost.filenames.length) {
message = parentPost.filenames[0].split('/').pop();
if (parentPost.filenames.length === 2) {
message += ' plus 1 other file';
} else if (parentPost.filenames.length > 2) {
- message += ' plus ' + (parentPost.filenames.length - 1) + ' other files';
+ message += ` plus ${parentPost.filenames.length - 1} other files`;
}
}
comment = (
<p className='post-link'>
- <span>Commented on {name}{apostrophe} message: <a className='theme' onClick={this.props.handleCommentClick}>{message}</a></span>
+ <span>
+ Commented on {name}{apostrophe} message:
+ <a
+ className='theme'
+ onClick={this.props.handleCommentClick}
+ >
+ {message}
+ </a>
+ </span>
</p>
);
postClass += ' post-comment';
}
- var loading;
+ let loading;
if (post.state === Constants.POST_FAILED) {
postClass += ' post-fail';
- loading = <a className='theme post-retry pull-right' href='#' onClick={this.props.retryPost}>Retry</a>;
+ loading = (
+ <a
+ className='theme post-retry pull-right'
+ href='#'
+ onClick={this.props.retryPost}
+ >
+ Retry
+ </a>
+ );
} else if (post.state === Constants.POST_LOADING) {
postClass += ' post-waiting';
- loading = <img className='post-loading-gif pull-right' src='/static/images/load.gif'/>;
+ loading = (
+ <img
+ className='post-loading-gif pull-right'
+ src='/static/images/load.gif'
+ />
+ );
}
- var embed;
+ let embed;
if (filenames.length === 0 && this.state.links) {
- embed = utils.getEmbed(this.state.links[0]);
+ embed = Utils.getEmbed(this.state.links[0]);
}
- var fileAttachmentHolder = '';
+ let fileAttachmentHolder = '';
if (filenames && filenames.length > 0) {
- fileAttachmentHolder = (<FileAttachmentList
- filenames={filenames}
- modalId={'view_image_modal_' + post.id}
- channelId={post.channel_id}
- userId={post.user_id} />);
+ fileAttachmentHolder = (
+ <FileAttachmentList
+ filenames={filenames}
+ modalId={`view_image_modal_${post.id}`}
+ channelId={post.channel_id}
+ userId={post.user_id}
+ />
+ );
}
return (
<div className='post-body'>
{comment}
- <p key={post.id + '_message'} className={postClass}>{loading}<span>{inner}</span></p>
+ <p
+ key={`${post.id}_message`}
+ className={postClass}
+ >
+ {loading}<span>{inner}</span>
+ </p>
{fileAttachmentHolder}
{embed}
</div>
);
}
-});
+}
+
+PostBody.propTypes = {
+ post: React.PropTypes.object.isRequired,
+ parentPost: React.PropTypes.object,
+ retryPost: React.PropTypes.func.isRequired,
+ handleCommentClick: React.PropTypes.func.isRequired
+};
diff --git a/web/react/components/rename_channel_modal.jsx b/web/react/components/rename_channel_modal.jsx
index 2fe6dd96b..37958b649 100644
--- a/web/react/components/rename_channel_modal.jsx
+++ b/web/react/components/rename_channel_modal.jsx
@@ -1,147 +1,217 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
+const Utils = require('../utils/utils.jsx');
+const Client = require('../utils/client.jsx');
+const AsyncClient = require('../utils/async_client.jsx');
+const ChannelStore = require('../stores/channel_store.jsx');
-var utils = require('../utils/utils.jsx');
-var Client = require('../utils/client.jsx');
-var AsyncClient = require('../utils/async_client.jsx');
-var ChannelStore = require('../stores/channel_store.jsx');
-var TeamStore = require('../stores/team_store.jsx');
-var Constants = require('../utils/constants.jsx');
+export default class RenameChannelModal extends React.Component {
+ constructor(props) {
+ super(props);
-module.exports = React.createClass({
- handleSubmit: function(e) {
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.onNameChange = this.onNameChange.bind(this);
+ this.onDisplayNameChange = this.onDisplayNameChange.bind(this);
+ this.displayNameKeyUp = this.displayNameKeyUp.bind(this);
+ this.handleClose = this.handleClose.bind(this);
+ this.handleSubmit = this.handleSubmit.bind(this);
+
+ this.state = {
+ displayName: '',
+ channelName: '',
+ channelId: '',
+ serverError: '',
+ nameError: '',
+ displayNameError: '',
+ invalid: false
+ };
+ }
+ handleSubmit(e) {
e.preventDefault();
- if (this.state.channel_id.length !== 26) return;
+ if (this.state.channelId.length !== 26) {
+ return;
+ }
- var channel = ChannelStore.get(this.state.channel_id);
- var oldName = channel.name
- var oldDisplayName = channel.display_name
- var state = { server_error: "" };
+ let channel = ChannelStore.get(this.state.channelId);
+ const oldName = channel.name;
+ const oldDisplayName = channel.displayName;
+ let state = {serverError: ''};
- channel.display_name = this.state.display_name.trim();
+ channel.display_name = this.state.displayName.trim();
if (!channel.display_name) {
- state.display_name_error = "This field is required";
- state.inValid = true;
- }
- else if (channel.display_name.length > 22) {
- state.display_name_error = "This field must be less than 22 characters";
- state.inValid = true;
- }
- else {
- state.display_name_error = "";
+ state.displayNameError = 'This field is required';
+ state.invalid = true;
+ } else if (channel.display_name.length > 22) {
+ state.displayNameError = 'This field must be less than 22 characters';
+ state.invalid = true;
+ } else {
+ state.displayNameError = '';
}
- channel.name = this.state.channel_name.trim();
+ channel.name = this.state.channelName.trim();
if (!channel.name) {
- state.name_error = "This field is required";
- state.inValid = true;
- }
- else if(channel.name.length > 22){
- state.name_error = "This field must be less than 22 characters";
- state.inValid = true;
- }
- else {
- var cleaned_name = utils.cleanUpUrlable(channel.name);
- if (cleaned_name != channel.name) {
- state.name_error = "Must be lowercase alphanumeric characters";
- state.inValid = true;
- }
- else {
- state.name_error = "";
+ state.nameError = 'This field is required';
+ state.invalid = true;
+ } else if (channel.name.length > 22) {
+ state.nameError = 'This field must be less than 22 characters';
+ state.invalid = true;
+ } else {
+ let cleanedName = Utils.cleanUpUrlable(channel.name);
+ if (cleanedName !== channel.name) {
+ state.nameError = 'Must be lowercase alphanumeric characters';
+ state.invalid = true;
+ } else {
+ state.nameError = '';
}
}
this.setState(state);
- if (state.inValid)
- return;
-
- if (oldName == channel.name && oldDisplayName == channel.display_name)
+ if (state.invalid || (oldName === channel.name && oldDisplayName === channel.display_name)) {
return;
+ }
Client.updateChannel(channel,
- function(data, text, req) {
- $(this.refs.modal.getDOMNode()).modal('hide');
+ function handleUpdateSuccess() {
+ $(React.findDOMNode(this.refs.modal)).modal('hide');
AsyncClient.getChannel(channel.id);
- utils.updateTabTitle(channel.display_name);
- utils.updateAddressBar(channel.name);
+ Utils.updateTabTitle(channel.display_name);
+ Utils.updateAddressBar(channel.name);
- this.refs.display_name.getDOMNode().value = "";
- this.refs.channel_name.getDOMNode().value = "";
+ React.findDOMNode(this.refs.displayName).value = '';
+ React.findDOMNode(this.refs.channelName).value = '';
}.bind(this),
- function(err) {
- state.server_error = err.message;
- state.inValid = true;
+ function handleUpdateError(err) {
+ state.serverError = err.message;
+ state.invalid = true;
this.setState(state);
}.bind(this)
);
- },
- onNameChange: function() {
- this.setState({ channel_name: this.refs.channel_name.getDOMNode().value })
- },
- onDisplayNameChange: function() {
- this.setState({ display_name: this.refs.display_name.getDOMNode().value })
- },
- displayNameKeyUp: function(e) {
- var display_name = this.refs.display_name.getDOMNode().value.trim();
- var channel_name = utils.cleanUpUrlable(display_name);
- this.refs.channel_name.getDOMNode().value = channel_name;
- this.setState({ channel_name: channel_name })
- },
- handleClose: function() {
- this.setState({display_name: "", channel_name: "", display_name_error: "", server_error: "", name_error: ""});
- },
- componentDidMount: function() {
- var self = this;
- $(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
- var button = $(e.relatedTarget);
- self.setState({ display_name: button.attr('data-display'), channel_name: button.attr('data-name'), channel_id: button.attr('data-channelid') });
- });
- $(this.refs.modal.getDOMNode()).on('hidden.bs.modal', this.handleClose);
- },
- componentWillUnmount: function() {
- $(this.refs.modal.getDOMNode()).off('hidden.bs.modal', this.handleClose);
- },
- getInitialState: function() {
- return { display_name: "", channel_name: "", channel_id: "" };
- },
- render: function() {
-
- var display_name_error = this.state.display_name_error ? <label className='control-label'>{ this.state.display_name_error }</label> : null;
- var name_error = this.state.name_error ? <label className='control-label'>{ this.state.name_error }</label> : null;
- var server_error = this.state.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.state.server_error }</label></div> : null;
+ }
+ onNameChange() {
+ this.setState({channelName: React.findDOMNode(this.refs.channelName).value});
+ }
+ onDisplayNameChange() {
+ this.setState({displayName: React.findDOMNode(this.refs.displayName).value});
+ }
+ displayNameKeyUp() {
+ const displayName = React.findDOMNode(this.refs.displayName).value.trim();
+ const channelName = Utils.cleanUpUrlable(displayName);
+ React.findDOMNode(this.refs.channelName).value = channelName;
+ this.setState({channelName: channelName});
+ }
+ handleClose() {
+ this.state = {
+ displayName: '',
+ channelName: '',
+ channelId: '',
+ serverError: '',
+ nameError: '',
+ displayNameError: '',
+ invalid: false
+ };
+ }
+ componentDidMount() {
+ $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function handleShow(e) {
+ const button = $(e.relatedTarget);
+ this.setState({displayName: button.attr('data-display'), channelName: button.attr('data-name'), channelId: button.attr('data-channelid')});
+ }.bind(this));
+ $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose);
+ }
+ componentWillUnmount() {
+ $(React.findDOMNode(this.refs.modal)).off('hidden.bs.modal', this.handleClose);
+ }
+ render() {
+ let displayNameError = null;
+ let displayNameClass = 'form-group';
+ if (this.state.displayNameError) {
+ displayNameError = <label className='control-label'>{this.state.displayNameError}</label>;
+ displayNameClass += ' has-error';
+ }
+
+ let nameError = null;
+ let nameClass = 'form-group';
+ if (this.state.nameError) {
+ nameError = <label className='control-label'>{this.state.nameError}</label>;
+ nameClass += ' has-error';
+ }
+
+ let serverError = null;
+ if (this.state.serverError) {
+ serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
+ }
return (
- <div className="modal fade" ref="modal" id="rename_channel" 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">
- <span aria-hidden="true">&times;</span>
- <span className="sr-only">Close</span>
+ <div
+ className='modal fade'
+ ref='modal'
+ id='rename_channel'
+ 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'
+ >
+ <span aria-hidden='true'>&times;</span>
+ <span className='sr-only'>Close</span>
</button>
- <h4 className="modal-title">Rename Channel</h4>
+ <h4 className='modal-title'>Rename Channel</h4>
</div>
- <form role="form">
- <div className="modal-body">
- <div className={ this.state.display_name_error ? "form-group has-error" : "form-group" }>
+ <form role='form'>
+ <div className='modal-body'>
+ <div className={displayNameClass}>
<label className='control-label'>Display Name</label>
- <input onKeyUp={this.displayNameKeyUp} onChange={this.onDisplayNameChange} type="text" ref="display_name" className="form-control" placeholder="Enter display name" value={this.state.display_name} maxLength="64" />
- { display_name_error }
+ <input
+ onKeyUp={this.displayNameKeyUp}
+ onChange={this.onDisplayNameChange}
+ type='text'
+ ref='displayName'
+ className='form-control'
+ placeholder='Enter display name'
+ value={this.state.displayName}
+ maxLength='64'
+ />
+ {displayNameError}
</div>
- <div className={ this.state.name_error ? "form-group has-error" : "form-group" }>
+ <div className={nameClass}>
<label className='control-label'>Handle</label>
- <input onChange={this.onNameChange} type="text" className="form-control" ref="channel_name" placeholder="lowercase alphanumeric's only" value={this.state.channel_name} maxLength="64" />
- { name_error }
+ <input
+ onChange={this.onNameChange}
+ type='text'
+ className='form-control'
+ ref='channelName'
+ placeholder='lowercase alphanumeric&#39;s only'
+ value={this.state.channelName}
+ maxLength='64'
+ />
+ {nameError}
</div>
- { server_error }
+ {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">Save</button>
+ <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'
+ >
+ Save
+ </button>
</div>
</form>
</div>
@@ -149,4 +219,4 @@ module.exports = React.createClass({
</div>
);
}
-});
+}
diff --git a/web/react/components/rhs_header_post.jsx b/web/react/components/rhs_header_post.jsx
index 4cf4231e9..5156ec4d7 100644
--- a/web/react/components/rhs_header_post.jsx
+++ b/web/react/components/rhs_header_post.jsx
@@ -1,9 +1,9 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
-var Constants = require('../utils/constants.jsx');
-var ActionTypes = Constants.ActionTypes;
+const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
+const Constants = require('../utils/constants.jsx');
+const ActionTypes = Constants.ActionTypes;
export default class RhsHeaderPost extends React.Component {
constructor(props) {
@@ -43,7 +43,7 @@ export default class RhsHeaderPost extends React.Component {
});
}
render() {
- var back;
+ let back;
if (this.props.fromSearch) {
back = (
<a
diff --git a/web/react/components/setting_item_min.jsx b/web/react/components/setting_item_min.jsx
index 3c87e416e..098729a4f 100644
--- a/web/react/components/setting_item_min.jsx
+++ b/web/react/components/setting_item_min.jsx
@@ -1,19 +1,23 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-module.exports = React.createClass({
- displayName: 'SettingsItemMin',
- propTypes: {
- title: React.PropTypes.string,
- disableOpen: React.PropTypes.bool,
- updateSection: React.PropTypes.func,
- describe: React.PropTypes.string
- },
- render: function() {
- var editButton = '';
+export default class SettingItemMin extends React.Component {
+ render() {
+ let editButton = null;
if (!this.props.disableOpen) {
- editButton = <li className='col-sm-2 section-edit'><a className='section-edit theme' href='#' onClick={this.props.updateSection}>Edit</a></li>;
+ editButton = (
+ <li className='col-sm-2 section-edit'>
+ <a
+ className='section-edit theme'
+ href='#'
+ onClick={this.props.updateSection}
+ >
+ Edit
+ </a>
+ </li>
+ );
}
+
return (
<ul className='section-min'>
<li className='col-sm-10 section-title'>{this.props.title}</li>
@@ -22,4 +26,11 @@ module.exports = React.createClass({
</ul>
);
}
-});
+}
+
+SettingItemMin.propTypes = {
+ title: React.PropTypes.string,
+ disableOpen: React.PropTypes.bool,
+ updateSection: React.PropTypes.func,
+ describe: React.PropTypes.string
+};
diff --git a/web/react/components/settings_sidebar.jsx b/web/react/components/settings_sidebar.jsx
index d8091ec28..e5cbd6e92 100644
--- a/web/react/components/settings_sidebar.jsx
+++ b/web/react/components/settings_sidebar.jsx
@@ -1,24 +1,56 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var utils = require('../utils/utils.jsx');
+export default class SettingsSidebar extends React.Component {
+ constructor(props) {
+ super(props);
-module.exports = React.createClass({
- displayName:'SettingsSidebar',
- updateTab: function(tab) {
- this.props.updateTab(tab);
+ this.handleClick = this.handleClick.bind(this);
+ }
+ handleClick(tab) {
+ this.props.updateTab(tab.name);
$('.settings-modal').addClass('display--content');
- },
- render: function() {
- var self = this;
+ }
+ render() {
+ let tabList = this.props.tabs.map(function makeTab(tab) {
+ let key = `${tab.name}_li`;
+ let className = '';
+ if (this.props.activeTab === tab.name) {
+ className = 'active';
+ }
+
+ return (
+ <li
+ key={key}
+ className={className}
+ >
+ <a
+ href='#'
+ onClick={this.handleClick.bind(null, tab)}
+ >
+ <i className={tab.icon} />
+ {tab.uiName}
+ </a>
+ </li>
+ );
+ }.bind(this));
+
return (
- <div className="">
- <ul className="nav nav-pills nav-stacked">
- {this.props.tabs.map(function(tab) {
- return <li key={tab.name+'_li'} className={self.props.activeTab == tab.name ? 'active' : ''}><a key={tab.name + '_a'} href="#" onClick={function(){self.updateTab(tab.name);}}><i key={tab.name+'_i'} className={tab.icon}></i>{tab.uiName}</a></li>
- })}
+ <div>
+ <ul className='nav nav-pills nav-stacked'>
+ {tabList}
</ul>
</div>
);
}
-});
+}
+
+SettingsSidebar.propTypes = {
+ tabs: React.PropTypes.arrayOf(React.PropTypes.shape({
+ name: React.PropTypes.string.isRequired,
+ uiName: React.PropTypes.string.isRequired,
+ icon: React.PropTypes.string.isRequired
+ })).isRequired,
+ activeTab: React.PropTypes.string,
+ updateTab: React.PropTypes.func.isRequired
+};
diff --git a/web/react/components/signup_team.jsx b/web/react/components/signup_team.jsx
index 13640b1e5..bf08e6508 100644
--- a/web/react/components/signup_team.jsx
+++ b/web/react/components/signup_team.jsx
@@ -1,10 +1,10 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var ChoosePage = require('./team_signup_choose_auth.jsx');
-var EmailSignUpPage = require('./team_signup_with_email.jsx');
-var SSOSignupPage = require('./team_signup_with_sso.jsx');
-var Constants = require('../utils/constants.jsx');
+const ChoosePage = require('./team_signup_choose_auth.jsx');
+const EmailSignUpPage = require('./team_signup_with_email.jsx');
+const SSOSignupPage = require('./team_signup_with_sso.jsx');
+const Constants = require('../utils/constants.jsx');
export default class TeamSignUp extends React.Component {
constructor(props) {
@@ -30,14 +30,14 @@ export default class TeamSignUp extends React.Component {
return <EmailSignUpPage />;
} else if (this.state.page === 'service' && this.state.service !== '') {
return <SSOSignupPage service={this.state.service} />;
- } else {
- return (
- <ChoosePage
- services={this.props.services}
- updatePage={this.updatePage}
- />
- );
}
+
+ return (
+ <ChoosePage
+ services={this.props.services}
+ updatePage={this.updatePage}
+ />
+ );
}
}
diff --git a/web/react/components/team_general_tab.jsx b/web/react/components/team_general_tab.jsx
index fd2a22731..2966a8a9a 100644
--- a/web/react/components/team_general_tab.jsx
+++ b/web/react/components/team_general_tab.jsx
@@ -1,11 +1,11 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var SettingItemMin = require('./setting_item_min.jsx');
-var SettingItemMax = require('./setting_item_max.jsx');
+const SettingItemMin = require('./setting_item_min.jsx');
+const SettingItemMax = require('./setting_item_max.jsx');
-var client = require('../utils/client.jsx');
-var utils = require('../utils/utils.jsx');
+const Client = require('../utils/client.jsx');
+const Utils = require('../utils/utils.jsx');
export default class GeneralTab extends React.Component {
constructor(props) {
@@ -21,10 +21,10 @@ export default class GeneralTab extends React.Component {
handleNameSubmit(e) {
e.preventDefault();
- var state = {serverError: '', clientError: ''};
- var valid = true;
+ let state = {serverError: '', clientError: ''};
+ let valid = true;
- var name = this.state.name.trim();
+ const name = this.state.name.trim();
if (!name) {
state.clientError = 'This field is required';
valid = false;
@@ -41,10 +41,10 @@ export default class GeneralTab extends React.Component {
return;
}
- var data = {};
+ let data = {};
data.new_name = name;
- client.updateTeamDisplayName(data,
+ Client.updateTeamDisplayName(data,
function nameChangeSuccess() {
this.props.updateSection('');
$('#team_settings').modal('hide');
@@ -84,8 +84,8 @@ export default class GeneralTab extends React.Component {
this.setState({name: e.target.value});
}
render() {
- var clientError = null;
- var serverError = null;
+ let clientError = null;
+ let serverError = null;
if (this.state.clientError) {
clientError = this.state.clientError;
}
@@ -93,18 +93,21 @@ export default class GeneralTab extends React.Component {
serverError = this.state.serverError;
}
- var nameSection;
+ let nameSection;
if (this.props.activeSection === 'name') {
let inputs = [];
- let teamNameLabel = utils.toTitleCase(strings.Team) + ' Name';
- if (utils.isMobile()) {
+ let teamNameLabel = Utils.toTitleCase(strings.Team) + ' Name';
+ if (Utils.isMobile()) {
teamNameLabel = '';
}
inputs.push(
- <div key='teamNameSetting' className='form-group'>
+ <div
+ key='teamNameSetting'
+ className='form-group'
+ >
<label className='col-sm-5 control-label'>{teamNameLabel}</label>
<div className='col-sm-7'>
<input
@@ -119,7 +122,7 @@ export default class GeneralTab extends React.Component {
nameSection = (
<SettingItemMax
- title={utils.toTitleCase(strings.Team) + ' Name'}
+ title={`${Utils.toTitleCase(strings.Team)} Name`}
inputs={inputs}
submit={this.handleNameSubmit}
server_error={serverError}
@@ -132,7 +135,7 @@ export default class GeneralTab extends React.Component {
nameSection = (
<SettingItemMin
- title={utils.toTitleCase(strings.Team) + ' Name'}
+ title={`${Utils.toTitleCase(strings.Team)} Name`}
describe={describe}
updateSection={this.onUpdateSection}
/>
diff --git a/web/react/components/team_settings_modal.jsx b/web/react/components/team_settings_modal.jsx
index 7e65e8cab..668bf76cf 100644
--- a/web/react/components/team_settings_modal.jsx
+++ b/web/react/components/team_settings_modal.jsx
@@ -1,70 +1,96 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var SettingsSidebar = require('./settings_sidebar.jsx');
-var TeamSettings = require('./team_settings.jsx');
+const SettingsSidebar = require('./settings_sidebar.jsx');
+const TeamSettings = require('./team_settings.jsx');
-module.exports = React.createClass({
- displayName: 'Team Settings Modal',
- propTypes: {
- teamDisplayName: React.PropTypes.string.isRequired
- },
- componentDidMount: function() {
- $('body').on('click', '.modal-back', function onClick() {
+export default class TeamSettingsModal extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.updateTab = this.updateTab.bind(this);
+ this.updateSection = this.updateSection.bind(this);
+
+ this.state = {
+ activeTab: 'general',
+ activeSection: ''
+ };
+ }
+ componentDidMount() {
+ $('body').on('click', '.modal-back', function handleBackClick() {
$(this).closest('.modal-dialog').removeClass('display--content');
});
- $('body').on('click', '.modal-header .close', function onClick() {
+ $('body').on('click', '.modal-header .close', function handleCloseClick() {
setTimeout(function removeContent() {
$('.modal-dialog.display--content').removeClass('display--content');
}, 500);
});
- },
- updateTab: function(tab) {
+ }
+ updateTab(tab) {
this.setState({activeTab: tab, activeSection: ''});
- },
- updateSection: function(section) {
+ }
+ updateSection(section) {
this.setState({activeSection: section});
- },
- getInitialState: function() {
- return {activeTab: 'general', activeSection: ''};
- },
- render: function() {
- var tabs = [];
+ }
+ render() {
+ let tabs = [];
tabs.push({name: 'general', uiName: 'General', icon: 'glyphicon glyphicon-cog'});
tabs.push({name: 'import', uiName: 'Import', icon: 'glyphicon glyphicon-upload'});
tabs.push({name: 'feature', uiName: 'Advanced', icon: 'glyphicon glyphicon-wrench'});
return (
- <div className='modal fade' ref='modal' id='team_settings' role='dialog' tabIndex='-1' aria-hidden='true'>
- <div className='modal-dialog settings-modal'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button>
- <h4 className='modal-title' ref='title'>Team Settings</h4>
- </div>
- <div className='modal-body'>
- <div className='settings-table'>
- <div className='settings-links'>
- <SettingsSidebar
- tabs={tabs}
- activeTab={this.state.activeTab}
- updateTab={this.updateTab}
- />
+ <div
+ className='modal fade'
+ ref='modal'
+ id='team_settings'
+ role='dialog'
+ tabIndex='-1'
+ aria-hidden='true'
+ >
+ <div className='modal-dialog settings-modal'>
+ <div className='modal-content'>
+ <div className='modal-header'>
+ <button
+ type='button'
+ className='close'
+ data-dismiss='modal'
+ aria-label='Close'
+ >
+ <span aria-hidden='true'>&times;</span>
+ </button>
+ <h4
+ className='modal-title'
+ ref='title'
+ >
+ Team Settings
+ </h4>
</div>
- <div className='settings-content minimize-settings'>
- <TeamSettings
- activeTab={this.state.activeTab}
- activeSection={this.state.activeSection}
- updateSection={this.updateSection}
- teamDisplayName={this.props.teamDisplayName}
- />
+ <div className='modal-body'>
+ <div className='settings-table'>
+ <div className='settings-links'>
+ <SettingsSidebar
+ tabs={tabs}
+ activeTab={this.state.activeTab}
+ updateTab={this.updateTab}
+ />
+ </div>
+ <div className='settings-content minimize-settings'>
+ <TeamSettings
+ activeTab={this.state.activeTab}
+ activeSection={this.state.activeSection}
+ updateSection={this.updateSection}
+ teamDisplayName={this.props.teamDisplayName}
+ />
+ </div>
+ </div>
</div>
</div>
- </div>
</div>
- </div>
</div>
);
}
-});
+}
+TeamSettingsModal.propTypes = {
+ teamDisplayName: React.PropTypes.string.isRequired
+};
diff --git a/web/react/components/team_signup_email_item.jsx b/web/react/components/team_signup_email_item.jsx
index 11cd17e74..10bb2d69e 100644
--- a/web/react/components/team_signup_email_item.jsx
+++ b/web/react/components/team_signup_email_item.jsx
@@ -1,28 +1,28 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var utils = require('../utils/utils.jsx');
-
-module.exports = React.createClass({
- displayName: 'TeamSignupEmailItem',
- propTypes: {
- focus: React.PropTypes.bool,
- email: React.PropTypes.string
- },
- getInitialState: function() {
- return {};
- },
- getValue: function() {
- return this.refs.email.getDOMNode().value.trim();
- },
- validate: function(teamEmail) {
- var email = this.refs.email.getDOMNode().value.trim().toLowerCase();
+const Utils = require('../utils/utils.jsx');
+
+export default class TeamSignupEmailItem extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.getValue = this.getValue.bind(this);
+ this.validate = this.validate.bind(this);
+
+ this.state = {};
+ }
+ getValue() {
+ return React.findDOMNode(this.refs.email).value.trim();
+ }
+ validate(teamEmail) {
+ const email = React.findDOMNode(this.refs.email).value.trim().toLowerCase();
if (!email) {
return true;
}
- if (!utils.isEmail(email)) {
+ if (!Utils.isEmail(email)) {
this.state.emailError = 'Please enter a valid email address';
this.setState(this.state);
return false;
@@ -31,13 +31,14 @@ module.exports = React.createClass({
this.setState(this.state);
return false;
}
+
this.state.emailError = '';
this.setState(this.state);
return true;
- },
- render: function() {
- var emailError = null;
- var emailDivClass = 'form-group';
+ }
+ render() {
+ let emailError = null;
+ let emailDivClass = 'form-group';
if (this.state.emailError) {
emailError = <label className='control-label'>{this.state.emailError}</label>;
emailDivClass += ' has-error';
@@ -45,9 +46,22 @@ module.exports = React.createClass({
return (
<div className={emailDivClass}>
- <input autoFocus={this.props.focus} type='email' ref='email' className='form-control' placeholder='Email Address' defaultValue={this.props.email} maxLength='128' />
+ <input
+ autoFocus={this.props.focus}
+ type='email'
+ ref='email'
+ className='form-control'
+ placeholder='Email Address'
+ defaultValue={this.props.email}
+ maxLength='128'
+ />
{emailError}
</div>
);
}
-});
+}
+
+TeamSignupEmailItem.propTypes = {
+ focus: React.PropTypes.bool,
+ email: React.PropTypes.string
+};
diff --git a/web/react/components/team_signup_url_page.jsx b/web/react/components/team_signup_url_page.jsx
index beef725e2..2ea6c3680 100644
--- a/web/react/components/team_signup_url_page.jsx
+++ b/web/react/components/team_signup_url_page.jsx
@@ -1,33 +1,37 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var utils = require('../utils/utils.jsx');
-var client = require('../utils/client.jsx');
-var constants = require('../utils/constants.jsx');
-
-module.exports = React.createClass({
- displayName: 'TeamSignupURLPage',
- propTypes: {
- state: React.PropTypes.object,
- updateParent: React.PropTypes.func
- },
- submitBack: function(e) {
+const Utils = require('../utils/utils.jsx');
+const Client = require('../utils/client.jsx');
+const Constants = require('../utils/constants.jsx');
+
+export default class TeamSignupUrlPage extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.submitBack = this.submitBack.bind(this);
+ this.submitNext = this.submitNext.bind(this);
+ this.handleFocus = this.handleFocus.bind(this);
+
+ this.state = {nameError: ''};
+ }
+ submitBack(e) {
e.preventDefault();
this.props.state.wizard = 'team_display_name';
this.props.updateParent(this.props.state);
- },
- submitNext: function(e) {
+ }
+ submitNext(e) {
e.preventDefault();
- var name = this.refs.name.getDOMNode().value.trim();
+ const name = React.findDOMNode(this.refs.name).value.trim();
if (!name) {
this.setState({nameError: 'This field is required'});
return;
}
- var cleanedName = utils.cleanUpUrlable(name);
+ const cleanedName = Utils.cleanUpUrlable(name);
- var urlRegex = /^[a-z]+([a-z\-0-9]+|(__)?)[a-z0-9]+$/g;
+ const urlRegex = /^[a-z]+([a-z\-0-9]+|(__)?)[a-z0-9]+$/g;
if (cleanedName !== name || !urlRegex.test(name)) {
this.setState({nameError: "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."});
return;
@@ -36,14 +40,14 @@ module.exports = React.createClass({
return;
}
- for (var index = 0; index < constants.RESERVED_TEAM_NAMES.length; index++) {
- if (cleanedName.indexOf(constants.RESERVED_TEAM_NAMES[index]) === 0) {
+ for (let index = 0; index < Constants.RESERVED_TEAM_NAMES.length; index++) {
+ if (cleanedName.indexOf(Constants.RESERVED_TEAM_NAMES[index]) === 0) {
this.setState({nameError: 'This team name is unavailable'});
return;
}
}
- client.findTeamByName(name,
+ Client.findTeamByName(name,
function success(data) {
if (!data) {
if (config.AllowSignupDomainsWizard) {
@@ -65,55 +69,88 @@ module.exports = React.createClass({
this.setState(this.state);
}.bind(this)
);
- },
- getInitialState: function() {
- return {};
- },
- handleFocus: function(e) {
+ }
+ handleFocus(e) {
e.preventDefault();
e.currentTarget.select();
- },
- render: function() {
+ }
+ render() {
$('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'});
- client.track('signup', 'signup_team_03_url');
+ Client.track('signup', 'signup_team_03_url');
- var nameError = null;
- var nameDivClass = 'form-group';
+ let nameError = null;
+ let nameDivClass = 'form-group';
if (this.state.nameError) {
nameError = <label className='control-label'>{this.state.nameError}</label>;
nameDivClass += ' has-error';
}
+ const title = `${Utils.getWindowLocationOrigin()}/`;
+
return (
<div>
<form>
- <img className='signup-team-logo' src='/static/images/logo.png' />
- <h2>{utils.toTitleCase(strings.Team) + ' URL'}</h2>
+ <img
+ className='signup-team-logo'
+ src='/static/images/logo.png'
+ />
+ <h2>{`${Utils.toTitleCase(strings.Team)} URL`}</h2>
<div className={nameDivClass}>
<div className='row'>
<div className='col-sm-11'>
<div className='input-group input-group--limit'>
- <span data-toggle='tooltip' title={utils.getWindowLocationOrigin() + '/'} className='input-group-addon'>{utils.getWindowLocationOrigin() + '/'}</span>
- <input type='text' ref='name' className='form-control' placeholder='' maxLength='128' defaultValue={this.props.state.team.name} autoFocus={true} onFocus={this.handleFocus}/>
+ <span
+ data-toggle='tooltip'
+ title={title}
+ className='input-group-addon'
+ >
+ {title}
+ </span>
+ <input
+ type='text'
+ ref='name'
+ className='form-control'
+ placeholder=''
+ maxLength='128'
+ defaultValue={this.props.state.team.name}
+ autoFocus={true}
+ onFocus={this.handleFocus}
+ />
</div>
</div>
</div>
{nameError}
</div>
- <p>{'Choose the web address of your new ' + strings.Team + ':'}</p>
+ <p>{`Choose the web address of your new ${strings.Team}:`}</p>
<ul className='color--light'>
<li>Short and memorable is best</li>
<li>Use lowercase letters, numbers and dashes</li>
<li>Must start with a letter and can't end in a dash</li>
</ul>
- <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>
);
}
-});
+}
+
+TeamSignupUrlPage.propTypes = {
+ state: React.PropTypes.object,
+ updateParent: React.PropTypes.func
+};
diff --git a/web/react/components/team_signup_with_email.jsx b/web/react/components/team_signup_with_email.jsx
index c7204880f..c0bbb7da9 100644
--- a/web/react/components/team_signup_with_email.jsx
+++ b/web/react/components/team_signup_with_email.jsx
@@ -1,8 +1,8 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var utils = require('../utils/utils.jsx');
-var client = require('../utils/client.jsx');
+const Utils = require('../utils/utils.jsx');
+const Client = require('../utils/client.jsx');
export default class EmailSignUpPage extends React.Component {
constructor() {
@@ -14,11 +14,11 @@ export default class EmailSignUpPage extends React.Component {
}
handleSubmit(e) {
e.preventDefault();
- var team = {};
- var state = {serverError: ''};
+ let team = {};
+ let state = {serverError: ''};
- team.email = this.refs.email.getDOMNode().value.trim().toLowerCase();
- if (!team.email || !utils.isEmail(team.email)) {
+ team.email = React.findDOMNode(this.refs.email).value.trim().toLowerCase();
+ if (!team.email || !Utils.isEmail(team.email)) {
state.emailError = 'Please enter a valid email address';
state.inValid = true;
} else {
@@ -30,12 +30,12 @@ export default class EmailSignUpPage extends React.Component {
return;
}
- client.signupTeam(team.email,
+ Client.signupTeam(team.email,
function success(data) {
if (data.follow_link) {
window.location.href = data.follow_link;
} else {
- window.location.href = '/signup_team_confirm/?email=' + encodeURIComponent(team.email);
+ window.location.href = `/signup_team_confirm/?email=${encodeURIComponent(team.email)}`;
}
},
function fail(err) {
@@ -69,7 +69,7 @@ export default class EmailSignUpPage extends React.Component {
</button>
</div>
<div className='form-group margin--extra-2x'>
- <span><a href='/find_team'>{'Find my ' + strings.Team}</a></span>
+ <span><a href='/find_team'>{`Find my ${strings.Team}`}</a></span>
</div>
</form>
);
diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx
index efd2dd810..0408a262d 100644
--- a/web/react/components/textbox.jsx
+++ b/web/react/components/textbox.jsx
@@ -1,66 +1,93 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
-var PostStore = require('../stores/post_store.jsx');
-var CommandList = require('./command_list.jsx');
-var ErrorStore = require('../stores/error_store.jsx');
-var AsyncClient = require('../utils/async_client.jsx');
-
-var utils = require('../utils/utils.jsx');
-var Constants = require('../utils/constants.jsx');
-var ActionTypes = Constants.ActionTypes;
+const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
+const PostStore = require('../stores/post_store.jsx');
+const CommandList = require('./command_list.jsx');
+const ErrorStore = require('../stores/error_store.jsx');
+const AsyncClient = require('../utils/async_client.jsx');
+
+const Utils = require('../utils/utils.jsx');
+const Constants = require('../utils/constants.jsx');
+const ActionTypes = Constants.ActionTypes;
+
+export default class Textbox extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.getStateFromStores = this.getStateFromStores.bind(this);
+ this.onListenerChange = this.onListenerChange.bind(this);
+ this.onRecievedError = this.onRecievedError.bind(this);
+ this.onTimerInterrupt = this.onTimerInterrupt.bind(this);
+ this.updateMentionTab = this.updateMentionTab.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ this.handleKeyPress = this.handleKeyPress.bind(this);
+ this.handleKeyDown = this.handleKeyDown.bind(this);
+ this.handleBackspace = this.handleBackspace.bind(this);
+ this.checkForNewMention = this.checkForNewMention.bind(this);
+ this.addMention = this.addMention.bind(this);
+ this.addCommand = this.addCommand.bind(this);
+ this.resize = this.resize.bind(this);
+ this.handleFocus = this.handleFocus.bind(this);
+ this.handleBlur = this.handleBlur.bind(this);
+ this.handlePaste = this.handlePaste.bind(this);
+
+ this.state = {
+ mentionText: '-1',
+ mentions: [],
+ connection: '',
+ timerInterrupt: null
+ };
+
+ this.caret = -1;
+ this.addedMention = false;
+ this.doProcessMentions = false;
+ this.mentions = [];
+ }
+ getStateFromStores() {
+ const error = ErrorStore.getLastError();
-function getStateFromStores() {
- var error = ErrorStore.getLastError();
+ if (error) {
+ return {message: error.message};
+ }
- if (error) {
- return {message: error.message};
+ return {message: null};
}
- return {message: null};
-}
-
-module.exports = React.createClass({
- displayName: 'Textbox',
- caret: -1,
- addedMention: false,
- doProcessMentions: false,
- mentions: [],
- componentDidMount: function() {
+ componentDidMount() {
PostStore.addAddMentionListener(this.onListenerChange);
ErrorStore.addChangeListener(this.onRecievedError);
this.resize();
this.updateMentionTab(null);
- },
- componentWillUnmount: function() {
+ }
+ componentWillUnmount() {
PostStore.removeAddMentionListener(this.onListenerChange);
ErrorStore.removeChangeListener(this.onRecievedError);
- },
- onListenerChange: function(id, username) {
+ }
+ onListenerChange(id, username) {
if (id === this.props.id) {
this.addMention(username);
}
- },
- onRecievedError: function() {
- var errorState = getStateFromStores();
+ }
+ onRecievedError() {
+ const errorState = this.getStateFromStores();
- if (this.state.timerInterrupt != null) {
+ if (this.state.timerInterrupt !== null) {
window.clearInterval(this.state.timerInterrupt);
this.setState({timerInterrupt: null});
}
if (errorState.message === 'There appears to be a problem with your internet connection') {
this.setState({connection: 'bad-connection'});
- var timerInterrupt = window.setInterval(this.onTimerInterrupt, 5000);
+ const timerInterrupt = window.setInterval(this.onTimerInterrupt, 5000);
this.setState({timerInterrupt: timerInterrupt});
} else {
this.setState({connection: ''});
}
- },
- onTimerInterrupt: function() {
- //Since these should only happen when you have no connection and slightly briefly after any
- //performance hit should not matter
+ }
+ onTimerInterrupt() {
+ // Since these should only happen when you have no connection and slightly briefly after any
+ // performance hit should not matter
if (this.state.connection === 'bad-connection') {
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_ERROR,
@@ -72,10 +99,10 @@ module.exports = React.createClass({
window.clearInterval(this.state.timerInterrupt);
this.setState({timerInterrupt: null});
- },
- componentDidUpdate: function() {
+ }
+ componentDidUpdate() {
if (this.caret >= 0) {
- utils.setCaretPosition(this.refs.message.getDOMNode(), this.caret);
+ Utils.setCaretPosition(React.findDOMNode(this.refs.message), this.caret);
this.caret = -1;
}
if (this.doProcessMentions) {
@@ -83,40 +110,35 @@ module.exports = React.createClass({
this.doProcessMentions = false;
}
this.resize();
- },
- componentWillReceiveProps: function(nextProps) {
+ }
+ componentWillReceiveProps(nextProps) {
if (!this.addedMention) {
this.checkForNewMention(nextProps.messageText);
}
- var text = this.refs.message.getDOMNode().value;
+ const text = React.findDOMNode(this.refs.message).value;
if (nextProps.channelId !== this.props.channelId || nextProps.messageText !== text) {
this.doProcessMentions = true;
}
this.addedMention = false;
this.refs.commands.getSuggestedCommands(nextProps.messageText);
this.resize();
- },
- getInitialState: function() {
- return {mentionText: '-1', mentions: [], connection: '', timerInterrupt: null};
- },
- updateMentionTab: function(mentionText) {
- var self = this;
-
+ }
+ updateMentionTab(mentionText) {
// using setTimeout so dispatch isn't called during an in progress dispatch
- setTimeout(function() {
+ setTimeout(function updateMentionTabAfterTimeout() {
AppDispatcher.handleViewAction({
type: ActionTypes.RECIEVED_MENTION_DATA,
- id: self.props.id,
+ id: this.props.id,
mention_text: mentionText
});
- }, 1);
- },
- handleChange: function() {
- this.props.onUserInput(this.refs.message.getDOMNode().value);
+ }.bind(this), 1);
+ }
+ handleChange() {
+ this.props.onUserInput(React.findDOMNode(this.refs.message).value);
this.resize();
- },
- handleKeyPress: function(e) {
- var text = this.refs.message.getDOMNode().value;
+ }
+ handleKeyPress(e) {
+ const text = React.findDOMNode(this.refs.message).value;
if (!this.refs.commands.isEmpty() && text.indexOf('/') === 0 && e.which === 13) {
this.refs.commands.addFirstCommand();
@@ -125,10 +147,10 @@ module.exports = React.createClass({
}
if (!this.doProcessMentions) {
- var caret = utils.getCaretPosition(this.refs.message.getDOMNode());
- var preText = text.substring(0, caret);
- var lastSpace = preText.lastIndexOf(' ');
- var lastAt = preText.lastIndexOf('@');
+ const caret = Utils.getCaretPosition(React.findDOMNode(this.refs.message));
+ const preText = text.substring(0, caret);
+ const lastSpace = preText.lastIndexOf(' ');
+ const lastAt = preText.lastIndexOf('@');
if (caret > lastAt && lastSpace < lastAt) {
this.doProcessMentions = true;
@@ -136,18 +158,18 @@ module.exports = React.createClass({
}
this.props.onKeyPress(e);
- },
- handleKeyDown: function(e) {
- if (utils.getSelectedText(this.refs.message.getDOMNode()) !== '') {
+ }
+ handleKeyDown(e) {
+ if (Utils.getSelectedText(React.findDOMNode(this.refs.message)) !== '') {
this.doProcessMentions = true;
}
if (e.keyCode === 8) {
this.handleBackspace(e);
}
- },
- handleBackspace: function() {
- var text = this.refs.message.getDOMNode().value;
+ }
+ handleBackspace() {
+ const text = React.findDOMNode(this.refs.message).value;
if (text.indexOf('/') === 0) {
this.refs.commands.getSuggestedCommands(text.substring(0, text.length - 1));
}
@@ -156,21 +178,21 @@ module.exports = React.createClass({
return;
}
- var caret = utils.getCaretPosition(this.refs.message.getDOMNode());
- var preText = text.substring(0, caret);
- var lastSpace = preText.lastIndexOf(' ');
- var lastAt = preText.lastIndexOf('@');
+ const caret = Utils.getCaretPosition(React.findDOMNode(this.refs.message));
+ const preText = text.substring(0, caret);
+ const lastSpace = preText.lastIndexOf(' ');
+ const lastAt = preText.lastIndexOf('@');
if (caret > lastAt && (lastSpace > lastAt || lastSpace === -1)) {
this.doProcessMentions = true;
}
- },
- checkForNewMention: function(text) {
- var caret = utils.getCaretPosition(this.refs.message.getDOMNode());
+ }
+ checkForNewMention(text) {
+ const caret = Utils.getCaretPosition(React.findDOMNode(this.refs.message));
- var preText = text.substring(0, caret);
+ const preText = text.substring(0, caret);
- var atIndex = preText.lastIndexOf('@');
+ const atIndex = preText.lastIndexOf('@');
// The @ character not typed, so nothing to do.
if (atIndex === -1) {
@@ -178,8 +200,8 @@ module.exports = React.createClass({
return;
}
- var lastCharSpace = preText.lastIndexOf(String.fromCharCode(160));
- var lastSpace = preText.lastIndexOf(' ');
+ const lastCharSpace = preText.lastIndexOf(String.fromCharCode(160));
+ const lastSpace = preText.lastIndexOf(' ');
// If there is a space after the last @, nothing to do.
if (lastSpace > atIndex || lastCharSpace > atIndex) {
@@ -188,43 +210,43 @@ module.exports = React.createClass({
}
// Get the name typed so far.
- var name = preText.substring(atIndex + 1, preText.length).toLowerCase();
+ const name = preText.substring(atIndex + 1, preText.length).toLowerCase();
this.updateMentionTab(name);
- },
- addMention: function(name) {
- var caret = utils.getCaretPosition(this.refs.message.getDOMNode());
+ }
+ addMention(name) {
+ const caret = Utils.getCaretPosition(React.findDOMNode(this.refs.message));
- var text = this.props.messageText;
+ const text = this.props.messageText;
- var preText = text.substring(0, caret);
+ const preText = text.substring(0, caret);
- var atIndex = preText.lastIndexOf('@');
+ const atIndex = preText.lastIndexOf('@');
// The @ character not typed, so nothing to do.
if (atIndex === -1) {
return;
}
- var prefix = text.substring(0, atIndex);
- var suffix = text.substring(caret, text.length);
+ const prefix = text.substring(0, atIndex);
+ const suffix = text.substring(caret, text.length);
this.caret = prefix.length + name.length + 2;
this.addedMention = true;
this.doProcessMentions = true;
- this.props.onUserInput(prefix + '@' + name + ' ' + suffix);
- },
- addCommand: function(cmd) {
- var elm = this.refs.message.getDOMNode();
+ this.props.onUserInput(`${prefix}@${name} ${suffix}`);
+ }
+ addCommand(cmd) {
+ const elm = React.findDOMNode(this.refs.message);
elm.value = cmd;
this.handleChange();
- },
- resize: function() {
- var e = this.refs.message.getDOMNode();
- var w = this.refs.wrapper.getDOMNode();
+ }
+ resize() {
+ const e = React.findDOMNode(this.refs.message);
+ const w = React.findDOMNode(this.refs.wrapper);
- var lht = parseInt($(e).css('lineHeight'), 10);
- var lines = e.scrollHeight / lht;
- var mod = 15;
+ const lht = parseInt($(e).css('lineHeight'), 10);
+ const lines = e.scrollHeight / lht;
+ let mod = 15;
if (lines < 2.5 || this.props.messageText === '') {
mod = 30;
@@ -237,28 +259,62 @@ module.exports = React.createClass({
$(e).css({height: 'auto', 'overflow-y': 'scroll'}).height(167);
$(w).css({height: 'auto'}).height(167);
}
- },
- handleFocus: function() {
- var elm = this.refs.message.getDOMNode();
+ }
+ handleFocus() {
+ const elm = React.findDOMNode(this.refs.message);
if (elm.title === elm.value) {
elm.value = '';
}
- },
- handleBlur: function() {
- var elm = this.refs.message.getDOMNode();
+ }
+ handleBlur() {
+ const elm = React.findDOMNode(this.refs.message);
if (elm.value === '') {
elm.value = elm.title;
}
- },
- handlePaste: function() {
+ }
+ handlePaste() {
this.doProcessMentions = true;
- },
- render: function() {
+ }
+ render() {
return (
- <div ref='wrapper' className='textarea-wrapper'>
- <CommandList ref='commands' addCommand={this.addCommand} channelId={this.props.channelId} />
- <textarea id={this.props.id} ref='message' className={'form-control custom-textarea ' + this.state.connection} spellCheck='true' autoComplete='off' autoCorrect='off' rows='1' maxLength={Constants.MAX_POST_LEN} placeholder={this.props.createMessage} value={this.props.messageText} onInput={this.handleChange} onChange={this.handleChange} onKeyPress={this.handleKeyPress} onKeyDown={this.handleKeyDown} onFocus={this.handleFocus} onBlur={this.handleBlur} onPaste={this.handlePaste} />
+ <div
+ ref='wrapper'
+ className='textarea-wrapper'
+ >
+ <CommandList
+ ref='commands'
+ addCommand={this.addCommand}
+ channelId={this.props.channelId}
+ />
+ <textarea
+ id={this.props.id}
+ ref='message'
+ className={`form-control custom-textarea ${this.state.connection}`}
+ spellCheck='true'
+ autoComplete='off'
+ autoCorrect='off'
+ rows='1'
+ maxLength={Constants.MAX_POST_LEN}
+ placeholder={this.props.createMessage}
+ value={this.props.messageText}
+ onInput={this.handleChange}
+ onChange={this.handleChange}
+ onKeyPress={this.handleKeyPress}
+ onKeyDown={this.handleKeyDown}
+ onFocus={this.handleFocus}
+ onBlur={this.handleBlur}
+ onPaste={this.handlePaste}
+ />
</div>
);
}
-});
+}
+
+Textbox.propTypes = {
+ id: React.PropTypes.string.isRequired,
+ channelId: React.PropTypes.string,
+ messageText: React.PropTypes.string.isRequired,
+ onUserInput: React.PropTypes.func.isRequired,
+ onKeyPress: React.PropTypes.func.isRequired,
+ createMessage: React.PropTypes.string.isRequired
+};
diff --git a/web/react/components/user_settings_general.jsx b/web/react/components/user_settings_general.jsx
index ddd2fb607..df4d02820 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 = '';
});