Этот коммит содержится в:
=Corey Hulen
2015-09-01 17:06:31 -07:00
родитель 72575ac7bd
Коммит f578bb1e48
18 изменённых файлов: 1003 добавлений и 459 удалений

Просмотреть файл

@@ -3,57 +3,86 @@
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
module.exports = React.createClass({ export default class CommandList extends React.Component {
componentDidMount: function() { constructor(props) {
super(props);
this.state = {
channel_id: ChannelStore.getCurrentId()
};
}
componentDidMount() {
var self = this; var self = this;
if(this.refs.modal) { if (this.refs.modal) {
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) { $(this.refs.modal.getDOMNode()).on('show.bs.modal', function show(e) {
var button = e.relatedTarget; var button = e.relatedTarget;
self.setState({ channel_id: $(button).attr('data-channelid') }); self.setState({channel_id: $(button).attr('data-channelid')});
}); });
} }
}, }
getInitialState: function() {
return { channel_id: ChannelStore.getCurrentId() }; render() {
},
render: function() {
var channel = ChannelStore.get(this.state.channel_id); var channel = ChannelStore.get(this.state.channel_id);
if (!channel) { if (!channel) {
channel = {}; channel = {};
channel.display_name = "No Channel Found"; channel.display_name = 'No Channel Found';
channel.name = "No Channel Found"; channel.name = 'No Channel Found';
channel.id = "No Channel Found"; channel.id = 'No Channel Found';
} }
return ( return (
<div className="modal fade" ref="modal" id="channel_info" tabIndex="-1" role="dialog" aria-hidden="true"> <div
<div className="modal-dialog"> className='modal fade'
<div className="modal-content"> ref='modal'
<div className="modal-header"> id='channel_info'
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> tabIndex='-1'
<h4 className="modal-title" id="myModalLabel"><span className="name">{channel.display_name}</span></h4> 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'>&times;</span>
</button>
<h4
className='modal-title'
id='myModalLabel'
>
<span className='name'>{channel.display_name}</span>
</h4>
</div> </div>
<div className="modal-body"> <div className='modal-body'>
<div className="row form-group"> <div className='row form-group'>
<div className="col-sm-3 info__label">Channel Name: </div> <div className='col-sm-3 info__label'>Channel Name: </div>
<div className="col-sm-9">{channel.display_name}</div> <div className='col-sm-9'>{channel.display_name}</div>
</div> </div>
<div className="row form-group"> <div className='row form-group'>
<div className="col-sm-3 info__label">Channel Handle:</div> <div className='col-sm-3 info__label'>Channel Handle:</div>
<div className="col-sm-9">{channel.name}</div> <div className='col-sm-9'>{channel.name}</div>
</div> </div>
<div className="row"> <div className='row'>
<div className="col-sm-3 info__label">Channel ID:</div> <div className='col-sm-3 info__label'>Channel ID:</div>
<div className="col-sm-9">{channel.id}</div> <div className='col-sm-9'>{channel.id}</div>
</div> </div>
</div> </div>
<div className="modal-footer"> <div className='modal-footer'>
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button> <button
type='button'
className='btn btn-default'
data-dismiss='modal'
>Close</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
); );
} }
}); }

Просмотреть файл

