MM-2065 style refactoring

This commit is contained in:
=Corey Hulen
2015-09-01 17:06:31 -07:00
parent 72575ac7bd
commit f578bb1e48
18 changed files with 1003 additions and 459 deletions

View File

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

View File

@@ -9,7 +9,6 @@ var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
export default class ChannelInviteModal extends React.Component {
constructor() {
super();
@@ -129,15 +128,37 @@ export default class ChannelInviteModal extends React.Component {
if (this.state.loading) {
content = (<LoadingScreen />);
} else {
content = (<MemberList memberList={this.state.nonmembers} isAdmin={isAdmin} handleInvite={this.handleInvite} />);
content = (
<MemberList
memberList={this.state.nonmembers}
isAdmin={isAdmin}
handleInvite={this.handleInvite}
/>
);
}
return (
<div className='modal fade' id='channel_invite' tabIndex='-1' role='dialog' aria-hidden='true'>
<div className='modal-dialog' role='document'>
<div
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-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>
</div>
<div className='modal-body'>
@@ -145,7 +166,11 @@ export default class ChannelInviteModal extends React.Component {
{content}
</div>
<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>
@@ -153,4 +178,3 @@ export default class ChannelInviteModal extends React.Component {
);
}
}
ChannelInviteModal.displayName = 'ChannelInviteModal';

View File

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

View File

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

View File

@@ -1,53 +1,57 @@
// 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');
module.exports = React.createClass({
handleSubmit: function(e) {
export default class FindTeam extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
var state = { };
var email = this.refs.email.getDOMNode().value.trim().toLowerCase();
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);
return;
}
else {
state.email_error = "";
}
state.email_error = '';
client.findTeamsSendEmail(email,
function(data) {
function success() {
state.sent = true;
this.setState(state);
}.bind(this),
function(err) {
function fail(err) {
state.email_error = err.message;
this.setState(state);
}.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 = {
"marginTop": "50px",
if (this.state.email_error) {
emailError = <label className='control-label'>{this.state.email_error}</label>;
emailErrorClass = 'form-group has-error';
}
if (this.state.sent) {
return (
<div>
<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>
<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>
</div>
);
}
@@ -56,17 +60,25 @@ module.exports = React.createClass({
<div>
<h4>Find Your Team</h4>
<form onSubmit={this.handleSubmit}>
<p>{"Get an email with links to any " + strings.TeamPlural + " to which you are a member."}</p>
<div className="form-group">
<p>{'Get an email with links to any ' + strings.TeamPlural + ' to which you are a member.'}</p>
<div className='form-group'>
<label className='control-label'>Email</label>
<div className={ email_error ? "form-group has-error" : "form-group" }>
<input type="text" ref="email" className="form-control" placeholder="you@domain.com" maxLength="128" />
{ email_error }
<div className={emailErrorClass}>
<input
type='text'
ref='email'
className='form-control'
placeholder='you@domain.com'
maxLength='128'
/>
{emailError}
</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>
</div>
);
}
});
}

View File

@@ -7,10 +7,28 @@ var Client = require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx');
var ConfirmModal = require('./confirm_modal.jsx');
module.exports = React.createClass({
componentDidMount: function() {
export default class InviteMemberModal extends React.Component {
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;
$('#invite_member').on('hide.bs.modal', function(e) {
$('#invite_member').on('hide.bs.modal', function hide(e) {
if ($('#invite_member').attr('data-confirm') === 'true') {
$('#invite_member').attr('data-confirm', 'false');
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();
});
},
handleSubmit: function(e) {
}
handleSubmit() {
if (!this.state.emailEnabled) {
return;
}
@@ -90,11 +109,11 @@ module.exports = React.createClass({
data.invites = invites;
Client.inviteMembers(data,
function() {
function success() {
$(this.refs.modal.getDOMNode()).attr('data-confirm', 'true');
$(this.refs.modal.getDOMNode()).modal('hide');
}.bind(this),
function(err) {
function fail(err) {
if (err.message === 'This person is already on your team') {
emailErrors[err.detailed_error] = err.message;
this.setState({emailErrors: emailErrors});
@@ -103,18 +122,21 @@ module.exports = React.createClass({
}
}.bind(this)
);
},
componentDidUpdate: function() {
}
componentDidUpdate() {
$(this.refs.modalBody.getDOMNode()).css('max-height', $(window).height() - 200);
$(this.refs.modalBody.getDOMNode()).css('overflow-y', 'scroll');
},
addInviteFields: function() {
}
addInviteFields() {
var count = this.state.idCount + 1;
var inviteIds = this.state.inviteIds;
inviteIds.push(count);
this.setState({inviteIds: inviteIds, idCount: count});
},
clearFields: function() {
}
clearFields() {
var inviteIds = this.state.inviteIds;
for (var i = 0; i < inviteIds.length; i++) {
@@ -133,8 +155,9 @@ module.exports = React.createClass({
firstNameErrors: {},
lastNameErrors: {}
});
},
removeInviteFields: function(index) {
}
removeInviteFields(index) {
var count = this.state.idCount;
var inviteIds = this.state.inviteIds;
var i = inviteIds.indexOf(index);
@@ -145,24 +168,10 @@ module.exports = React.createClass({
inviteIds.push(++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 = '';
if (!this.state.emailEnabled) {
inputDisabled = 'disabled';
}
render() {
var currentUser = UserStore.getCurrentUser();
if (currentUser != null) {
var inviteSections = [];
@@ -185,7 +194,13 @@ module.exports = React.createClass({
var removeButton = null;
if (index) {
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>);
}
var emailClass = 'form-group invite';
@@ -206,13 +221,27 @@ module.exports = React.createClass({
nameFields = (<div className='row--invite'>
<div className='col-sm-6'>
<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}
</div>
</div>
<div className='col-sm-6'>
<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}
</div>
</div>
@@ -223,7 +252,15 @@ module.exports = React.createClass({
<div key={'key' + index}>
{removeButton}
<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}
</div>
{nameFields}
@@ -242,23 +279,44 @@ module.exports = React.createClass({
content = (
<div>
{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/>
<span>People invited automatically join Town Square channel.</span>
</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 {
var teamInviteLink = null;
if (currentUser && this.props.teamType === 'O') {
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={
function() {
$('#invite_member').modal('hide');
}
}>Team Invite Link</a>;
var link =
(
<a
href='#'
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 = (
<p>
@@ -277,22 +335,46 @@ module.exports = React.createClass({
return (
<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-content'>
<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>
<h4 className='modal-title' id='myModalLabel'>Invite New Member</h4>
<button
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 ref='modalBody' className='modal-body'>
<div
ref='modalBody'
className='modal-body'
>
<form role='form'>
{inviteSections}
</form>
{content}
</div>
<div className='modal-footer'>
<button type='button' className='btn btn-default' data-dismiss='modal'>Cancel</button>
{sendButton}
<button
type='button'
className='btn btn-default'
data-dismiss='modal'
>Cancel</button>
{sendButton}
</div>
</div>
</div>
@@ -309,4 +391,8 @@ module.exports = React.createClass({
}
return <div/>;
}
});
}
InviteMemberModal.propTypes = {
teamType: React.PropTypes.string
};

View File

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

View File

@@ -7,19 +7,22 @@ var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var utils = require('../utils/utils.jsx');
module.exports = React.createClass({
displayName: 'MoreDirectChannels',
componentDidMount: function() {
export default class MoreDirectChannels extends React.Component {
constructor(props) {
super(props);
this.state = {channels: [], loadingDMChannel: -1};
}
componentDidMount() {
var self = this;
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function showModal(e) {
var button = e.relatedTarget;
self.setState({channels: $(button).data('channels')});
});
},
getInitialState: function() {
return {channels: [], loadingDMChannel: -1};
},
render: function() {
}
render() {
var self = this;
var directMessageItems = this.state.channels.map(function mapActivityToChannel(channel, index) {
@@ -48,7 +51,12 @@ module.exports = React.createClass({
var otherUserId = utils.getUserIdFromChannelName(channel);
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) {
@@ -73,16 +81,36 @@ module.exports = React.createClass({
}
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 (
<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-content'>
<div className='modal-header'>
<button type='button' className='close' data-dismiss='modal'>
<button
type='button'
className='close'
data-dismiss='modal'
>
<span aria-hidden='true'>&times;</span>
<span className='sr-only'>Close</span>
</button>
@@ -94,11 +122,15 @@ module.exports = React.createClass({
</ul>
</div>
<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>
);
}
});
}

View File

@@ -1,57 +1,70 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var SocketStore = require('../stores/socket_store.jsx');
var UserStore = require('../stores/user_store.jsx');
module.exports = React.createClass({
timer: null,
lastTime: 0,
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) {
export default class MsgTyping extends React.Component {
constructor(props) {
super(props);
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();
var username = "Someone";
var username = 'Someone';
if (UserStore.hasProfile(msg.user_id)) {
username = UserStore.getProfile(msg.user_id).username;
}
this.setState({ text: username + " is typing..." });
this.setState({text: username + ' is typing...'});
if (!this.timer) {
var outer = this;
outer.timer = setInterval(function() {
if ((new Date().getTime() - outer.lastTime) > 8000) {
outer.setState({ text: "" });
}
}, 3000);
this.timer = setInterval(function myTimer() {
if ((new Date().getTime() - this.lastTime) > 8000) {
this.setState({text: ''});
}
}.bind(this), 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:""})
}
},
getInitialState: function() {
return { text: "" };
},
render: function() {
}
render() {
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
};

View File

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

View File

@@ -3,62 +3,99 @@
var ChannelStore = require('../stores/channel_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');
module.exports = React.createClass({
handleShow: function() {
var newState = {};
if(BrowserStore.getItem("channel-removed-state")) {
newState = BrowserStore.getItem("channel-removed-state");
BrowserStore.removeItem("channel-removed-state");
}
export default class RemovedFromChannelModal extends React.Component {
constructor(props) {
super(props);
this.setState(newState);
},
handleClose: function() {
var townSquare = ChannelStore.getByName("town-square");
utils.switchChannel(townSquare);
this.handleShow = this.handleShow.bind(this);
this.handleClose = this.handleClose.bind(this);
this.setState({channelName: "", remover: ""});
},
componentDidMount: function() {
$(this.getDOMNode()).on('show.bs.modal',this.handleShow);
$(this.getDOMNode()).on('hidden.bs.modal',this.handleClose);
},
componentWillUnmount: function() {
$(this.getDOMNode()).off('show.bs.modal',this.handleShow);
$(this.getDOMNode()).off('hidden.bs.modal',this.handleClose);
},
getInitialState: function() {
return {channelName: "", remover: ""}
},
render: function() {
this.state = {
channelName: '',
remover: ''
};
}
handleShow() {
var newState = {};
if (BrowserStore.getItem('channel-removed-state')) {
newState = BrowserStore.getItem('channel-removed-state');
BrowserStore.removeItem('channel-removed-state');
}
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 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) {
return (
<div className='modal fade' ref='modal' id='removed_from_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' aria-label='Close'><span aria-hidden='true'>&times;</span></button>
<h4 className='modal-title'>Removed from <span className='name'>{channelName}</span></h4>
<div
className='modal fade'
ref='modal'
id='removed_from_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'
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 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>
</div>
);
} else {
return <div/>;
}
return <div/>;
}
});
}

View File

@@ -33,7 +33,9 @@ export default class RhsThread extends React.Component {
if (pendingPostList) {
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) {
currentSelected.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);
@@ -128,9 +132,11 @@ export default class RhsThread extends React.Component {
var postsArray = [];
for (var postId in postList.posts) {
var cpost = postList.posts[postId];
if (cpost.root_id === rootPost.id) {
postsArray.push(cpost);
if (postList.posts.hasOwnProperty(postId)) {
var cpost = postList.posts[postId];
if (cpost.root_id === rootPost.id) {
postsArray.push(cpost);
}
}
}
@@ -209,6 +215,7 @@ RhsThread.defaultProps = {
fromSearch: '',
isMentionSearch: false
};
RhsThread.propTypes = {
fromSearch: React.PropTypes.string,
isMentionSearch: React.PropTypes.bool

View File

@@ -1,25 +1,33 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
module.exports = React.createClass({
setPicture: function(file) {
export default class SettingPicture extends React.Component {
constructor(props) {
super(props);
this.setPicture = this.setPicture.bind(this);
}
setPicture(file) {
if (file) {
var reader = new FileReader();
var img = this.refs.image.getDOMNode();
reader.onload = function(e) {
reader.onload = function load(e) {
$(img).attr('src', e.target.result);
};
reader.readAsDataURL(file);
}
},
componentWillReceiveProps: function(nextProps) {
}
componentWillReceiveProps(nextProps) {
if (nextProps.picture) {
this.setPicture(nextProps.picture);
}
},
render: function() {
}
render() {
var clientError = null;
if (this.props.client_error) {
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;
if (this.props.picture) {
img = (<img ref='image' className='profile-img' src=''/>);
img = (
<img
ref='image'
className='profile-img'
src=''
/>
);
} 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;
if (this.props.loadingPicture) {
confirmButton = <img className='spinner' src='/static/images/load.gif'/>;
confirmButton = (
<img
className='spinner'
src='/static/images/load.gif'
/>
);
} else {
var confirmButtonClass = 'btn btn-sm';
if (this.props.submitActive) {
@@ -46,9 +71,15 @@ module.exports = React.createClass({
} else {
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;
return (
@@ -65,13 +96,36 @@ module.exports = React.createClass({
<li className='setting-list-item'>
{serverError}
{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}
<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>
</ul>
</li>
</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
};

View File

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

View File

@@ -5,12 +5,19 @@ var UserStore = require('../stores/user_store.jsx');
var client = require('../utils/client.jsx');
var utils = require('../utils/utils.jsx');
module.exports = React.createClass({
handleLogoutClick: function(e) {
export default class SidebarRightMenu extends React.Component {
constructor(props) {
super(props);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
}
handleLogoutClick(e) {
e.preventDefault();
client.logout();
},
render: function() {
}
render() {
var teamLink = '';
var inviteLink = '';
var teamSettingsLink = '';
@@ -23,14 +30,22 @@ module.exports = React.createClass({
inviteLink = (
<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>
);
if (this.props.teamType === 'O') {
teamLink = (
<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>
);
}
@@ -39,12 +54,20 @@ module.exports = React.createClass({
if (isAdmin) {
teamSettingsLink = (
<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>
);
manageLink = (
<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>
);
}
@@ -61,23 +84,48 @@ module.exports = React.createClass({
return (
<div>
<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 className='nav-pills__container'>
<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}
{inviteLink}
{teamLink}
{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><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>
<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>
</div>
</div>
);
}
});
}
SidebarRightMenu.propTypes = {
teamType: React.PropTypes.string,
teamDisplayName: React.PropTypes.string
};

View File

@@ -4,26 +4,38 @@
var utils = require('../utils/utils.jsx');
var SettingUpload = require('./setting_upload.jsx');
module.exports = React.createClass({
displayName: 'Import Tab',
getInitialState: function() {
return {status: 'ready', link: ''};
},
onImportFailure: function() {
export default class TeamImportTab extends React.Component {
constructor(props) {
super(props);
this.onImportFailure = this.onImportFailure.bind(this);
this.onImportSuccess = this.onImportSuccess.bind(this);
this.doImportSlack = this.doImportSlack.bind(this);
this.state = {
status: 'ready',
link: ''
};
}
onImportFailure() {
this.setState({status: 'fail', link: ''});
},
onImportSuccess: function(data) {
}
onImportSuccess(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: ''});
utils.importSlack(file, this.onImportSuccess, this.onImportFailure);
},
render: function() {
}
render() {
var uploadHelpText = (
<div>
<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/>
The Slack import to Mattermost is in "Preview". Slack bot posts and channels with underscores do not yet import.
<br/><br/>
@@ -39,22 +51,25 @@ module.exports = React.createClass({
var messageSection;
switch (this.state.status) {
case 'ready':
messageSection = '';
case 'ready':
messageSection = '';
break;
case 'in-progress':
messageSection = (
<p className="confirm-import alert alert-warning"><i className="fa fa-spinner fa-pulse"></i> Importing...</p>
case 'in-progress':
messageSection = (
<p className='confirm-import alert alert-warning'><i className='fa fa-spinner fa-pulse'></i> Importing...</p>
);
break;
case 'done':
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>
case 'done':
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>
);
break;
case 'fail':
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>
case 'fail':
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>
);
break;
}
@@ -62,10 +77,22 @@ module.exports = React.createClass({
return (
<div>
<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'><i className='modal-back'></i>Import</h4>
<button type='button'
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 ref='wrapper' className='user-settings'>
<div
ref='wrapper'
className='user-settings'
>
<h3 className='tab-header'>Import</h3>
<div className='divider-dark first'/>
{uploadSection}
@@ -75,4 +102,4 @@ module.exports = React.createClass({
</div>
);
}
});
}

View File

@@ -2,77 +2,131 @@
// See License.txt for license information.
var UserStore = require('../stores/user_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
var MemberListTeam = require('./member_list_team.jsx');
var Client = require('../utils/client.jsx');
var utils = require('../utils/utils.jsx');
function getStateFromStores() {
var users = UserStore.getProfiles();
var member_list = [];
for (var id in users) member_list.push(users[id]);
var memberList = [];
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 {
member_list: member_list
member_list: memberList
};
}
module.exports = React.createClass({
componentDidMount: function() {
UserStore.addChangeListener(this._onChange);
export default class TeamMembers extends React.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.state = getStateFromStores();
}
componentDidMount() {
UserStore.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('hidden.bs.modal', function show() {
self.setState({render_members: false});
});
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
self.setState({ render_members: true });
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function hide() {
self.setState({render_members: true});
});
},
componentWillUnmount: function() {
UserStore.removeChangeListener(this._onChange);
},
_onChange: function() {
}
componentWillUnmount() {
UserStore.removeChangeListener(this.onChange);
}
onChange() {
var newState = getStateFromStores();
if (!utils.areStatesEqual(newState, this.state)) {
this.setState(newState);
}
},
getInitialState: function() {
return getStateFromStores();
},
render: function() {
var server_error = this.state.server_error ? <label className='has-error control-label'>{this.state.server_error}</label> : null;
}
render() {
var serverError = 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 (
<div className="modal fade" ref="modal" id="team_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" data-reactid=".5.0.0.0.0"><span aria-hidden="true" data-reactid=".5.0.0.0.0.0">×</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">
{ this.state.render_members ? <MemberListTeam users={this.state.member_list} /> : "" }
<div
className='modal fade'
ref='modal'
id='team_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'
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>
{ server_error }
</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>
);
}
});
}
TeamMembers.propTypes = {
teamDisplayName: React.PropTypes.string
};

View File

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