Merge pull request #553 from mattermost/MM-2065

MM-2065  Cosmetic refactoring for ESLint
Этот коммит содержится в:
Corey Hulen
2015-09-02 12:41:47 -07:00
родитель b31be3afa7 63bc243b17
Коммит c7cafb6ac7
21 изменённых файлов: 1246 добавлений и 608 удалений

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

@@ -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>
);
}
});
}

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

@@ -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';

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

@@ -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
};

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

@@ -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
};

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

@@ -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>
);
}
});
}

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

@@ -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
};

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

@@ -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
};

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

@@ -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>
);
}
});
}

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

@@ -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: "" });
this.timer = setInterval(function myTimer() {
if ((new Date().getTime() - this.lastTime) > 8000) {
this.setState({text: ''});
}
}, 3000);
}.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
};

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

@@ -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}

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

@@ -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/>;
}
});
}

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

@@ -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

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

@@ -1,181 +1,109 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var PostStore = require('../stores/post_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var UserProfile = require( './user_profile.jsx' );
var SearchBox =require('./search_bar.jsx');
var SearchBox = require('./search_bar.jsx');
var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
var RhsHeaderSearch = React.createClass({
handleClose: function(e) {
e.preventDefault();
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_SEARCH,
results: null
});
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_SEARCH_TERM,
term: null,
do_search: false,
is_mention_search: false
});
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_POST_SELECTED,
results: null
});
},
render: function() {
var title = this.props.isMentionSearch ? "Recent Mentions" : "Search Results";
return (
<div className="sidebar--right__header">
<span className="sidebar--right__title">{title}</span>
<button type="button" className="sidebar--right__close" aria-label="Close" title="Close" onClick={this.handleClose}></button>
</div>
);
}
});
var SearchItem = React.createClass({
handleClick: function(e) {
e.preventDefault();
var self = this;
client.getPost(
this.props.post.channel_id,
this.props.post.id,
function(data) {
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_POST_SELECTED,
post_list: data,
from_search: PostStore.getSearchTerm()
});
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_SEARCH,
results: null,
is_mention_search: self.props.isMentionSearch
});
},
function(err) {
AsyncClient.dispatchError(err, "getPost");
}
);
var postChannel = ChannelStore.get(this.props.post.channel_id);
var teammate = postChannel.type === 'D' ? utils.getDirectTeammate(this.props.post.channel_id).username : "";
utils.switchChannel(postChannel, teammate);
},
render: function() {
var message = utils.textToJsx(this.props.post.message, {searchTerm: this.props.term, noMentionHighlight: !this.props.isMentionSearch});
var channelName = "";
var channel = ChannelStore.get(this.props.post.channel_id);
var timestamp = UserStore.getCurrentUser().update_at;
if (channel) {
channelName = (channel.type === 'D') ? "Private Message" : channel.display_name;
}
return (
<div className="search-item-container post" onClick={this.handleClick}>
<div className="search-channel__name">{ channelName }</div>
<div className="post-profile-img__container">
<img className="post-profile-img" src={"/api/v1/users/" + this.props.post.user_id + "/image?time=" + timestamp} height="36" width="36" />
</div>
<div className="post__content">
<ul className="post-header">
<li className="post-header-col"><strong><UserProfile userId={this.props.post.user_id} /></strong></li>
<li className="post-header-col">
<time className="search-item-time">
{ utils.displayDate(this.props.post.create_at) + ' ' + utils.displayTime(this.props.post.create_at) }
</time>
</li>
</ul>
<div className="search-item-snippet"><span>{message}</span></div>
</div>
</div>
);
}
});
var SearchResultsHeader = require('./search_results_header.jsx');
var SearchResultsItem = require('./search_results_item.jsx');
function getStateFromStores() {
return { results: PostStore.getSearchResults() };
return {results: PostStore.getSearchResults()};
}
module.exports = React.createClass({
displayName: 'SearchResults',
componentDidMount: function() {
PostStore.addSearchChangeListener(this._onChange);
export default class SearchResults extends React.Component {
constructor(props) {
super(props);
this.mounted = false;
this.onChange = this.onChange.bind(this);
this.resize = this.resize.bind(this);
this.state = getStateFromStores();
}
componentDidMount() {
this.mounted = true;
PostStore.addSearchChangeListener(this.onChange);
this.resize();
var self = this;
$(window).resize(function(){
$(window).resize(function resize() {
self.resize();
});
},
componentDidUpdate: function() {
}
componentDidUpdate() {
this.resize();
},
componentWillUnmount: function() {
PostStore.removeSearchChangeListener(this._onChange);
},
_onChange: function() {
if (this.isMounted()) {
}
componentWillUnmount() {
PostStore.removeSearchChangeListener(this.onChange);
this.mounted = false;
}
onChange() {
if (this.mounted) {
var newState = getStateFromStores();
if (!utils.areStatesEqual(newState, this.state)) {
this.setState(newState);
}
}
},
getInitialState: function() {
return getStateFromStores();
},
resize: function() {
var height = $(window).height() - $('#error_bar').outerHeight() - 100;
$("#search-items-container").css("height", height + "px");
$("#search-items-container").scrollTop(0);
$("#search-items-container").perfectScrollbar();
},
render: function() {
}
resize() {
var height = $(window).height() - $('#error_bar').outerHeight() - 100;
$('#search-items-container').css('height', height + 'px');
$('#search-items-container').scrollTop(0);
$('#search-items-container').perfectScrollbar();
}
render() {
var results = this.state.results;
var currentId = UserStore.getCurrentId();
var searchForm = currentId ? <SearchBox /> : null;
var searchForm = null;
if (currentId) {
searchForm = <SearchBox />;
}
var noResults = (!results || !results.order || !results.order.length);
var searchTerm = PostStore.getSearchTerm();
var ctls = null;
if (noResults) {
ctls = <div className='sidebar--right__subheader'>No results</div>;
} else {
ctls = results.order.map(function mymap(id) {
var post = results.posts[id];
return (
<SearchResultsItem
key={post.id}
post={post}
term={searchTerm}
isMentionSearch={this.props.isMentionSearch}
/>
);
}, this);
}
return (
<div className="sidebar--right__content">
<div className="search-bar__container sidebar--right__search-header">{searchForm}</div>
<div className="sidebar-right__body">
<RhsHeaderSearch isMentionSearch={this.props.isMentionSearch} />
<div id="search-items-container" className="search-items-container">
{ noResults ? <div className="sidebar--right__subheader">No results</div>
: results.order.map(function(id) {
var post = results.posts[id];
return <SearchItem key={post.id} post={post} term={searchTerm} isMentionSearch={this.props.isMentionSearch} />
}, this)
}
<div className='sidebar--right__content'>
<div className='search-bar__container sidebar--right__search-header'>{searchForm}</div>
<div className='sidebar-right__body'>
<SearchResultsHeader isMentionSearch={this.props.isMentionSearch} />
<div
id='search-items-container'
className='search-items-container'
>
{ctls}
</div>
</div>
</div>
);
}
});
}
SearchResults.propTypes = {
isMentionSearch: React.PropTypes.bool
};

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

@@ -0,0 +1,61 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
export default class SearchResultsHeader extends React.Component {
constructor(props) {
super(props);
this.handleClose = this.handleClose.bind(this);
}
handleClose(e) {
e.preventDefault();
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_SEARCH,
results: null
});
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_SEARCH_TERM,
term: null,
do_search: false,
is_mention_search: false
});
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_POST_SELECTED,
results: null
});
}
render() {
var title = 'Search Results';
if (this.props.isMentionSearch) {
title = 'Recent Mentions';
}
return (
<div className='sidebar--right__header'>
<span className='sidebar--right__title'>{title}</span>
<button
type='button'
className='sidebar--right__close'
aria-label='Close'
title='Close'
onClick={this.handleClose}
>
</button>
</div>
);
}
}
SearchResultsHeader.propTypes = {
isMentionSearch: React.PropTypes.bool
};