@@ -9,7 +9,6 @@ var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx'); var client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx'); var AsyncClient = require('../utils/async_client.jsx');
export default class ChannelInviteModal extends React.Component { export default class ChannelInviteModal extends React.Component {
constructor() { constructor() {
super(); super();
@@ -129,15 +128,37 @@ export default class ChannelInviteModal extends React.Component {
if (this.state.loading) { if (this.state.loading) {
content = (<LoadingScreen />); content = (<LoadingScreen />);
} else { } else {
content = (<MemberList memberList={this.state.nonmembers} isAdmin={isAdmin} handleInvite={this.handleInvite} />); content = (
<MemberList
memberList={this.state.nonmembers}
isAdmin={isAdmin}
handleInvite={this.handleInvite}
/>
);
} }
return ( return (
<div className='modal fade' id='channel_invite' tabIndex='-1' role='dialog' aria-hidden='true'> <div
<div className='modal-dialog' role='document'> className='modal fade'
id='channel_invite'
tabIndex='-1'
role='dialog'
aria-hidden='true'
>
<div
className='modal-dialog'
role='document'
>
<div className='modal-content'> <div className='modal-content'>
<div className='modal-header'> <div className='modal-header'>
<button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button> <button
type='button'
className='close'
data-dismiss='modal'
aria-label='Close'
>
<span aria-hidden='true'>&times;</span>
</button>
<h4 className='modal-title'>Add New Members to <span className='name'>{this.state.channelName}</span></h4> <h4 className='modal-title'>Add New Members to <span className='name'>{this.state.channelName}</span></h4>
</div> </div>
<div className='modal-body'> <div className='modal-body'>
@@ -145,7 +166,11 @@ export default class ChannelInviteModal extends React.Component {
{content} {content}
</div> </div>
<div className='modal-footer'> <div className='modal-footer'>
<button type='button' className='btn btn-default' data-dismiss='modal'>Close</button> <button
type='button'
className='btn btn-default'
data-dismiss='modal'
>Close</button>
</div> </div>
</div> </div>
</div> </div>
@@ -153,4 +178,3 @@ export default class ChannelInviteModal extends React.Component {
); );
} }
} }
ChannelInviteModal.displayName = 'ChannelInviteModal';

Просмотреть файл

@@ -3,25 +3,40 @@
var client = require('../utils/client.jsx'); var client = require('../utils/client.jsx');
module.exports = React.createClass({ export default class CommandList extends React.Component {
getInitialState: function() { constructor(props) {
return { suggestions: [ ], cmd: "" }; super(props);
},
handleClick: function(i) {
this.props.addCommand(this.state.suggestions[i].suggestion)
this.setState({ suggestions: [ ], cmd: "" });
},
addFirstCommand: function() {
if (this.state.suggestions.length == 0) return;
this.handleClick(0);
},
isEmpty: function() {
return this.state.suggestions.length == 0;
},
getSuggestedCommands: function(cmd) {
if (!cmd || cmd.charAt(0) != '/') { this.handleClick = this.handleClick.bind(this);
this.setState({ suggestions: [ ], cmd: "" }); this.addFirstCommand = this.addFirstCommand.bind(this);
this.isEmpty = this.isEmpty.bind(this);
this.getSuggestedCommands = this.getSuggestedCommands.bind(this);
this.state = {
suggestions: [ ],
cmd: ''
};
}
handleClick(i) {
this.props.addCommand(this.state.suggestions[i].suggestion);
this.setState({suggestions: [ ], cmd: ''});
}
addFirstCommand() {
if (this.state.suggestions.length === 0) {
return;
}
this.handleClick(0);
}
isEmpty() {
return this.state.suggestions.length === 0;
}
getSuggestedCommands(cmd) {
if (!cmd || cmd.charAt(0) !== '/') {
this.setState({suggestions: [ ], cmd: ''});
return; return;
} }
@@ -29,36 +44,56 @@ module.exports = React.createClass({
this.props.channelId, this.props.channelId,
cmd, cmd,
true, true,
function(data) { function success(data) {
if (data.suggestions.length === 1 && data.suggestions[0].suggestion === cmd) { if (data.suggestions.length === 1 && data.suggestions[0].suggestion === cmd) {
data.suggestions = []; data.suggestions = [];
} }
this.setState({ suggestions: data.suggestions, cmd: cmd }); this.setState({suggestions: data.suggestions, cmd: cmd});
}.bind(this), }.bind(this),
function(err){ function fail() {
} }
); );
}, }
render: function() {
if (this.state.suggestions.length == 0) return (<div/>); render() {
if (this.state.suggestions.length === 0) {
return (<div/>);
}
var suggestions = []; var suggestions = [];
for (var i = 0; i < this.state.suggestions.length; i++) { for (var i = 0; i < this.state.suggestions.length; i++) {
if (this.state.suggestions[i].suggestion != this.state.cmd) { if (this.state.suggestions[i].suggestion !== this.state.cmd) {
suggestions.push( suggestions.push(
<div key={i} className="command-name" onClick={this.handleClick.bind(this, i)}> <div
<div className="command__title"><strong>{ this.state.suggestions[i].suggestion }</strong></div> key={i}
<div className="command__desc">{ this.state.suggestions[i].description }</div> className='command-name'
onClick={this.handleClick.bind(this, i)}
>
<div className='command__title'><strong>{this.state.suggestions[i].suggestion}</strong></div>
<div className='command__desc'>{this.state.suggestions[i].description}</div>
</div> </div>
); );
} }
} }
return ( return (
<div ref="mentionlist" className="command-box" style={{height:(this.state.suggestions.length*56)+2}}> <div
{ suggestions } ref='mentionlist'
className='command-box'
style={{height: (this.state.suggestions.length * 56) + 2}}
>
{suggestions}
</div> </div>
); );
} }
}); }
CommandList.defaultProps = {
channelId: null
};
CommandList.propTypes = {
addCommand: React.PropTypes.func,
channelId: React.PropTypes.string
};

Просмотреть файл

@@ -6,20 +6,19 @@ var Constants = require('../utils/constants.jsx');
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
module.exports = React.createClass({ export default class FileUpload extends React.Component {
displayName: 'FileUpload', constructor(props) {
propTypes: { super(props);
onUploadError: React.PropTypes.func,
getFileCount: React.PropTypes.func, this.handleChange = this.handleChange.bind(this);
onFileUpload: React.PropTypes.func, this.handleDrop = this.handleDrop.bind(this);
onUploadStart: React.PropTypes.func,
channelId: React.PropTypes.string, this.state = {
postType: React.PropTypes.string requests: {}
}, };
getInitialState: function() { }
return {requests: {}};
}, handleChange() {
handleChange: function() {
var element = $(this.refs.fileInput.getDOMNode()); var element = $(this.refs.fileInput.getDOMNode());
var files = element.prop('files'); var files = element.prop('files');
@@ -30,7 +29,7 @@ module.exports = React.createClass({
// This looks redundant, but must be done this way due to // This looks redundant, but must be done this way due to
// setState being an asynchronous call // setState being an asynchronous call
var numFiles = 0; var numFiles = 0;
for (var i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {
if (files[i].size <= Constants.MAX_FILE_SIZE) { if (files[i].size <= Constants.MAX_FILE_SIZE) {
numFiles++; numFiles++;
} }
@@ -42,7 +41,7 @@ module.exports = React.createClass({
this.props.onUploadError('Uploads limited to ' + Constants.MAX_UPLOAD_FILES + ' files maximum. Please use additional posts for more files.'); this.props.onUploadError('Uploads limited to ' + Constants.MAX_UPLOAD_FILES + ' files maximum. Please use additional posts for more files.');
} }
for (var i = 0; i < files.length && i < numToUpload; i++) { for (let i = 0; i < files.length && i < numToUpload; i++) {
if (files[i].size > Constants.MAX_FILE_SIZE) { if (files[i].size > Constants.MAX_FILE_SIZE) {
this.props.onUploadError('Files must be no more than ' + Constants.MAX_FILE_SIZE / 1000000 + ' MB'); this.props.onUploadError('Files must be no more than ' + Constants.MAX_FILE_SIZE / 1000000 + ' MB');
continue; continue;
@@ -58,7 +57,7 @@ module.exports = React.createClass({
formData.append('client_ids', clientId); formData.append('client_ids', clientId);
var request = client.uploadFile(formData, var request = client.uploadFile(formData,
function(data) { function success(data) {
var parsedData = $.parseJSON(data); var parsedData = $.parseJSON(data);
this.props.onFileUpload(parsedData.filenames, parsedData.client_ids, channelId); this.props.onFileUpload(parsedData.filenames, parsedData.client_ids, channelId);
@@ -68,7 +67,7 @@ module.exports = React.createClass({
} }
this.setState({requests: requests}); this.setState({requests: requests});
}.bind(this), }.bind(this),
function(err) { function fail(err) {
this.props.onUploadError(err, clientId); this.props.onUploadError(err, clientId);
}.bind(this) }.bind(this)
); );
@@ -87,9 +86,12 @@ module.exports = React.createClass({
element[0].type = 'text'; element[0].type = 'text';
element[0].type = 'file'; element[0].type = 'file';
} }
} catch(e) {} } catch(e) {
}, // Do nothing
handleDrop: function(e) { }
}
handleDrop(e) {
this.props.onUploadError(null); this.props.onUploadError(null);
var files = e.originalEvent.dataTransfer.files; var files = e.originalEvent.dataTransfer.files;
@@ -120,7 +122,7 @@ module.exports = React.createClass({
formData.append('client_ids', clientId); formData.append('client_ids', clientId);
var request = client.uploadFile(formData, var request = client.uploadFile(formData,
function(data) { function success(data) {
var parsedData = $.parseJSON(data); var parsedData = $.parseJSON(data);
this.props.onFileUpload(parsedData.filenames, parsedData.client_ids, channelId); this.props.onFileUpload(parsedData.filenames, parsedData.client_ids, channelId);
@@ -130,7 +132,7 @@ module.exports = React.createClass({
} }
this.setState({requests: requests}); this.setState({requests: requests});
}.bind(this), }.bind(this),
function(err) { function fail(err) {
this.props.onUploadError(err, clientId); this.props.onUploadError(err, clientId);
}.bind(this) }.bind(this)
); );
@@ -144,40 +146,41 @@ module.exports = React.createClass({
} else { } else {
this.props.onUploadError('Invalid file upload', -1); this.props.onUploadError('Invalid file upload', -1);
} }
}, }
componentDidMount: function() {
componentDidMount() {
var inputDiv = this.refs.input.getDOMNode(); var inputDiv = this.refs.input.getDOMNode();
var self = this; var self = this;
if (this.props.postType === 'post') { if (this.props.postType === 'post') {
$('.row.main').dragster({ $('.row.main').dragster({
enter: function() { enter() {
$('.center-file-overlay').removeClass('hidden'); $('.center-file-overlay').removeClass('hidden');
}, },
leave: function() { leave() {
$('.center-file-overlay').addClass('hidden'); $('.center-file-overlay').addClass('hidden');
}, },
drop: function(dragsterEvent, e) { drop(dragsterEvent, e) {
$('.center-file-overlay').addClass('hidden'); $('.center-file-overlay').addClass('hidden');
self.handleDrop(e); self.handleDrop(e);
} }
}); });
} else if (this.props.postType === 'comment') { } else if (this.props.postType === 'comment') {
$('.post-right__container').dragster({ $('.post-right__container').dragster({
enter: function() { enter() {
$('.right-file-overlay').removeClass('hidden'); $('.right-file-overlay').removeClass('hidden');
}, },
leave: function() { leave() {
$('.right-file-overlay').addClass('hidden'); $('.right-file-overlay').addClass('hidden');
}, },
drop: function(dragsterEvent, e) { drop(dragsterEvent, e) {
$('.right-file-overlay').addClass('hidden'); $('.right-file-overlay').addClass('hidden');
self.handleDrop(e); self.handleDrop(e);
} }
}); });
} }
document.addEventListener('paste', function(e) { document.addEventListener('paste', function handlePaste(e) {
var textarea = $(inputDiv.parentNode.parentNode).find('.custom-textarea')[0]; var textarea = $(inputDiv.parentNode.parentNode).find('.custom-textarea')[0];
if (textarea !== e.target && !$.contains(textarea, e.target)) { if (textarea !== e.target && !$.contains(textarea, e.target)) {
@@ -191,7 +194,7 @@ module.exports = React.createClass({
var items = e.clipboardData.items; var items = e.clipboardData.items;
var numItems = 0; var numItems = 0;
if (items) { if (items) {
for (var i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) { if (items[i].type.indexOf('image') !== -1) {
var testExt = items[i].type.split('/')[1].toLowerCase(); var testExt = items[i].type.split('/')[1].toLowerCase();
@@ -269,8 +272,9 @@ module.exports = React.createClass({
} }
} }
}); });
}, }
cancelUpload: function(clientId) {
cancelUpload(clientId) {
var requests = this.state.requests; var requests = this.state.requests;
var request = requests[clientId]; var request = requests[clientId];
@@ -280,15 +284,33 @@ module.exports = React.createClass({
delete requests[clientId]; delete requests[clientId];
this.setState({requests: requests}); this.setState({requests: requests});
} }
}, }
render: function() {
render() {
return ( return (
<span ref='input' className='btn btn-file'> <span
ref='input'
className='btn btn-file'
>
<span> <span>
<i className='glyphicon glyphicon-paperclip' /> <i className='glyphicon glyphicon-paperclip' />
</span> </span>
<input ref='fileInput' type='file' onChange={this.handleChange} multiple/> <input
ref='fileInput'
type='file'
onChange={this.handleChange}
multiple='true'
/>
</span> </span>
); );
} }
}); }
FileUpload.propTypes = {
onUploadError: React.PropTypes.func,
getFileCount: React.PropTypes.func,
onFileUpload: React.PropTypes.func,
onUploadStart: React.PropTypes.func,
channelId: React.PropTypes.string,
postType: React.PropTypes.string
};

Просмотреть файл

@@ -1,53 +1,57 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx'); var client = require('../utils/client.jsx');
module.exports = React.createClass({ export default class FindTeam extends React.Component {
handleSubmit: function(e) { constructor(props) {
super(props);
this.state = {};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault(); e.preventDefault();
var state = { }; var state = { };
var email = this.refs.email.getDOMNode().value.trim().toLowerCase(); var email = this.refs.email.getDOMNode().value.trim().toLowerCase();
if (!email || !utils.isEmail(email)) { if (!email || !utils.isEmail(email)) {
state.email_error = "Please enter a valid email address"; state.email_error = 'Please enter a valid email address';
this.setState(state); this.setState(state);
return; return;
} }
else {
state.email_error = ""; state.email_error = '';
}
client.findTeamsSendEmail(email, client.findTeamsSendEmail(email,
function(data) { function success() {
state.sent = true; state.sent = true;
this.setState(state); this.setState(state);
}.bind(this), }.bind(this),
function(err) { function fail(err) {
state.email_error = err.message; state.email_error = err.message;
this.setState(state); this.setState(state);
}.bind(this) }.bind(this)
); );
}, }
getInitialState: function() {
return { };
},
render: function() {
var email_error = this.state.email_error ? <label className='control-label'>{ this.state.email_error }</label> : null; render() {
var emailError = null;
var emailErrorClass = 'form-group';
var divStyle = { if (this.state.email_error) {
"marginTop": "50px", emailError = <label className='control-label'>{this.state.email_error}</label>;
emailErrorClass = 'form-group has-error';
} }
if (this.state.sent) { if (this.state.sent) {
return ( return (
<div> <div>
<h4>{"Find Your " + utils.toTitleCase(strings.Team)}</h4> <h4>{'Find Your ' + utils.toTitleCase(strings.Team)}</h4>
<p>{"An email was sent with links to any " + strings.TeamPlural + " to which you are a member."}</p> <p>{'An email was sent with links to any ' + strings.TeamPlural + ' to which you are a member.'}</p>
</div> </div>
); );
} }
@@ -56,17 +60,25 @@ module.exports = React.createClass({
<div> <div>
<h4>Find Your Team</h4> <h4>Find Your Team</h4>
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<p>{"Get an email with links to any " + strings.TeamPlural + " to which you are a member."}</p> <p>{'Get an email with links to any ' + strings.TeamPlural + ' to which you are a member.'}</p>
<div className="form-group"> <div className='form-group'>
<label className='control-label'>Email</label> <label className='control-label'>Email</label>
<div className={ email_error ? "form-group has-error" : "form-group" }> <div className={emailErrorClass}>
<input type="text" ref="email" className="form-control" placeholder="you@domain.com" maxLength="128" /> <input
{ email_error } type='text'
ref='email'
className='form-control'
placeholder='you@domain.com'
maxLength='128'
/>
{emailError}
</div> </div>
</div> </div>
<button className="btn btn-md btn-primary" type="submit">Send</button> <button
className='btn btn-md btn-primary'
type='submit'>Send</button>
</form> </form>
</div> </div>
); );
} }
}); }

Просмотреть файл

@@ -7,10 +7,28 @@ var Client = require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
var ConfirmModal = require('./confirm_modal.jsx'); var ConfirmModal = require('./confirm_modal.jsx');
module.exports = React.createClass({ export default class InviteMemberModal extends React.Component {
componentDidMount: function() { constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.addInviteFields = this.addInviteFields.bind(this);
this.clearFields = this.clearFields.bind(this);
this.removeInviteFields = this.removeInviteFields.bind(this);
this.state = {
inviteIds: [0],
idCount: 0,
emailErrors: {},
firstNameErrors: {},
lastNameErrors: {},
emailEnabled: !ConfigStore.getSettingAsBoolean('ByPassEmail', false)
};
}
componentDidMount() {
var self = this; var self = this;
$('#invite_member').on('hide.bs.modal', function(e) { $('#invite_member').on('hide.bs.modal', function hide(e) {
if ($('#invite_member').attr('data-confirm') === 'true') { if ($('#invite_member').attr('data-confirm') === 'true') {
$('#invite_member').attr('data-confirm', 'false'); $('#invite_member').attr('data-confirm', 'false');
return; return;
@@ -31,11 +49,12 @@ module.exports = React.createClass({
} }
}); });
$('#invite_member').on('hidden.bs.modal', function() { $('#invite_member').on('hidden.bs.modal', function show() {
self.clearFields(); self.clearFields();
}); });
}, }
handleSubmit: function(e) {
handleSubmit() {
if (!this.state.emailEnabled) { if (!this.state.emailEnabled) {
return; return;
} }
@@ -90,11 +109,11 @@ module.exports = React.createClass({
data.invites = invites; data.invites = invites;
Client.inviteMembers(data, Client.inviteMembers(data,
function() { function success() {
$(this.refs.modal.getDOMNode()).attr('data-confirm', 'true'); $(this.refs.modal.getDOMNode()).attr('data-confirm', 'true');
$(this.refs.modal.getDOMNode()).modal('hide'); $(this.refs.modal.getDOMNode()).modal('hide');
}.bind(this), }.bind(this),
function(err) { function fail(err) {
if (err.message === 'This person is already on your team') { if (err.message === 'This person is already on your team') {
emailErrors[err.detailed_error] = err.message; emailErrors[err.detailed_error] = err.message;
this.setState({emailErrors: emailErrors}); this.setState({emailErrors: emailErrors});
@@ -103,18 +122,21 @@ module.exports = React.createClass({
} }
}.bind(this) }.bind(this)
); );
}, }
componentDidUpdate: function() {
componentDidUpdate() {
$(this.refs.modalBody.getDOMNode()).css('max-height', $(window).height() - 200); $(this.refs.modalBody.getDOMNode()).css('max-height', $(window).height() - 200);
$(this.refs.modalBody.getDOMNode()).css('overflow-y', 'scroll'); $(this.refs.modalBody.getDOMNode()).css('overflow-y', 'scroll');
}, }
addInviteFields: function() {
addInviteFields() {
var count = this.state.idCount + 1; var count = this.state.idCount + 1;
var inviteIds = this.state.inviteIds; var inviteIds = this.state.inviteIds;
inviteIds.push(count); inviteIds.push(count);
this.setState({inviteIds: inviteIds, idCount: count}); this.setState({inviteIds: inviteIds, idCount: count});
}, }
clearFields: function() {
clearFields() {
var inviteIds = this.state.inviteIds; var inviteIds = this.state.inviteIds;
for (var i = 0; i < inviteIds.length; i++) { for (var i = 0; i < inviteIds.length; i++) {
@@ -133,8 +155,9 @@ module.exports = React.createClass({
firstNameErrors: {}, firstNameErrors: {},
lastNameErrors: {} lastNameErrors: {}
}); });
}, }
removeInviteFields: function(index) {
removeInviteFields(index) {
var count = this.state.idCount; var count = this.state.idCount;
var inviteIds = this.state.inviteIds; var inviteIds = this.state.inviteIds;
var i = inviteIds.indexOf(index); var i = inviteIds.indexOf(index);
@@ -145,24 +168,10 @@ module.exports = React.createClass({
inviteIds.push(++count); inviteIds.push(++count);
} }
this.setState({inviteIds: inviteIds, idCount: count}); this.setState({inviteIds: inviteIds, idCount: count});
}, }
getInitialState: function() {
return {
inviteIds: [0],
idCount: 0,
emailErrors: {},
firstNameErrors: {},
lastNameErrors: {},
emailEnabled: !ConfigStore.getSettingAsBoolean('ByPassEmail', false)
};
},
render: function() {
var currentUser = UserStore.getCurrentUser();
var inputDisabled = ''; render() {
if (!this.state.emailEnabled) { var currentUser = UserStore.getCurrentUser();
inputDisabled = 'disabled';
}
if (currentUser != null) { if (currentUser != null) {
var inviteSections = []; var inviteSections = [];
@@ -185,7 +194,13 @@ module.exports = React.createClass({
var removeButton = null; var removeButton = null;
if (index) { if (index) {
removeButton = (<div> removeButton = (<div>
<button type='button' className='btn btn-link remove__member' onClick={this.removeInviteFields.bind(this, index)}><span className='fa fa-trash'></span></button> <button
type='button'
className='btn btn-link remove__member'
onClick={this.removeInviteFields.bind(this, index)}
>
<span className='fa fa-trash'></span>
</button>
</div>); </div>);
} }
var emailClass = 'form-group invite'; var emailClass = 'form-group invite';
@@ -206,13 +221,27 @@ module.exports = React.createClass({
nameFields = (<div className='row--invite'> nameFields = (<div className='row--invite'>
<div className='col-sm-6'> <div className='col-sm-6'>
<div className={firstNameClass}> <div className={firstNameClass}>
<input type='text' className='form-control' ref={'first_name' + index} placeholder='First name' maxLength='64' disabled={!this.state.emailEnabled}/> <input
type='text'
className='form-control'
ref={'first_name' + index}
placeholder='First name'
maxLength='64'
disabled={!this.state.emailEnabled}
/>
{firstNameError} {firstNameError}
</div> </div>
</div> </div>
<div className='col-sm-6'> <div className='col-sm-6'>
<div className={lastNameClass}> <div className={lastNameClass}>
<input type='text' className='form-control' ref={'last_name' + index} placeholder='Last name' maxLength='64' disabled={!this.state.emailEnabled}/> <input
type='text'
className='form-control'
ref={'last_name' + index}
placeholder='Last name'
maxLength='64'
disabled={!this.state.emailEnabled}
/>
{lastNameError} {lastNameError}
</div> </div>
</div> </div>
@@ -223,7 +252,15 @@ module.exports = React.createClass({
<div key={'key' + index}> <div key={'key' + index}>
{removeButton} {removeButton}
<div className={emailClass}> <div className={emailClass}>
<input onKeyUp={this.displayNameKeyUp} type='text' ref={'email' + index} className='form-control' placeholder='email@domain.com' maxLength='64' disabled={!this.state.emailEnabled}/> <input
onKeyUp={this.displayNameKeyUp}
type='text'
ref={'email' + index}
className='form-control'
placeholder='email@domain.com'
maxLength='64'
disabled={!this.state.emailEnabled}
/>
{emailError} {emailError}
</div> </div>
{nameFields} {nameFields}
@@ -242,23 +279,44 @@ module.exports = React.createClass({
content = ( content = (
<div> <div>
{serverError} {serverError}
<button type='button' className='btn btn-default' onClick={this.addInviteFields}>Add another</button> <button
type='button'
className='btn btn-default'
onClick={this.addInviteFields}
>Add another</button>
<br/> <br/>
<br/> <br/>
<span>People invited automatically join Town Square channel.</span> <span>People invited automatically join Town Square channel.</span>
</div> </div>
); );
sendButton = <button onClick={this.handleSubmit} type='button' className='btn btn-primary'>Send Invitations</button> sendButton =
(
<button
onClick={this.handleSubmit}
type='button'
className='btn btn-primary'
>Send Invitations</button>
);
} else { } else {
var teamInviteLink = null; var teamInviteLink = null;
if (currentUser && this.props.teamType === 'O') { if (currentUser && this.props.teamType === 'O') {
var linkUrl = utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + currentUser.team_id; var linkUrl = utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + currentUser.team_id;
var link = <a href='#' data-toggle='modal' data-target='#get_link' data-title='Team Invite' data-value={linkUrl} onClick={ var link =
function() { (
$('#invite_member').modal('hide'); <a
} href='#'
}>Team Invite Link</a>; data-toggle='modal'
data-target='#get_link'
data-title='Team Invite'
data-value={linkUrl}
onClick={
function click() {
$('#invite_member').modal('hide');
}
}
>Team Invite Link</a>
);
teamInviteLink = ( teamInviteLink = (
<p> <p>
@@ -277,22 +335,46 @@ module.exports = React.createClass({
return ( return (
<div> <div>
<div className='modal fade' ref='modal' id='invite_member' tabIndex='-1' role='dialog' aria-hidden='true'> <div
className='modal fade'
ref='modal'
id='invite_member'
tabIndex='-1'
role='dialog'
aria-hidden='true'
>
<div className='modal-dialog'> <div className='modal-dialog'>
<div className='modal-content'> <div className='modal-content'>
<div className='modal-header'> <div className='modal-header'>
<button type='button' className='close' data-dismiss='modal' aria-label='Close' data-reactid='.5.0.0.0.0'><span aria-hidden='true' data-reactid='.5.0.0.0.0.0'>×</span></button> <button
<h4 className='modal-title' id='myModalLabel'>Invite New Member</h4> type='button'
className='close'
data-dismiss='modal'
aria-label='Close'
>
<span aria-hidden='true'>×</span>
</button>
<h4
className='modal-title'
id='myModalLabel'
>Invite New Member</h4>
</div> </div>
<div ref='modalBody' className='modal-body'> <div
ref='modalBody'
className='modal-body'
>
<form role='form'> <form role='form'>
{inviteSections} {inviteSections}
</form> </form>
{content} {content}
</div> </div>
<div className='modal-footer'> <div className='modal-footer'>
<button type='button' className='btn btn-default' data-dismiss='modal'>Cancel</button> <button
{sendButton} type='button'
className='btn btn-default'
data-dismiss='modal'
>Cancel</button>
{sendButton}
</div> </div>
</div> </div>
</div> </div>
@@ -309,4 +391,8 @@ module.exports = React.createClass({
} }
return <div/>; return <div/>;
} }
}); }
InviteMemberModal.propTypes = {
teamType: React.PropTypes.string
};

Просмотреть файл

@@ -3,32 +3,45 @@
var MemberListItem = require('./member_list_item.jsx'); var MemberListItem = require('./member_list_item.jsx');
module.exports = React.createClass({ export default class MemberList extends React.Component {
render: function() { constructor(props) {
super(props);
}
render() {
var members = []; var members = [];
if (this.props.memberList != null) { if (this.props.memberList !== null) {
members = this.props.memberList; members = this.props.memberList;
} }
var message = ""; var message = '';
if (members.length === 0) if (members.length === 0) {
message = <span>No users to add.</span>; message = <span>No users to add.</span>;
}
return ( return (
<div className="member-list-holder"> <div className='member-list-holder'>
{members.map(function(member) { {members.map(function mymembers(member) {
return <MemberListItem return (<MemberListItem
key={member.id} key={member.id}
member={member} member={member}
isAdmin={this.props.isAdmin} isAdmin={this.props.isAdmin}
handleInvite={this.props.handleInvite} handleInvite={this.props.handleInvite}
handleRemove={this.props.handleRemove} handleRemove={this.props.handleRemove}
handleMakeAdmin={this.props.handleMakeAdmin} handleMakeAdmin={this.props.handleMakeAdmin}
/>; />);
}, this)} }, this)}
{message} {message}
</div> </div>
); );
} }
}); }
MemberList.propTypes = {
memberList: React.PropTypes.array,
isAdmin: React.PropTypes.bool,
handleInvite: React.PropTypes.func,
handleRemove: React.PropTypes.func,
handleMakeAdmin: React.PropTypes.func
};

Просмотреть файл

@@ -7,19 +7,22 @@ var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx'); var AsyncClient = require('../utils/async_client.jsx');
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
module.exports = React.createClass({ export default class MoreDirectChannels extends React.Component {
displayName: 'MoreDirectChannels', constructor(props) {
componentDidMount: function() { super(props);
this.state = {channels: [], loadingDMChannel: -1};
}
componentDidMount() {
var self = this; var self = this;
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function showModal(e) { $(this.refs.modal.getDOMNode()).on('show.bs.modal', function showModal(e) {
var button = e.relatedTarget; var button = e.relatedTarget;
self.setState({channels: $(button).data('channels')}); self.setState({channels: $(button).data('channels')});
}); });
}, }
getInitialState: function() {
return {channels: [], loadingDMChannel: -1}; render() {
},
render: function() {
var self = this; var self = this;
var directMessageItems = this.state.channels.map(function mapActivityToChannel(channel, index) { var directMessageItems = this.state.channels.map(function mapActivityToChannel(channel, index) {
@@ -48,7 +51,12 @@ module.exports = React.createClass({
var otherUserId = utils.getUserIdFromChannelName(channel); var otherUserId = utils.getUserIdFromChannelName(channel);
if (self.state.loadingDMChannel === index) { if (self.state.loadingDMChannel === index) {
badge = <img className='channel-loading-gif pull-right' src='/static/images/load.gif'/>; badge = (
<img
className='channel-loading-gif pull-right'
src='/static/images/load.gif'
/>
);
} }
if (self.state.loadingDMChannel === -1) { if (self.state.loadingDMChannel === -1) {
@@ -73,16 +81,36 @@ module.exports = React.createClass({
} }
return ( return (
<li key={channel.name} className={active}><a className={'sidebar-channel ' + titleClass} href='#' onClick={handleClick}>{badge}{channel.display_name}</a></li> <li
key={channel.name}
className={active}
>
<a
className={'sidebar-channel ' + titleClass}
href='#'
onClick={handleClick}
>{badge}{channel.display_name}</a>
</li>
); );
}); });
return ( return (
<div className='modal fade' id='more_direct_channels' ref='modal' tabIndex='-1' role='dialog' aria-hidden='true'> <div
className='modal fade'
id='more_direct_channels'
ref='modal'
tabIndex='-1'
role='dialog'
aria-hidden='true'
>
<div className='modal-dialog'> <div className='modal-dialog'>
<div className='modal-content'> <div className='modal-content'>
<div className='modal-header'> <div className='modal-header'>
<button type='button' className='close' data-dismiss='modal'> <button
type='button'
className='close'
data-dismiss='modal'
>
<span aria-hidden='true'>&times;</span> <span aria-hidden='true'>&times;</span>
<span className='sr-only'>Close</span> <span className='sr-only'>Close</span>
</button> </button>
@@ -94,11 +122,15 @@ module.exports = React.createClass({
</ul> </ul>
</div> </div>
<div className='modal-footer'> <div className='modal-footer'>
<button type='button' className='btn btn-default' data-dismiss='modal'>Close</button> <button
type='button'
className='btn btn-default'
data-dismiss='modal'
>Close</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
); );
} }
}); }

Просмотреть файл

@@ -1,57 +1,70 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
var SocketStore = require('../stores/socket_store.jsx'); var SocketStore = require('../stores/socket_store.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
module.exports = React.createClass({ export default class MsgTyping extends React.Component {
timer: null, constructor(props) {
lastTime: 0, super(props);
componentDidMount: function() {
SocketStore.addChangeListener(this._onChange);
},
componentWillReceiveProps: function(newProps) {
if(this.props.channelId !== newProps.channelId) {
this.setState({text:""});
}
},
componentWillUnmount: function() {
SocketStore.removeChangeListener(this._onChange);
},
_onChange: function(msg) {
if (msg.action == "typing" &&
this.props.channelId == msg.channel_id &&
this.props.parentId == msg.props.parent_id) {
this.timer = null;
this.lastTime = 0;
this.onChange = this.onChange.bind(this);
this.state = {
text: ''
};
}
componentDidMount() {
SocketStore.addChangeListener(this.onChange);
}
componentWillReceiveProps(newProps) {
if (this.props.channelId !== newProps.channelId) {
this.setState({text: ''});
}
}
componentWillUnmount() {
SocketStore.removeChangeListener(this.onChange);
}
onChange(msg) {
if (msg.action === 'typing' &&
this.props.channelId === msg.channel_id &&
this.props.parentId === msg.props.parent_id) {
this.lastTime = new Date().getTime(); this.lastTime = new Date().getTime();
var username = "Someone"; var username = 'Someone';
if (UserStore.hasProfile(msg.user_id)) { if (UserStore.hasProfile(msg.user_id)) {
username = UserStore.getProfile(msg.user_id).username; username = UserStore.getProfile(msg.user_id).username;
} }
this.setState({ text: username + " is typing..." }); this.setState({text: username + ' is typing...'});
if (!this.timer) { if (!this.timer) {
var outer = this; this.timer = setInterval(function myTimer() {
outer.timer = setInterval(function() { if ((new Date().getTime() - this.lastTime) > 8000) {
if ((new Date().getTime() - outer.lastTime) > 8000) { this.setState({text: ''});
outer.setState({ text: "" }); }
} }.bind(this), 3000);
}, 3000);
} }
} else if (msg.action === 'posted' && msg.channel_id === this.props.channelId) {
this.setState({text: ''});
} }
else if (msg.action == "posted" && msg.channel_id === this.props.channelId) { }
this.setState({text:""})
} render() {
},
getInitialState: function() {
return { text: "" };
},
render: function() {
return ( return (
<span className="msg-typing">{ this.state.text }</span> <span className='msg-typing'>{this.state.text}</span>
); );
} }
}); }
MsgTyping.propTypes = {
channelId: React.PropTypes.string,
parentId: React.PropTypes.string
};

Просмотреть файл

@@ -51,8 +51,10 @@ export default class PostList extends React.Component {
if (deletedPosts && Object.keys(deletedPosts).length > 0) { if (deletedPosts && Object.keys(deletedPosts).length > 0) {
for (var pid in deletedPosts) { for (var pid in deletedPosts) {
postList.posts[pid] = deletedPosts[pid]; if (deletedPosts.hasOwnProperty(pid)) {
postList.order.unshift(pid); postList.posts[pid] = deletedPosts[pid];
postList.order.unshift(pid);
}
} }
postList.order.sort(function postSort(a, b) { postList.order.sort(function postSort(a, b) {
@@ -71,7 +73,9 @@ export default class PostList extends React.Component {
if (pendingPostList) { if (pendingPostList) {
postList.order = pendingPostList.order.concat(postList.order); postList.order = pendingPostList.order.concat(postList.order);
for (var ppid in pendingPostList.posts) { for (var ppid in pendingPostList.posts) {
postList.posts[ppid] = pendingPostList.posts[ppid]; if (pendingPostList.posts.hasOwnProperty(ppid)) {
postList.posts[ppid] = pendingPostList.posts[ppid];
}
} }
} }
} }
@@ -267,7 +271,6 @@ export default class PostList extends React.Component {
} }
} }
onSocketChange(msg) { onSocketChange(msg) {
var postList;
var post; var post;
if (msg.action === 'posted' || msg.action === 'post_edited') { if (msg.action === 'posted' || msg.action === 'post_edited') {
post = JSON.parse(msg.props.post); post = JSON.parse(msg.props.post);
@@ -280,7 +283,6 @@ export default class PostList extends React.Component {
} }
post = JSON.parse(msg.props.post); post = JSON.parse(msg.props.post);
postList = this.state.postList;
PostStore.storeUnseenDeletedPost(post); PostStore.storeUnseenDeletedPost(post);
PostStore.removePost(post, true); PostStore.removePost(post, true);
@@ -644,11 +646,18 @@ export default class PostList extends React.Component {
if (posts && this.state.isFirstLoadComplete) { if (posts && this.state.isFirstLoadComplete) {
postCtls = this.createPosts(posts, order); postCtls = this.createPosts(posts, order);
} else { } else {
postCtls.push(<LoadingScreen position='absolute' />); postCtls.push(
<LoadingScreen
position='absolute'
key='loading'
/>);
} }
return ( return (
<div ref='postlist' className='post-list-holder-by-time'> <div
ref='postlist'
className='post-list-holder-by-time'
>
<div className='post-list__table'> <div className='post-list__table'>
<div className='post-list__content'> <div className='post-list__content'>
{moreMessages} {moreMessages}
@@ -658,4 +667,4 @@ export default class PostList extends React.Component {
</div> </div>
); );
} }
} }

Просмотреть файл

@@ -3,62 +3,99 @@
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
var BrowserStore = require('../stores/browser_store.jsx') var BrowserStore = require('../stores/browser_store.jsx');
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
module.exports = React.createClass({ export default class RemovedFromChannelModal extends React.Component {
handleShow: function() { constructor(props) {
var newState = {}; super(props);
if(BrowserStore.getItem("channel-removed-state")) {
newState = BrowserStore.getItem("channel-removed-state");
BrowserStore.removeItem("channel-removed-state");
}
this.setState(newState); this.handleShow = this.handleShow.bind(this);
}, this.handleClose = this.handleClose.bind(this);
handleClose: function() {
var townSquare = ChannelStore.getByName("town-square");
utils.switchChannel(townSquare);
this.setState({channelName: "", remover: ""}); this.state = {
}, channelName: '',
componentDidMount: function() { remover: ''
$(this.getDOMNode()).on('show.bs.modal',this.handleShow); };
$(this.getDOMNode()).on('hidden.bs.modal',this.handleClose); }
},
componentWillUnmount: function() { handleShow() {
$(this.getDOMNode()).off('show.bs.modal',this.handleShow); var newState = {};
$(this.getDOMNode()).off('hidden.bs.modal',this.handleClose); if (BrowserStore.getItem('channel-removed-state')) {
}, newState = BrowserStore.getItem('channel-removed-state');
getInitialState: function() { BrowserStore.removeItem('channel-removed-state');
return {channelName: "", remover: ""} }
},
render: function() { this.setState(newState);
}
handleClose() {
var townSquare = ChannelStore.getByName('town-square');
utils.switchChannel(townSquare);
this.setState({channelName: '', remover: ''});
}
componentDidMount() {
$(React.findDOMNode(this)).on('show.bs.modal', this.handleShow);
$(React.findDOMNode(this)).on('hidden.bs.modal', this.handleClose);
}
componentWillUnmount() {
$(React.findDOMNode(this)).off('show.bs.modal', this.handleShow);
$(React.findDOMNode(this)).off('hidden.bs.modal', this.handleClose);
}
render() {
var currentUser = UserStore.getCurrentUser(); var currentUser = UserStore.getCurrentUser();
var channelName = this.state.channelName ? this.state.channelName : "the channel"
var remover = this.state.remover ? this.state.remover : "Someone" var channelName = 'the channel';
if (this.state.channelName) {
channelName = this.state.channelName;
}
var remover = 'Someone';
if (this.state.remover) {
remover = this.state.remover;
}
if (currentUser != null) { if (currentUser != null) {
return ( return (
<div className='modal fade' ref='modal' id='removed_from_channel' tabIndex='-1' role='dialog' aria-hidden='true'> <div
<div className='modal-dialog'> className='modal fade'
<div className='modal-content'> ref='modal'
<div className='modal-header'> id='removed_from_channel'
<button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button> tabIndex='-1'
<h4 className='modal-title'>Removed from <span className='name'>{channelName}</span></h4> 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'>&times;</span></button>
<h4 className='modal-title'>Removed from <span className='name'>{channelName}</span></h4>
</div>
<div className='modal-body'>
<p>{remover} removed you from {channelName}</p>
</div>
<div className='modal-footer'>
<button
type='button'
className='btn btn-primary'
data-dismiss='modal'
>Okay</button>
</div>
</div> </div>
<div className='modal-body'> </div>
<p>{remover} removed you from {channelName}</p>
</div>
<div className='modal-footer'>
<button type='button' className='btn btn-primary' data-dismiss='modal'>Okay</button>
</div>
</div>
</div>
</div> </div>
); );
} else {
return <div/>;
} }
return <div/>;
} }
}); }

Просмотреть файл

@@ -33,7 +33,9 @@ export default class RhsThread extends React.Component {
if (pendingPostList) { if (pendingPostList) {
for (var pid in pendingPostList.posts) { for (var pid in pendingPostList.posts) {
postList.posts[pid] = pendingPostList.posts[pid]; if (pendingPostList.posts.hasOwnProperty(pid)) {
postList.posts[pid] = pendingPostList.posts[pid];
}
} }
} }
@@ -81,7 +83,9 @@ export default class RhsThread extends React.Component {
if (currentPosts.posts[currentPosts.order[0]].channel_id === currentSelected.posts[currentSelected.order[0]].channel_id) { if (currentPosts.posts[currentPosts.order[0]].channel_id === currentSelected.posts[currentSelected.order[0]].channel_id) {
currentSelected.posts = {}; currentSelected.posts = {};
for (var postId in currentPosts.posts) { for (var postId in currentPosts.posts) {
currentSelected.posts[postId] = currentPosts.posts[postId]; if (currentPosts.posts.hasOwnProperty(postId)) {
currentSelected.posts[postId] = currentPosts.posts[postId];
}
} }
PostStore.storeSelectedPost(currentSelected); PostStore.storeSelectedPost(currentSelected);
@@ -128,9 +132,11 @@ export default class RhsThread extends React.Component {
var postsArray = []; var postsArray = [];
for (var postId in postList.posts) { for (var postId in postList.posts) {
var cpost = postList.posts[postId]; if (postList.posts.hasOwnProperty(postId)) {
if (cpost.root_id === rootPost.id) { var cpost = postList.posts[postId];
postsArray.push(cpost); if (cpost.root_id === rootPost.id) {
postsArray.push(cpost);
}
} }
} }
@@ -209,6 +215,7 @@ RhsThread.defaultProps = {
fromSearch: '', fromSearch: '',
isMentionSearch: false isMentionSearch: false
}; };
RhsThread.propTypes = { RhsThread.propTypes = {
fromSearch: React.PropTypes.string, fromSearch: React.PropTypes.string,
isMentionSearch: React.PropTypes.bool isMentionSearch: React.PropTypes.bool

Просмотреть файл

@@ -1,25 +1,33 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
module.exports = React.createClass({ export default class SettingPicture extends React.Component {
setPicture: function(file) { constructor(props) {
super(props);
this.setPicture = this.setPicture.bind(this);
}
setPicture(file) {
if (file) { if (file) {
var reader = new FileReader(); var reader = new FileReader();
var img = this.refs.image.getDOMNode(); var img = this.refs.image.getDOMNode();
reader.onload = function(e) { reader.onload = function load(e) {
$(img).attr('src', e.target.result); $(img).attr('src', e.target.result);
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
} }
}, }
componentWillReceiveProps: function(nextProps) {
componentWillReceiveProps(nextProps) {
if (nextProps.picture) { if (nextProps.picture) {
this.setPicture(nextProps.picture); this.setPicture(nextProps.picture);
} }
}, }
render: function() {
render() {
var clientError = null; var clientError = null;
if (this.props.client_error) { if (this.props.client_error) {
clientError = <div className='form-group has-error'><label className='control-label'>{this.props.client_error}</label></div>; clientError = <div className='form-group has-error'><label className='control-label'>{this.props.client_error}</label></div>;
@@ -31,14 +39,31 @@ module.exports = React.createClass({
var img = null; var img = null;
if (this.props.picture) { if (this.props.picture) {
img = (<img ref='image' className='profile-img' src=''/>); img = (
<img
ref='image'
className='profile-img'
src=''
/>
);
} else { } else {
img = (<img ref='image' className='profile-img' src={this.props.src}/>); img = (
<img
ref='image'
className='profile-img'
src={this.props.src}
/>
);
} }
var confirmButton; var confirmButton;
if (this.props.loadingPicture) { if (this.props.loadingPicture) {
confirmButton = <img className='spinner' src='/static/images/load.gif'/>; confirmButton = (
<img
className='spinner'
src='/static/images/load.gif'
/>
);
} else { } else {
var confirmButtonClass = 'btn btn-sm'; var confirmButtonClass = 'btn btn-sm';
if (this.props.submitActive) { if (this.props.submitActive) {
@@ -46,9 +71,15 @@ module.exports = React.createClass({
} else { } else {
confirmButtonClass += ' btn-inactive disabled'; confirmButtonClass += ' btn-inactive disabled';
} }
confirmButton = <a className={confirmButtonClass} onClick={this.props.submit}>Save</a>;
confirmButton = (
<a
className={confirmButtonClass}
onClick={this.props.submit}
>Save</a>
);
} }
var helpText = 'Upload a profile picture in either JPG or PNG format, at least ' + config.ProfileWidth + 'px in width and ' + config.ProfileHeight + 'px height.' var helpText = 'Upload a profile picture in either JPG or PNG format, at least ' + config.ProfileWidth + 'px in width and ' + config.ProfileHeight + 'px height.';
var self = this; var self = this;
return ( return (
@@ -65,13 +96,36 @@ module.exports = React.createClass({
<li className='setting-list-item'> <li className='setting-list-item'>
{serverError} {serverError}
{clientError} {clientError}
<span className='btn btn-sm btn-primary btn-file sel-btn'>Select<input ref='input' accept='.jpg,.png,.bmp' type='file' onChange={this.props.pictureChange}/></span> <span className='btn btn-sm btn-primary btn-file sel-btn'
>Select<input
ref='input'
accept='.jpg,.png,.bmp'
type='file'
onChange={this.props.pictureChange}
/>
</span>
{confirmButton} {confirmButton}
<a className='btn btn-sm theme' href='#' onClick={self.props.updateSection}>Cancel</a> <a
className='btn btn-sm theme'
href='#'
onClick={self.props.updateSection}
>Cancel</a>
</li> </li>
</ul> </ul>
</li> </li>
</ul> </ul>
); );
} }
}); }
SettingPicture.propTypes = {
client_error: React.PropTypes.string,
server_error: React.PropTypes.string,
src: React.PropTypes.string,
picture: React.PropTypes.object,
loadingPicture: React.PropTypes.bool,
submitActive: React.PropTypes.bool,
submit: React.PropTypes.func,
title: React.PropTypes.string,
pictureChange: React.PropTypes.func
};

Просмотреть файл

@@ -1,36 +1,37 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
module.exports = React.createClass({ export default class SettingsUpload extends React.Component {
displayName: 'Setting Upload',
propTypes: { constructor(props) {
title: React.PropTypes.string.isRequired, super(props);
submit: React.PropTypes.func.isRequired,
fileTypesAccepted: React.PropTypes.string.isRequired, this.doFileSelect = this.doFileSelect.bind(this);
clientError: React.PropTypes.string, this.doSubmit = this.doSubmit.bind(this);
serverError: React.PropTypes.string, this.onFileSelect = this.onFileSelect.bind(this);
helpText: React.PropTypes.string
}, this.state = {
getInitialState: function() {
return {
clientError: this.props.clientError, clientError: this.props.clientError,
serverError: this.props.serverError serverError: this.props.serverError
}; };
}, }
componentWillReceiveProps: function() {
componentWillReceiveProps() {
this.setState({ this.setState({
clientError: this.props.clientError, clientError: this.props.clientError,
serverError: this.props.serverError serverError: this.props.serverError
}); });
}, }
doFileSelect: function(e) {
doFileSelect(e) {
e.preventDefault(); e.preventDefault();
this.setState({ this.setState({
clientError: '', clientError: '',
serverError: '' serverError: ''
}); });
}, }
doSubmit: function(e) {
doSubmit(e) {
e.preventDefault(); e.preventDefault();
var inputnode = this.refs.uploadinput.getDOMNode(); var inputnode = this.refs.uploadinput.getDOMNode();
if (inputnode.files && inputnode.files[0]) { if (inputnode.files && inputnode.files[0]) {
@@ -38,16 +39,18 @@ module.exports = React.createClass({
} else { } else {
this.setState({clientError: 'No file selected.'}); this.setState({clientError: 'No file selected.'});
} }
}, }
onFileSelect: function(e) {
onFileSelect(e) {
var filename = $(e.target).val(); var filename = $(e.target).val();
if (filename.substring(3, 11) === 'fakepath') { if (filename.substring(3, 11) === 'fakepath') {
filename = filename.substring(12); filename = filename.substring(12);
} }
$(e.target).closest('li').find('.file-status').addClass('hide'); $(e.target).closest('li').find('.file-status').addClass('hide');
$(e.target).closest('li').find('.file-name').removeClass('hide').html(filename); $(e.target).closest('li').find('.file-name').removeClass('hide').html(filename);
}, }
render: function() {
render() {
var clientError = null; var clientError = null;
if (this.state.clientError) { if (this.state.clientError) {
clientError = ( clientError = (
@@ -67,7 +70,11 @@ module.exports = React.createClass({
<li className='col-xs-offset-3 col-xs-8'> <li className='col-xs-offset-3 col-xs-8'>
<ul className='setting-list'> <ul className='setting-list'>
<li className='setting-list-item'> <li className='setting-list-item'>
<span className='btn btn-sm btn-primary btn-file sel-btn'>Select file<input ref='uploadinput' accept={this.props.fileTypesAccepted} type='file' onChange={this.onFileSelect}/></span> <span className='btn btn-sm btn-primary btn-file sel-btn'>Select file<input
ref='uploadinput'
accept={this.props.fileTypesAccepted}
type='file'
onChange={this.onFileSelect}/></span>
<a <a
className={'btn btn-sm btn-primary'} className={'btn btn-sm btn-primary'}
onClick={this.doSubmit}> onClick={this.doSubmit}>
@@ -82,4 +89,13 @@ module.exports = React.createClass({
</ul> </ul>
); );
} }
}); }
SettingsUpload.propTypes = {
title: React.PropTypes.string.isRequired,
submit: React.PropTypes.func.isRequired,
fileTypesAccepted: React.PropTypes.string.isRequired,
clientError: React.PropTypes.string,
serverError: React.PropTypes.string,
helpText: React.PropTypes.object
};

Просмотреть файл

@@ -5,12 +5,19 @@ var UserStore = require('../stores/user_store.jsx');
var client = require('../utils/client.jsx'); var client = require('../utils/client.jsx');
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
module.exports = React.createClass({ export default class SidebarRightMenu extends React.Component {
handleLogoutClick: function(e) { constructor(props) {
super(props);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
}
handleLogoutClick(e) {
e.preventDefault(); e.preventDefault();
client.logout(); client.logout();
}, }
render: function() {
render() {
var teamLink = ''; var teamLink = '';
var inviteLink = ''; var inviteLink = '';
var teamSettingsLink = ''; var teamSettingsLink = '';
@@ -23,14 +30,22 @@ module.exports = React.createClass({
inviteLink = ( inviteLink = (
<li> <li>
<a href='#' data-toggle='modal' data-target='#invite_member'><i className='glyphicon glyphicon-user'></i>Invite New Member</a> <a href='#'
data-toggle='modal'
data-target='#invite_member'
><i className='glyphicon glyphicon-user'></i>Invite New Member</a>
</li> </li>
); );
if (this.props.teamType === 'O') { if (this.props.teamType === 'O') {
teamLink = ( teamLink = (
<li> <li>
<a href='#' data-toggle='modal' data-target='#get_link' data-title='Team Invite' data-value={utils.getWindowLocationOrigin()+'/signup_user_complete/?id='+currentUser.team_id}><i className='glyphicon glyphicon-link'></i>Get Team Invite Link</a> <a href='#'
data-toggle='modal'
data-target='#get_link'
data-title='Team Invite'
data-value={utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + currentUser.team_id}
><i className='glyphicon glyphicon-link'></i>Get Team Invite Link</a>
</li> </li>
); );
} }
@@ -39,12 +54,20 @@ module.exports = React.createClass({
if (isAdmin) { if (isAdmin) {
teamSettingsLink = ( teamSettingsLink = (
<li> <li>
<a href='#' data-toggle='modal' data-target='#team_settings'><i className='glyphicon glyphicon-globe'></i>Team Settings</a> <a
href='#'
data-toggle='modal'
data-target='#team_settings'
><i className='glyphicon glyphicon-globe'></i>Team Settings</a>
</li> </li>
); );
manageLink = ( manageLink = (
<li> <li>
<a href='#' data-toggle='modal' data-target='#team_members'><i className='glyphicon glyphicon-wrench'></i>Manage Team</a> <a
href='#'
data-toggle='modal'
data-target='#team_members'
><i className='glyphicon glyphicon-wrench'></i>Manage Team</a>
</li> </li>
); );
} }
@@ -61,23 +84,48 @@ module.exports = React.createClass({
return ( return (
<div> <div>
<div className='team__header theme'> <div className='team__header theme'>
<a className='team__name' href='/channels/town-square'>{teamDisplayName}</a> <a
className='team__name'
href='/channels/town-square'
>{teamDisplayName}</a>
</div> </div>
<div className='nav-pills__container'> <div className='nav-pills__container'>
<ul className='nav nav-pills nav-stacked'> <ul className='nav nav-pills nav-stacked'>
<li><a href='#' data-toggle='modal' data-target='#user_settings'><i className='glyphicon glyphicon-cog'></i>Account Settings</a></li> <li>
<a
href='#'
data-toggle='modal'
data-target='#user_settings'
><i className='glyphicon glyphicon-cog'></i>Account Settings</a></li>
{teamSettingsLink} {teamSettingsLink}
{inviteLink} {inviteLink}
{teamLink} {teamLink}
{manageLink} {manageLink}
<li><a href='#' onClick={this.handleLogoutClick}><i className='glyphicon glyphicon-log-out'></i>Logout</a></li> <li>
<a
href='#'
onClick={this.handleLogoutClick}
><i className='glyphicon glyphicon-log-out'></i>Logout</a></li>
<li className='divider'></li> <li className='divider'></li>
<li><a target='_blank' href='/static/help/configure_links.html'><i className='glyphicon glyphicon-question-sign'></i>Help</a></li> <li>
<li><a target='_blank' href='/static/help/configure_links.html'><i className='glyphicon glyphicon-earphone'></i>Report a Problem</a></li> <a
target='_blank'
href='/static/help/configure_links.html'
><i className='glyphicon glyphicon-question-sign'></i>Help</a></li>
<li>
<a
target='_blank'
href='/static/help/configure_links.html'
><i className='glyphicon glyphicon-earphone'></i>Report a Problem</a></li>
</ul> </ul>
</div> </div>
</div> </div>
); );
} }
}); }
SidebarRightMenu.propTypes = {
teamType: React.PropTypes.string,
teamDisplayName: React.PropTypes.string
};

Просмотреть файл

@@ -4,26 +4,38 @@
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
var SettingUpload = require('./setting_upload.jsx'); var SettingUpload = require('./setting_upload.jsx');
module.exports = React.createClass({ export default class TeamImportTab extends React.Component {
displayName: 'Import Tab', constructor(props) {
getInitialState: function() { super(props);
return {status: 'ready', link: ''};
}, this.onImportFailure = this.onImportFailure.bind(this);
onImportFailure: function() { this.onImportSuccess = this.onImportSuccess.bind(this);
this.doImportSlack = this.doImportSlack.bind(this);
this.state = {
status: 'ready',
link: ''
};
}
onImportFailure() {
this.setState({status: 'fail', link: ''}); this.setState({status: 'fail', link: ''});
}, }
onImportSuccess: function(data) {
onImportSuccess(data) {
this.setState({status: 'done', link: 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(data)}); this.setState({status: 'done', link: 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(data)});
}, }
doImportSlack: function(file) {
doImportSlack(file) {
this.setState({status: 'in-progress', link: ''}); this.setState({status: 'in-progress', link: ''});
utils.importSlack(file, this.onImportSuccess, this.onImportFailure); utils.importSlack(file, this.onImportSuccess, this.onImportFailure);
}, }
render: function() {
render() {
var uploadHelpText = ( var uploadHelpText = (
<div> <div>
<br/> <br/>
Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team's public channels. Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team's public channels.
<br/><br/> <br/><br/>
The Slack import to Mattermost is in "Preview". Slack bot posts and channels with underscores do not yet import. The Slack import to Mattermost is in "Preview". Slack bot posts and channels with underscores do not yet import.
<br/><br/> <br/><br/>
@@ -39,22 +51,25 @@ module.exports = React.createClass({
var messageSection; var messageSection;
switch (this.state.status) { switch (this.state.status) {
case 'ready':
messageSection = ''; case 'ready':
messageSection = '';
break; break;
case 'in-progress': case 'in-progress':
messageSection = ( messageSection = (
<p className="confirm-import alert alert-warning"><i className="fa fa-spinner fa-pulse"></i> Importing...</p> <p className='confirm-import alert alert-warning'><i className='fa fa-spinner fa-pulse'></i> Importing...</p>
); );
break; break;
case 'done': case 'done':
messageSection = ( messageSection = (
<p className="confirm-import alert alert-success"><i className="fa fa-check"></i> Import successful: <a href={this.state.link} download='MattermostImportSummary.txt'>View Summary</a></p> <p className='confirm-import alert alert-success'><i className='fa fa-check'></i> Import successful: <a href={this.state.link}
download='MattermostImportSummary.txt'>View Summary</a></p>
); );
break; break;
case 'fail': case 'fail':
messageSection = ( messageSection = (
<p className="confirm-import alert alert-warning"><i className="fa fa-warning"></i> Import failure: <a href={this.state.link} download='MattermostImportSummary.txt'>View Summary</a></p> <p className='confirm-import alert alert-warning'><i className='fa fa-warning'></i> Import failure: <a href={this.state.link}
download='MattermostImportSummary.txt'>View Summary</a></p>
); );
break; break;
} }
@@ -62,10 +77,22 @@ module.exports = React.createClass({
return ( return (
<div> <div>
<div className='modal-header'> <div className='modal-header'>
<button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button> <button type='button'
<h4 className='modal-title' ref='title'><i className='modal-back'></i>Import</h4> className='close'
data-dismiss='modal'
aria-label='Close'
>
<span aria-hidden='true'>&times;</span>
</button>
<h4
className='modal-title'
ref='title'
><i className='modal-back'></i>Import</h4>
</div> </div>
<div ref='wrapper' className='user-settings'> <div
ref='wrapper'
className='user-settings'
>
<h3 className='tab-header'>Import</h3> <h3 className='tab-header'>Import</h3>
<div className='divider-dark first'/> <div className='divider-dark first'/>
{uploadSection} {uploadSection}
@@ -75,4 +102,4 @@ module.exports = React.createClass({
</div> </div>
); );
} }
}); }

Просмотреть файл

@@ -2,77 +2,131 @@
// See License.txt for license information. // See License.txt for license information.
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
var MemberListTeam = require('./member_list_team.jsx'); var MemberListTeam = require('./member_list_team.jsx');
var Client = require('../utils/client.jsx');
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
function getStateFromStores() { function getStateFromStores() {
var users = UserStore.getProfiles(); var users = UserStore.getProfiles();
var member_list = []; var memberList = [];
for (var id in users) member_list.push(users[id]); for (var id in users) {
if (users.hasOwnProperty(id)) {
memberList.push(users[id]);
}
}
memberList.sort(function sort(a, b) {
if (a.username < b.username) {
return -1;
}
if (a.username > b.username) {
return 1;
}
member_list.sort(function(a,b) {
if (a.username < b.username) return -1;
if (a.username > b.username) return 1;
return 0; return 0;
}); });
return { return {
member_list: member_list member_list: memberList
}; };
} }
module.exports = React.createClass({ export default class TeamMembers extends React.Component {
componentDidMount: function() { constructor(props) {
UserStore.addChangeListener(this._onChange); super(props);
this.onChange = this.onChange.bind(this);
this.state = getStateFromStores();
}
componentDidMount() {
UserStore.addChangeListener(this.onChange);
var self = this; var self = this;
$(this.refs.modal.getDOMNode()).on('hidden.bs.modal', function(e) { $(this.refs.modal.getDOMNode()).on('hidden.bs.modal', function show() {
self.setState({ render_members: false }); self.setState({render_members: false});
}); });
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) { $(this.refs.modal.getDOMNode()).on('show.bs.modal', function hide() {
self.setState({ render_members: true }); self.setState({render_members: true});
}); });
}, }
componentWillUnmount: function() {
UserStore.removeChangeListener(this._onChange); componentWillUnmount() {
}, UserStore.removeChangeListener(this.onChange);
_onChange: function() { }
onChange() {
var newState = getStateFromStores(); var newState = getStateFromStores();
if (!utils.areStatesEqual(newState, this.state)) { if (!utils.areStatesEqual(newState, this.state)) {
this.setState(newState); this.setState(newState);
} }
}, }
getInitialState: function() {
return getStateFromStores(); render() {
}, var serverError = null;
render: function() {
var server_error = this.state.server_error ? <label className='has-error control-label'>{this.state.server_error}</label> : null; if (this.state.server_error) {
serverError = <label className='has-error control-label'>{this.state.server_error}</label>;
}
var renderMembers = '';
if (this.state.render_members) {
renderMembers = <MemberListTeam users={this.state.member_list} />;
}
return ( return (
<div className="modal fade" ref="modal" id="team_members" tabIndex="-1" role="dialog" aria-hidden="true"> <div
<div className="modal-dialog"> className='modal fade'
<div className="modal-content"> ref='modal'
<div className="modal-header"> id='team_members'
<button type="button" className="close" data-dismiss="modal" aria-label="Close" data-reactid=".5.0.0.0.0"><span aria-hidden="true" data-reactid=".5.0.0.0.0.0">×</span></button> tabIndex='-1'
<h4 className="modal-title" id="myModalLabel">{this.props.teamDisplayName + " Members"}</h4> role='dialog'
</div> aria-hidden='true'
<div ref="modalBody" className="modal-body"> >
<div className="channel-settings"> <div className='modal-dialog'>
<div className="team-member-list"> <div className='modal-content'>
{ this.state.render_members ? <MemberListTeam users={this.state.member_list} /> : "" } <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'
id='myModalLabel'
>{this.props.teamDisplayName + ' Members'}</h4>
</div>
<div
ref='modalBody'
className='modal-body'
>
<div className='channel-settings'>
<div className='team-member-list'>
{renderMembers}
</div>
{serverError}
</div> </div>
{ server_error } </div>
<div className='modal-footer'>
<button
type='button'
className='btn btn-default'
data-dismiss='modal'
>Close</button>
</div> </div>
</div> </div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div> </div>
</div> </div>
); );
} }
}); }
TeamMembers.propTypes = {
teamDisplayName: React.PropTypes.string
};

Просмотреть файл

@@ -8,56 +8,82 @@ var SecurityTab = require('./user_settings_security.jsx');
var GeneralTab = require('./user_settings_general.jsx'); var GeneralTab = require('./user_settings_general.jsx');
var AppearanceTab = require('./user_settings_appearance.jsx'); var AppearanceTab = require('./user_settings_appearance.jsx');
module.exports = React.createClass({ export default class UserSettings extends React.Component {
displayName: 'UserSettings', constructor(props) {
propTypes: { super(props);
activeTab: React.PropTypes.string,
activeSection: React.PropTypes.string, this.onListenerChange = this.onListenerChange.bind(this);
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func this.state = {user: UserStore.getCurrentUser()};
}, }
componentDidMount: function() {
componentDidMount() {
UserStore.addChangeListener(this.onListenerChange); UserStore.addChangeListener(this.onListenerChange);
}, }
componentWillUnmount: function() {
componentWillUnmount() {
UserStore.removeChangeListener(this.onListenerChange); UserStore.removeChangeListener(this.onListenerChange);
}, }
onListenerChange: function () {
onListenerChange() {
var user = UserStore.getCurrentUser(); var user = UserStore.getCurrentUser();
if (!utils.areStatesEqual(this.state.user, user)) { if (!utils.areStatesEqual(this.state.user, user)) {
this.setState({user: user}); this.setState({user: user});
} }
}, }
getInitialState: function() {
return {user: UserStore.getCurrentUser()}; render() {
},
render: function() {
if (this.props.activeTab === 'general') { if (this.props.activeTab === 'general') {
return ( return (
<div> <div>
<GeneralTab user={this.state.user} activeSection={this.props.activeSection} updateSection={this.props.updateSection} /> <GeneralTab
user={this.state.user}
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
/>
</div> </div>
); );
} else if (this.props.activeTab === 'security') { } else if (this.props.activeTab === 'security') {
return ( return (
<div> <div>
<SecurityTab user={this.state.user} activeSection={this.props.activeSection} updateSection={this.props.updateSection} updateTab={this.props.updateTab} /> <SecurityTab
user={this.state.user}
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
updateTab={this.props.updateTab}
/>
</div> </div>
); );
} else if (this.props.activeTab === 'notifications') { } else if (this.props.activeTab === 'notifications') {
return ( return (
<div> <div>
<NotificationsTab user={this.state.user} activeSection={this.props.activeSection} updateSection={this.props.updateSection} updateTab={this.props.updateTab} /> <NotificationsTab
user={this.state.user}
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
updateTab={this.props.updateTab}
/>
</div> </div>
); );
} else if (this.props.activeTab === 'appearance') { } else if (this.props.activeTab === 'appearance') {
return ( return (
<div> <div>
<AppearanceTab activeSection={this.props.activeSection} updateSection={this.props.updateSection} updateTab={this.props.updateTab} /> <AppearanceTab
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
updateTab={this.props.updateTab}
/>
</div> </div>
); );
} else {
return <div/>;
} }
return <div/>;
} }
}); }
UserSettings.propTypes = {
activeTab: React.PropTypes.string,
activeSection: React.PropTypes.string,
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func
};