summaryrefslogtreecommitdiffstats
path: root/web/react/components
diff options
context:
space:
mode:
Diffstat (limited to 'web/react/components')
-rw-r--r--web/react/components/command_list.jsx6
-rw-r--r--web/react/components/create_comment.jsx7
-rw-r--r--web/react/components/create_post.jsx7
-rw-r--r--web/react/components/edit_post_modal.jsx6
-rw-r--r--web/react/components/file_attachment.jsx19
-rw-r--r--web/react/components/file_attachment_list.jsx2
-rw-r--r--web/react/components/login.jsx72
-rw-r--r--web/react/components/post_list.jsx42
-rw-r--r--web/react/components/setting_item_max.jsx2
-rw-r--r--web/react/components/sidebar_header.jsx2
-rw-r--r--web/react/components/sidebar_right_menu.jsx7
-rw-r--r--web/react/components/signup_team.jsx106
-rw-r--r--web/react/components/signup_user_complete.jsx56
-rw-r--r--web/react/components/signup_user_oauth.jsx87
-rw-r--r--web/react/components/team_signup_choose_auth.jsx70
-rw-r--r--web/react/components/team_signup_password_page.jsx38
-rw-r--r--web/react/components/team_signup_with_email.jsx82
-rw-r--r--web/react/components/team_signup_with_sso.jsx125
-rw-r--r--web/react/components/user_settings_notifications.jsx16
19 files changed, 499 insertions, 253 deletions
diff --git a/web/react/components/command_list.jsx b/web/react/components/command_list.jsx
index 5efe98dc6..27264ff6e 100644
--- a/web/react/components/command_list.jsx
+++ b/web/react/components/command_list.jsx
@@ -48,15 +48,15 @@ module.exports = React.createClass({
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="pull-left"><strong>{ this.state.suggestions[i].suggestion }</strong></div>
- <div className="command-desc pull-right">{ this.state.suggestions[i].description }</div>
+ <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*37)+2}}>
+ <div ref="mentionlist" className="command-box" style={{height:(this.state.suggestions.length*56)+2}}>
{ suggestions }
</div>
);
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index f6e34fda9..c2b7e222f 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -104,17 +104,14 @@ module.exports = React.createClass({
this.lastTime = t;
}
},
- handleUserInput: function(message) {
- var messageText = utils.truncateText(message);
- var newPostError = utils.checkMessageLengthError(messageText, this.state.postError, 'Comment length cannot exceed ' + Constants.MAX_POST_LEN + ' characters');
-
+ handleUserInput: function(messageText) {
var draft = PostStore.getCommentDraft(this.props.rootId);
draft.message = messageText;
PostStore.storeCommentDraft(this.props.rootId, draft);
$('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
$('.post-right__scroll').perfectScrollbar('update');
- this.setState({messageText: messageText, postError: newPostError});
+ this.setState({messageText: messageText});
},
handleUploadStart: function(clientIds, channelId) {
var draft = PostStore.getCommentDraft(this.props.rootId);
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index 73210c855..b9142223f 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -130,12 +130,9 @@ module.exports = React.createClass({
this.lastTime = t;
}
},
- handleUserInput: function(message) {
- var messageText = utils.truncateText(message);
- var newPostError = utils.checkMessageLengthError(messageText, this.state.postError, 'Message length cannot exceed ' + Constants.MAX_POST_LEN + ' characters');
-
+ handleUserInput: function(messageText) {
this.resizePostHolder();
- this.setState({messageText: messageText, postError: newPostError});
+ this.setState({messageText: messageText});
var draft = PostStore.getCurrentDraft();
draft['message'] = messageText;
diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx
index df692e1bb..1c5a1ed5e 100644
--- a/web/react/components/edit_post_modal.jsx
+++ b/web/react/components/edit_post_modal.jsx
@@ -38,10 +38,8 @@ module.exports = React.createClass({
$("#edit_post").modal('hide');
$(this.state.refocusId).focus();
},
- handleEditInput: function(editText) {
- var editMessage = utils.truncateText(editText);
- var newError = utils.checkMessageLengthError(editMessage, this.state.error, 'New message length cannot exceed ' + Constants.MAX_POST_LEN + ' characters');
- this.setState({editText: editMessage, error: newError});
+ handleEditInput: function(editMessage) {
+ this.setState({editText: editMessage});
},
handleEditKeyPress: function(e) {
if (e.which == 13 && !e.shiftKey && !e.altKey) {
diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx
index ab550d500..45e6c5e28 100644
--- a/web/react/components/file_attachment.jsx
+++ b/web/react/components/file_attachment.jsx
@@ -10,7 +10,7 @@ module.exports = React.createClass({
canSetState: false,
propTypes: {
// a list of file pathes displayed by the parent FileAttachmentList
- filenames: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
+ filename: React.PropTypes.string.isRequired,
// the index of this attachment preview in the parent FileAttachmentList
index: React.PropTypes.number.isRequired,
// the identifier of the modal dialog used to preview files
@@ -22,9 +22,17 @@ module.exports = React.createClass({
return {fileSize: -1};
},
componentDidMount: function() {
+ this.loadFiles();
+ },
+ componentDidUpdate: function(prevProps) {
+ if (this.props.filename !== prevProps.filename) {
+ this.loadFiles();
+ }
+ },
+ loadFiles: function() {
this.canSetState = true;
- var filename = this.props.filenames[this.props.index];
+ var filename = this.props.filename;
if (filename) {
var fileInfo = utils.splitFileLocation(filename);
@@ -71,6 +79,10 @@ module.exports = React.createClass({
this.canSetState = false;
},
shouldComponentUpdate: function(nextProps, nextState) {
+ if (!utils.areStatesEqual(nextProps, this.props)) {
+ return true;
+ }
+
// the only time this object should update is when it receives an updated file size which we can usually handle without re-rendering
if (nextState.fileSize != this.state.fileSize) {
if (this.refs.fileSize) {
@@ -87,8 +99,7 @@ module.exports = React.createClass({
}
},
render: function() {
- var filenames = this.props.filenames;
- var filename = filenames[this.props.index];
+ var filename = this.props.filename;
var fileInfo = utils.splitFileLocation(filename);
var type = utils.getFileType(fileInfo.ext);
diff --git a/web/react/components/file_attachment_list.jsx b/web/react/components/file_attachment_list.jsx
index b92442957..df4424d03 100644
--- a/web/react/components/file_attachment_list.jsx
+++ b/web/react/components/file_attachment_list.jsx
@@ -26,7 +26,7 @@ module.exports = React.createClass({
var postFiles = [];
for (var i = 0; i < filenames.length && i < Constants.MAX_DISPLAY_FILES; i++) {
- postFiles.push(<FileAttachment key={i} filenames={filenames} index={i} modalId={modalId} handleImageClick={this.handleImageClick} />);
+ postFiles.push(<FileAttachment key={i} filename={filenames[i]} index={i} modalId={modalId} handleImageClick={this.handleImageClick} />);
}
return (
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
index 678a2ff87..0f3aa42db 100644
--- a/web/react/components/login.jsx
+++ b/web/react/components/login.jsx
@@ -10,7 +10,9 @@ var Constants = require('../utils/constants.jsx');
export default class Login extends React.Component {
constructor(props) {
super(props);
+
this.handleSubmit = this.handleSubmit.bind(this);
+
this.state = {};
}
handleSubmit(e) {
@@ -96,19 +98,16 @@ export default class Login extends React.Component {
var authServices = JSON.parse(this.props.authServices);
var loginMessage = [];
- if (authServices.indexOf(Constants.GITLAB_SERVICE) >= 0) {
+ if (authServices.indexOf(Constants.GITLAB_SERVICE) !== -1) {
loginMessage.push(
- <div className='form-group form-group--small'>
- <span><a href={'/' + teamName + '/login/gitlab'}>{'Log in with GitLab'}</a></span>
- </div>
- );
- }
- if (authServices.indexOf(Constants.GOOGLE_SERVICE) >= 0) {
- loginMessage.push(
- <div className='form-group form-group--small'>
- <span><a href={'/' + teamName + '/login/google'}>{'Log in with Google'}</a></span>
- </div>
- );
+ <a
+ className='btn btn-custom-login gitlab'
+ href={'/' + teamName + '/login/gitlab'}
+ >
+ <span className='icon' />
+ <span>with GitLab</span>
+ </a>
+ );
}
var errorClass = '';
@@ -116,15 +115,10 @@ export default class Login extends React.Component {
errorClass = ' has-error';
}
- return (
- <div className='signup-team__container'>
- <h5 className='margin--less'>Sign in to:</h5>
- <h2 className='signup-team__name'>{teamDisplayName}</h2>
- <h2 className='signup-team__subdomain'>on {config.SiteName}</h2>
- <form onSubmit={this.handleSubmit}>
- <div className={'form-group' + errorClass}>
- {serverError}
- </div>
+ var emailSignup;
+ if (authServices.indexOf(Constants.EMAIL_SERVICE) !== -1) {
+ emailSignup = (
+ <div>
<div className={'form-group' + errorClass}>
<input
autoFocus={focusEmail}
@@ -154,13 +148,43 @@ export default class Login extends React.Component {
Sign in
</button>
</div>
+ </div>
+ );
+ }
+
+ var forgotPassword;
+ if (loginMessage.length > 0 && emailSignup) {
+ loginMessage = (
+ <div>
{loginMessage}
+ <div className='or__container'>
+ <span>or</span>
+ </div>
+ </div>
+ );
+
+ forgotPassword = (
+ <div className='form-group'>
+ <a href={'/' + teamName + '/reset_password'}>I forgot my password</a>
+ </div>
+ );
+ }
+
+ return (
+ <div className='signup-team__container'>
+ <h5 className='margin--less'>Sign in to:</h5>
+ <h2 className='signup-team__name'>{teamDisplayName}</h2>
+ <h2 className='signup-team__subdomain'>on {config.SiteName}</h2>
+ <form onSubmit={this.handleSubmit}>
+ <div className={'form-group' + errorClass}>
+ {serverError}
+ </div>
+ {loginMessage}
+ {emailSignup}
<div className='form-group margin--extra form-group--small'>
<span><a href='/find_team'>{'Find other ' + strings.TeamPlural}</a></span>
</div>
- <div className='form-group'>
- <a href={'/' + teamName + '/reset_password'}>I forgot my password</a>
- </div>
+ {forgotPassword}
<div className='margin--extra'>
<span>{'Want to create your own ' + strings.Team + '? '}
<a
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx
index 865a22dbd..c1e6e490d 100644
--- a/web/react/components/post_list.jsx
+++ b/web/react/components/post_list.jsx
@@ -31,9 +31,11 @@ export default class PostList extends React.Component {
this.onSocketChange = this.onSocketChange.bind(this);
this.createChannelIntroMessage = this.createChannelIntroMessage.bind(this);
this.loadMorePosts = this.loadMorePosts.bind(this);
+ this.loadFirstPosts = this.loadFirstPosts.bind(this);
this.state = this.getStateFromStores();
this.state.numToDisplay = Constants.POST_CHUNK_SIZE;
+ this.state.isFirstLoadComplete = false;
}
getStateFromStores() {
var channel = ChannelStore.getCurrent();
@@ -157,7 +159,10 @@ export default class PostList extends React.Component {
});
this.scrollToBottom();
- setTimeout(this.scrollToBottom, 100);
+
+ if (this.state.channel.id != null) {
+ this.loadFirstPosts(this.state.channel.id);
+ }
}
componentDidUpdate(prevProps, prevState) {
$('.post-list__content div .post').removeClass('post--last');
@@ -229,9 +234,26 @@ export default class PostList extends React.Component {
postHolder.removeClass('hide-scroll');
}
}
+ loadFirstPosts(id) {
+ Client.getPosts(
+ id,
+ PostStore.getLatestUpdate(id),
+ function success() {
+ this.setState({isFirstLoadComplete: true});
+ }.bind(this),
+ function fail() {
+ this.setState({isFirstLoadComplete: true});
+ }.bind(this)
+ );
+ }
onChange() {
var newState = this.getStateFromStores();
+ // Special case where the channel wasn't yet set in componentDidMount
+ if (!this.state.isFirstLoadComplete && this.state.channel.id == null && newState.channel.id != null) {
+ this.loadFirstPosts(newState.channel.id);
+ }
+
if (!utils.areStatesEqual(newState, this.state)) {
if (this.state.channel.id !== newState.channel.id) {
PostStore.clearUnseenDeletedPosts(this.state.channel.id);
@@ -379,6 +401,14 @@ export default class PostList extends React.Component {
>
<i className='fa fa-pencil'></i>Set a description
</a>
+ <a
+ className='intro-links'
+ href='#'
+ data-toggle='modal'
+ data-target='#channel_invite'
+ >
+ <i className='fa fa-user-plus'></i>Invite others to this channel
+ </a>
</div>
);
}
@@ -511,9 +541,15 @@ export default class PostList extends React.Component {
if (post.user_id !== userId && post.create_at > this.state.lastViewed && !renderedLastViewed) {
renderedLastViewed = true;
+
+ // Temporary fix to solve ie10/11 rendering issue
+ let newSeparatorId = '';
+ if (!utils.isBrowserIE()) {
+ newSeparatorId = 'new_message';
+ }
postCtls.push(
<div
- id='new_message'
+ id={newSeparatorId}
key='unviewed'
className='new-separator'
>
@@ -605,7 +641,7 @@ export default class PostList extends React.Component {
}
var postCtls = [];
- if (posts) {
+ if (posts && this.state.isFirstLoadComplete) {
postCtls = this.createPosts(posts, order);
} else {
postCtls.push(<LoadingScreen position='absolute' />);
diff --git a/web/react/components/setting_item_max.jsx b/web/react/components/setting_item_max.jsx
index 1599041b0..b978cdb0c 100644
--- a/web/react/components/setting_item_max.jsx
+++ b/web/react/components/setting_item_max.jsx
@@ -5,6 +5,7 @@ module.exports = React.createClass({
render: function() {
var clientError = this.props.client_error ? <div className='form-group'><label className='col-sm-12 has-error'>{ this.props.client_error }</label></div> : null;
var server_error = this.props.server_error ? <div className='form-group'><label className='col-sm-12 has-error'>{ this.props.server_error }</label></div> : null;
+ var extraInfo = this.props.extraInfo ? this.props.extraInfo : null;
var inputs = this.props.inputs;
@@ -15,6 +16,7 @@ module.exports = React.createClass({
<ul className="setting-list">
<li className="setting-list-item">
{inputs}
+ {extraInfo}
</li>
<li className="setting-list-item">
<hr />
diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
index d5d16816f..af65b7e1d 100644
--- a/web/react/components/sidebar_header.jsx
+++ b/web/react/components/sidebar_header.jsx
@@ -85,7 +85,7 @@ var NavbarDropdown = React.createClass({
}
});
}
- teams.push(<li key='newTeam_li'><a key='newTeam_a' href={utils.getWindowLocationOrigin() + '/signup_team' }>Create a New Team</a></li>);
+ teams.push(<li key='newTeam_li'><a key='newTeam_a' target="_blank" href={utils.getWindowLocationOrigin() + '/signup_team' }>Create a New Team</a></li>);
return (
<ul className='nav navbar-nav navbar-right'>
diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx
index d221ca840..615bc4ef2 100644
--- a/web/react/components/sidebar_right_menu.jsx
+++ b/web/react/components/sidebar_right_menu.jsx
@@ -15,7 +15,6 @@ module.exports = React.createClass({
var inviteLink = '';
var teamSettingsLink = '';
var manageLink = '';
- var renameLink = '';
var currentUser = UserStore.getCurrentUser();
var isAdmin = false;
@@ -48,11 +47,6 @@ module.exports = React.createClass({
<a href='#' data-toggle='modal' data-target='#team_members'><i className='glyphicon glyphicon-wrench'></i>Manage Team</a>
</li>
);
- renameLink = (
- <li>
- <a href='#' data-toggle='modal' data-target='#rename_team_link'><i className='glyphicon glyphicon-pencil'></i>Rename</a>
- </li>
- );
}
var siteName = '';
@@ -77,7 +71,6 @@ module.exports = React.createClass({
{inviteLink}
{teamLink}
{manageLink}
- {renameLink}
<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>
diff --git a/web/react/components/signup_team.jsx b/web/react/components/signup_team.jsx
index edd48e0b9..13640b1e5 100644
--- a/web/react/components/signup_team.jsx
+++ b/web/react/components/signup_team.jsx
@@ -1,69 +1,49 @@
// 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) {
- e.preventDefault();
- var team = {};
- var state = { server_error: "" };
-
- team.email = this.refs.email.getDOMNode().value.trim().toLowerCase();
- if (!team.email || !utils.isEmail(team.email)) {
- state.email_error = "Please enter a valid email address";
- state.inValid = true;
+var ChoosePage = require('./team_signup_choose_auth.jsx');
+var EmailSignUpPage = require('./team_signup_with_email.jsx');
+var SSOSignupPage = require('./team_signup_with_sso.jsx');
+var Constants = require('../utils/constants.jsx');
+
+export default class TeamSignUp extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.updatePage = this.updatePage.bind(this);
+
+ if (props.services.length === 1) {
+ if (props.services[0] === Constants.EMAIL_SERVICE) {
+ this.state = {page: 'email', service: ''};
+ } else {
+ this.state = {page: 'service', service: props.services[0]};
+ }
+ } else {
+ this.state = {page: 'choose', service: ''};
}
- else {
- state.email_error = "";
- }
-
- if (state.inValid) {
- this.setState(state);
- return;
+ }
+ updatePage(page, service) {
+ this.setState({page: page, service: service});
+ }
+ render() {
+ if (this.state.page === 'email') {
+ return <EmailSignUpPage />;
+ } else if (this.state.page === 'service' && this.state.service !== '') {
+ return <SSOSignupPage service={this.state.service} />;
+ } else {
+ return (
+ <ChoosePage
+ services={this.props.services}
+ updatePage={this.updatePage}
+ />
+ );
}
-
- client.signupTeam(team.email,
- function(data) {
- if (data["follow_link"]) {
- window.location.href = data["follow_link"];
- }
- else {
- window.location.href = "/signup_team_confirm/?email=" + encodeURIComponent(team.email);
- }
- }.bind(this),
- function(err) {
- state.server_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;
- var server_error = this.state.server_error ? <div className={ "form-group has-error" }><label className='control-label'>{ this.state.server_error }</label></div> : null;
-
- return (
- <form role="form" onSubmit={this.handleSubmit}>
- <div className={ email_error ? "form-group has-error" : "form-group" }>
- <input autoFocus={true} type="email" ref="email" className="form-control" placeholder="Email Address" maxLength="128" />
- { email_error }
- </div>
- { server_error }
- <div className="form-group">
- <button className="btn btn-md btn-primary" type="submit">Sign up</button>
- </div>
- <div className="form-group margin--extra-2x">
- <span><a href="/find_team">{"Find my " + strings.Team}</a></span>
- </div>
- </form>
- );
}
-});
-
-
+}
+
+TeamSignUp.defaultProps = {
+ services: []
+};
+TeamSignUp.propTypes = {
+ services: React.PropTypes.array
+};
diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx
index 0393e0413..2080cc191 100644
--- a/web/react/components/signup_user_complete.jsx
+++ b/web/react/components/signup_user_complete.jsx
@@ -162,16 +162,35 @@ module.exports = React.createClass({
);
}
- if (authServices.indexOf(Constants.GOOGLE_SERVICE) >= 0) {
- signupMessage.push(
- <a className='btn btn-custom-login google' href={'/' + this.props.teamName + '/signup/google' + window.location.search}>
- <span className='icon' />
- <span>with Google</span>
- </a>
- );
+ var emailSignup;
+ if (authServices.indexOf(Constants.EMAIL_SERVICE) !== -1) {
+ emailSignup = (
+ <div>
+ <div className='inner__content'>
+ {email}
+ {yourEmailIs}
+ <div className='margin--extra'>
+ <h5><strong>Choose your username</strong></h5>
+ <div className={nameDivStyle}>
+ <input type='text' ref='name' className='form-control' placeholder='' maxLength='128' />
+ {nameError}
+ <p className='form__hint'>Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'</p>
+ </div>
+ </div>
+ <div className='margin--extra'>
+ <h5><strong>Choose your password</strong></h5>
+ <div className={passwordDivStyle}>
+ <input type='password' ref='password' className='form-control' placeholder='' maxLength='128' />
+ {passwordError}
+ </div>
+ </div>
+ </div>
+ <p className='margin--extra'><button type='submit' onClick={this.handleSubmit} className='btn-primary btn'>Create Account</button></p>
+ </div>
+ );
}
- if (signupMessage.length > 0) {
+ if (signupMessage.length > 0 && emailSignup) {
signupMessage = (
<div>
{signupMessage}
@@ -196,26 +215,7 @@ module.exports = React.createClass({
<h2 className='signup-team__subdomain'>on {config.SiteName}</h2>
<h4 className='color--light'>Let's create your account</h4>
{signupMessage}
- <div className='inner__content'>
- {email}
- {yourEmailIs}
- <div className='margin--extra'>
- <h5><strong>Choose your username</strong></h5>
- <div className={nameDivStyle}>
- <input type='text' ref='name' className='form-control' placeholder='' maxLength='128' />
- {nameError}
- <p className='form__hint'>Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'</p>
- </div>
- </div>
- <div className='margin--extra'>
- <h5><strong>Choose your password</strong></h5>
- <div className={passwordDivStyle}>
- <input type='password' ref='password' className='form-control' placeholder='' maxLength='128' />
- {passwordError}
- </div>
- </div>
- </div>
- <p className='margin--extra'><button type='submit' onClick={this.handleSubmit} className='btn-primary btn'>Create Account</button></p>
+ {emailSignup}
{serverError}
{termsDisclaimer}
</form>
diff --git a/web/react/components/signup_user_oauth.jsx b/web/react/components/signup_user_oauth.jsx
deleted file mode 100644
index 8b2800bde..000000000
--- a/web/react/components/signup_user_oauth.jsx
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-
-var utils = require('../utils/utils.jsx');
-var client = require('../utils/client.jsx');
-var UserStore = require('../stores/user_store.jsx');
-var BrowserStore = require('../stores/browser_store.jsx');
-
-module.exports = React.createClass({
- handleSubmit: function(e) {
- e.preventDefault();
-
- if (!this.state.user.username) {
- this.setState({name_error: "This field is required", email_error: "", password_error: "", server_error: ""});
- return;
- }
-
- var username_error = utils.isValidUsername(this.state.user.username);
- if (username_error === "Cannot use a reserved word as a username.") {
- this.setState({name_error: "This username is reserved, please choose a new one.", email_error: "", password_error: "", server_error: ""});
- return;
- } else if (username_error) {
- this.setState({name_error: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'.", email_error: "", password_error: "", server_error: ""});
- return;
- }
-
- this.setState({name_error: "", server_error: ""});
-
- this.state.user.allow_marketing = this.refs.email_service.getDOMNode().checked;
-
- var user = this.state.user;
- client.createUser(user, "", "",
- function(data) {
- client.track('signup', 'signup_user_oauth_02');
- UserStore.setCurrentUser(data);
- UserStore.setLastEmail(data.email);
-
- window.location.href = '/' + this.props.teamName + '/login/' + user.auth_service + '?login_hint=' + user.email;
- }.bind(this),
- function(err) {
- this.state.server_error = err.message;
- this.setState(this.state);
- }.bind(this)
- );
- },
- handleChange: function() {
- var user = this.state.user;
- user.username = this.refs.name.getDOMNode().value;
- this.setState({ user: user });
- },
- getInitialState: function() {
- var user = JSON.parse(this.props.user);
- return { user: user };
- },
- render: function() {
-
- client.track('signup', 'signup_user_oauth_01');
-
- var name_error = this.state.name_error ? <label className='control-label'>{ this.state.name_error }</label> : null;
- var server_error = this.state.server_error ? <div className={ "form-group has-error" }><label className='control-label'>{ this.state.server_error }</label></div> : null;
-
- var yourEmailIs = this.state.user.email == "" ? "" : <span>Your email address is <b>{ this.state.user.email }.</b></span>;
-
- return (
- <div>
- <img className="signup-team-logo" src="/static/images/logo.png" />
- <h4>Welcome to { config.SiteName }</h4>
- <p>{"To continue signing up with " + this.state.user.auth_service + ", please register a username."}</p>
- <p>Your username can be made of lowercase letters and numbers.</p>
- <label className="control-label">Username</label>
- <div className={ name_error ? "form-group has-error" : "form-group" }>
- <input type="text" ref="name" className="form-control" placeholder="" maxLength="128" value={this.state.user.username} onChange={this.handleChange} />
- { name_error }
- </div>
- <p>{"Pick something " + strings.Team + "mates will recognize. Your username is how you will appear to others."}</p>
- <p>{ yourEmailIs } You’ll use this address to sign in to {config.SiteName}.</p>
- <div className="checkbox"><label><input type="checkbox" ref="email_service" /> It's ok to send me occassional email with updates about the {config.SiteName} service. </label></div>
- <p><button onClick={this.handleSubmit} className="btn-primary btn">Create Account</button></p>
- { server_error }
- <p>By proceeding to create your account and use { config.SiteName }, you agree to our <a href={ config.TermsLink }>Terms of Service</a> and <a href={ config.PrivacyLink }>Privacy Policy</a>. If you do not agree, you cannot use {config.SiteName}.</p>
- </div>
- );
- }
-});
-
-
diff --git a/web/react/components/team_signup_choose_auth.jsx b/web/react/components/team_signup_choose_auth.jsx
new file mode 100644
index 000000000..92ade5d24
--- /dev/null
+++ b/web/react/components/team_signup_choose_auth.jsx
@@ -0,0 +1,70 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var Constants = require('../utils/constants.jsx');
+
+export default class ChooseAuthPage extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {};
+ }
+ render() {
+ var buttons = [];
+ if (this.props.services.indexOf(Constants.GITLAB_SERVICE) !== -1) {
+ buttons.push(
+ <a
+ className='btn btn-custom-login gitlab btn-full'
+ href='#'
+ onClick={
+ function clickGit(e) {
+ e.preventDefault();
+ this.props.updatePage('service', Constants.GITLAB_SERVICE);
+ }.bind(this)
+ }
+ >
+ <span className='icon' />
+ <span>Create new {strings.Team} with GitLab Account</span>
+ </a>
+ );
+ }
+
+ if (this.props.services.indexOf(Constants.EMAIL_SERVICE) !== -1) {
+ buttons.push(
+ <a
+ className='btn btn-custom-login email btn-full'
+ href='#'
+ onClick={
+ function clickEmail(e) {
+ e.preventDefault();
+ this.props.updatePage('email', '');
+ }.bind(this)
+ }
+ >
+ <span className='fa fa-envelope' />
+ <span>Create new {strings.Team} with email address</span>
+ </a>
+ );
+ }
+
+ if (buttons.length === 0) {
+ buttons = <span>No sign-up methods configured, please contact your system administrator.</span>;
+ }
+
+ return (
+ <div>
+ {buttons}
+ <div className='form-group margin--extra-2x'>
+ <span><a href='/find_team'>{'Find my ' + strings.Team}</a></span>
+ </div>
+ </div>
+ );
+ }
+}
+
+ChooseAuthPage.defaultProps = {
+ services: []
+};
+ChooseAuthPage.propTypes = {
+ services: React.PropTypes.array,
+ updatePage: React.PropTypes.func
+};
diff --git a/web/react/components/team_signup_password_page.jsx b/web/react/components/team_signup_password_page.jsx
index e4f35f100..bbe82a5c2 100644
--- a/web/react/components/team_signup_password_page.jsx
+++ b/web/react/components/team_signup_password_page.jsx
@@ -31,31 +31,35 @@ module.exports = React.createClass({
teamSignup.user.allow_marketing = true;
delete teamSignup.wizard;
- // var ctl = this;
-
client.createTeamFromSignup(teamSignup,
function success() {
client.track('signup', 'signup_team_08_complete');
var props = this.props;
- $('#sign-up-button').button('reset');
- props.state.wizard = 'finished';
- props.updateParent(props.state, true);
+
+ client.loginByEmail(teamSignup.team.name, teamSignup.team.email, teamSignup.user.password,
+ function(data) {
+ UserStore.setLastEmail(teamSignup.team.email);
+ UserStore.setCurrentUser(teamSignup.user);
+ if (this.props.hash > 0) {
+ BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'}));
+ }
- window.location.href = utils.getWindowLocationOrigin() + '/' + props.state.team.name + '/login?email=' + encodeURIComponent(teamSignup.team.email);
+ $('#sign-up-button').button('reset');
+ props.state.wizard = 'finished';
+ props.updateParent(props.state, true);
- // client.loginByEmail(teamSignup.team.domain, teamSignup.team.email, teamSignup.user.password,
- // function(data) {
- // TeamStore.setLastName(teamSignup.team.domain);
- // UserStore.setLastEmail(teamSignup.team.email);
- // UserStore.setCurrentUser(data);
- // window.location.href = '/channels/town-square';
- // }.bind(ctl),
- // function(err) {
- // this.setState({nameError: err.message});
- // }.bind(ctl)
- // );
+ window.location.href = '/';
+ }.bind(this),
+ function(err) {
+ if (err.message === 'Login failed because email address has not been verified') {
+ window.location.href = '/verify_email?email=' + encodeURIComponent(teamSignup.team.email) + '&teamname=' + encodeURIComponent(teamSignup.team.name);
+ } else {
+ this.setState({serverError: err.message});
+ }
+ }.bind(this)
+ );
}.bind(this),
function error(err) {
this.setState({serverError: err.message});
diff --git a/web/react/components/team_signup_with_email.jsx b/web/react/components/team_signup_with_email.jsx
new file mode 100644
index 000000000..c7204880f
--- /dev/null
+++ b/web/react/components/team_signup_with_email.jsx
@@ -0,0 +1,82 @@
+// 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');
+
+export default class EmailSignUpPage extends React.Component {
+ constructor() {
+ super();
+
+ this.handleSubmit = this.handleSubmit.bind(this);
+
+ this.state = {};
+ }
+ handleSubmit(e) {
+ e.preventDefault();
+ var team = {};
+ var state = {serverError: ''};
+
+ team.email = this.refs.email.getDOMNode().value.trim().toLowerCase();
+ if (!team.email || !utils.isEmail(team.email)) {
+ state.emailError = 'Please enter a valid email address';
+ state.inValid = true;
+ } else {
+ state.emailError = '';
+ }
+
+ if (state.inValid) {
+ this.setState(state);
+ return;
+ }
+
+ client.signupTeam(team.email,
+ function success(data) {
+ if (data.follow_link) {
+ window.location.href = data.follow_link;
+ } else {
+ window.location.href = '/signup_team_confirm/?email=' + encodeURIComponent(team.email);
+ }
+ },
+ function fail(err) {
+ state.serverError = err.message;
+ this.setState(state);
+ }.bind(this)
+ );
+ }
+ render() {
+ return (
+ <form
+ role='form'
+ onSubmit={this.handleSubmit}
+ >
+ <div className='form-group'>
+ <input
+ autoFocus={true}
+ type='email'
+ ref='email'
+ className='form-control'
+ placeholder='Email Address'
+ maxLength='128'
+ />
+ </div>
+ <div className='form-group'>
+ <button
+ className='btn btn-md btn-primary'
+ type='submit'
+ >
+ Sign up
+ </button>
+ </div>
+ <div className='form-group margin--extra-2x'>
+ <span><a href='/find_team'>{'Find my ' + strings.Team}</a></span>
+ </div>
+ </form>
+ );
+ }
+}
+
+EmailSignUpPage.defaultProps = {
+};
+EmailSignUpPage.propTypes = {
+};
diff --git a/web/react/components/team_signup_with_sso.jsx b/web/react/components/team_signup_with_sso.jsx
new file mode 100644
index 000000000..6cb62efc7
--- /dev/null
+++ b/web/react/components/team_signup_with_sso.jsx
@@ -0,0 +1,125 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var utils = require('../utils/utils.jsx');
+var client = require('../utils/client.jsx');
+var Constants = require('../utils/constants.jsx');
+
+export default class SSOSignUpPage extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.nameChange = this.nameChange.bind(this);
+
+ this.state = {name: ''};
+ }
+ handleSubmit(e) {
+ e.preventDefault();
+ var team = {};
+ var state = this.state;
+ state.nameError = null;
+ state.serverError = null;
+
+ team.display_name = this.state.name;
+
+ if (team.display_name.length <= 3) {
+ return;
+ }
+
+ if (!team.display_name) {
+ state.nameError = 'Please enter a team name';
+ this.setState(state);
+ return;
+ }
+
+ team.name = utils.cleanUpUrlable(team.display_name);
+ team.type = 'O';
+
+ client.createTeamWithSSO(team,
+ this.props.service,
+ function success(data) {
+ if (data.follow_link) {
+ window.location.href = data.follow_link;
+ } else {
+ window.location.href = '/';
+ }
+ },
+ function fail(err) {
+ state.serverError = err.message;
+ this.setState(state);
+ }.bind(this)
+ );
+ }
+ nameChange() {
+ this.setState({name: this.refs.teamname.getDOMNode().value.trim()});
+ }
+ render() {
+ var nameError = null;
+ var nameDivClass = 'form-group';
+ if (this.state.nameError) {
+ nameError = <label className='control-label'>{this.state.nameError}</label>;
+ nameDivClass += ' has-error';
+ }
+
+ var serverError = null;
+ if (this.state.serverError) {
+ serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
+ }
+
+ var disabled = false;
+ if (this.state.name.length <= 3) {
+ disabled = true;
+ }
+
+ var button = null;
+
+ if (this.props.service === Constants.GITLAB_SERVICE) {
+ button = (
+ <a
+ className='btn btn-custom-login gitlab btn-full'
+ href='#'
+ onClick={this.handleSubmit}
+ disabled={disabled}
+ >
+ <span className='icon'/>
+ <span>Create {strings.Team} with GitLab Account</span>
+ </a>
+ );
+ }
+
+ return (
+ <form
+ role='form'
+ onSubmit={this.handleSubmit}
+ >
+ <div className={nameDivClass}>
+ <input
+ autoFocus={true}
+ type='text'
+ ref='teamname'
+ className='form-control'
+ placeholder='Enter name of new team'
+ maxLength='128'
+ onChange={this.nameChange}
+ />
+ {nameError}
+ </div>
+ <div className='form-group'>
+ {button}
+ {serverError}
+ </div>
+ <div className='form-group margin--extra-2x'>
+ <span><a href='/find_team'>{'Find my ' + strings.Team}</a></span>
+ </div>
+ </form>
+ );
+ }
+}
+
+SSOSignUpPage.defaultProps = {
+ service: ''
+};
+SSOSignUpPage.propTypes = {
+ service: React.PropTypes.string
+};
diff --git a/web/react/components/user_settings_notifications.jsx b/web/react/components/user_settings_notifications.jsx
index b89f72987..ba0bda78e 100644
--- a/web/react/components/user_settings_notifications.jsx
+++ b/web/react/components/user_settings_notifications.jsx
@@ -1,6 +1,7 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
+var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var SettingItemMin = require('./setting_item_min.jsx');
var SettingItemMax = require('./setting_item_max.jsx');
@@ -67,7 +68,11 @@ function getNotificationsStateFromStores() {
}
}
- return {notifyLevel: desktop, enableEmail: email, soundNeeded: soundNeeded, enableSound: sound, usernameKey: usernameKey, mentionKey: mentionKey, customKeys: customKeys, customKeysChecked: customKeys.length > 0, firstNameKey: firstNameKey, allKey: allKey, channelKey: channelKey};
+ var curChannel = ChannelStore.getCurrent().display_name;
+
+ return {notifyLevel: desktop, enableEmail: email, soundNeeded: soundNeeded, enableSound: sound,
+ usernameKey: usernameKey, mentionKey: mentionKey, customKeys: customKeys, customKeysChecked: customKeys.length > 0,
+ firstNameKey: firstNameKey, allKey: allKey, channelKey: channelKey, curChannel: curChannel};
}
export default class NotificationsTab extends React.Component {
@@ -141,10 +146,12 @@ export default class NotificationsTab extends React.Component {
}
componentDidMount() {
UserStore.addChangeListener(this.onListenerChange);
+ ChannelStore.addChangeListener(this.onListenerChange);
$('#user_settings').on('hidden.bs.modal', this.handleClose);
}
componentWillUnmount() {
UserStore.removeChangeListener(this.onListenerChange);
+ ChannelStore.removeChangeListener(this.onListenerChange);
$('#user_settings').off('hidden.bs.modal', this.handleClose);
this.props.updateSection('');
}
@@ -265,6 +272,12 @@ export default class NotificationsTab extends React.Component {
e.preventDefault();
};
+ let extraInfo = (
+ <div className='setting-list__hint'>
+ These settings will override the global notification settings for the <b>{this.state.curChannel}</b> channel
+ </div>
+ )
+
desktopSection = (
<SettingItemMax
title='Send desktop notifications'
@@ -272,6 +285,7 @@ export default class NotificationsTab extends React.Component {
submit={this.handleSubmit}
server_error={serverError}
updateSection={handleUpdateDesktopSection}
+ extraInfo={extraInfo}
/>
);
} else {