105
web/react/components/search_results_item.jsx Обычный файл
Просмотреть файл

@@ -0,0 +1,105 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var PostStore = require('../stores/post_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var UserProfile = require('./user_profile.jsx');
var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
export default class SearchResultsItem extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
e.preventDefault();
var self = this;
client.getPost(
this.props.post.channel_id,
this.props.post.id,
function success(data) {
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_POST_SELECTED,
post_list: data,
from_search: PostStore.getSearchTerm()
});
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_SEARCH,
results: null,
is_mention_search: self.props.isMentionSearch
});
},
function success(err) {
AsyncClient.dispatchError(err, 'getPost');
}
);
var postChannel = ChannelStore.get(this.props.post.channel_id);
var teammate = '';
if (postChannel.type === 'D') {
teammate = utils.getDirectTeammate(this.props.post.channel_id).username;
}
utils.switchChannel(postChannel, teammate);
}
render() {
var message = utils.textToJsx(this.props.post.message, {searchTerm: this.props.term, noMentionHighlight: !this.props.isMentionSearch});
var channelName = '';
var channel = ChannelStore.get(this.props.post.channel_id);
var timestamp = UserStore.getCurrentUser().update_at;
if (channel) {
channelName = channel.display_name;
if (channel.type === 'D') {
channelName = 'Private Message';
}
}
return (
<div
className='search-item-container post'
onClick={this.handleClick}
>
<div className='search-channel__name'>{channelName}</div>
<div className='post-profile-img__container'>
<img
className='post-profile-img'
src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp}
height='36'
width='36'
/>
</div>
<div className='post__content'>
<ul className='post-header'>
<li className='post-header-col'><strong><UserProfile userId={this.props.post.user_id} /></strong></li>
<li className='post-header-col'>
<time className='search-item-time'>
{utils.displayDate(this.props.post.create_at) + ' ' + utils.displayTime(this.props.post.create_at)}
</time>
</li>
</ul>
<div className='search-item-snippet'><span>{message}</span></div>
</div>
</div>
);
}
}
SearchResultsItem.propTypes = {
post: React.PropTypes.object,
isMentionSearch: React.PropTypes.bool,
term: React.PropTypes.string
};

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

@@ -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
};

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

@@ -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
};

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

@@ -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
};

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

@@ -4,22 +4,34 @@
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/>
@@ -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>
);
}
});
}

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

@@ -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
};

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

@@ -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
};