summaryrefslogtreecommitdiffstats
path: root/web/react
diff options
context:
space:
mode:
Diffstat (limited to 'web/react')
-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.jsx34
-rw-r--r--web/react/components/setting_item_max.jsx2
-rw-r--r--web/react/components/sidebar.jsx3
-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_import_tab.jsx10
-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
-rw-r--r--web/react/pages/signup_team.jsx6
-rw-r--r--web/react/pages/signup_user_oauth.jsx11
-rw-r--r--web/react/stores/channel_store.jsx1
-rw-r--r--web/react/utils/async_client.jsx5
-rw-r--r--web/react/utils/client.jsx15
-rw-r--r--web/react/utils/constants.jsx2
-rw-r--r--web/react/utils/utils.jsx96
27 files changed, 572 insertions, 315 deletions
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..5b0b1f79a 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);
@@ -511,9 +533,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 +633,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.jsx b/web/react/components/sidebar.jsx
index d79505e9e..8dd192893 100644
--- a/web/react/components/sidebar.jsx
+++ b/web/react/components/sidebar.jsx
@@ -64,7 +64,8 @@ function getStateFromStores() {
var tempChannel = {};
tempChannel.fake = true;
tempChannel.name = channelName;
- tempChannel.display_name = utils.getDisplayName(teammate);
+ tempChannel.display_name = teammate.username;
+ tempChannel.teammate_username = teammate.username;
tempChannel.status = UserStore.getStatus(teammate.id);
tempChannel.last_post_at = 0;
tempChannel.total_msg_count = 0;
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_import_tab.jsx b/web/react/components/team_import_tab.jsx
index ecb3491b0..e3415d7f4 100644
--- a/web/react/components/team_import_tab.jsx
+++ b/web/react/components/team_import_tab.jsx
@@ -23,12 +23,10 @@ module.exports = React.createClass({
var uploadHelpText = (
<div>
<br/>
- Slack does now allow you to export any of your files or images stored in slack. Private channels and direct message channels are also not exported. Therefore, no files, images, or private channels will be imported.
- <br/>
- Slack bot posts are not imported.
- <br/>
- Unable to import Slack channels that are not valid Mattermost channels. (ex underscores)
- <br/>
+ Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team's public channels.
+ <br/><br/>
+ The Slack import to Mattermost is in "Preview". Slack bot posts and channels with underscores do not yet import.
+ <br/><br/>
</div>
);
var uploadSection = (
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 {
diff --git a/web/react/pages/signup_team.jsx b/web/react/pages/signup_team.jsx
index 37c441d4f..4b58025ac 100644
--- a/web/react/pages/signup_team.jsx
+++ b/web/react/pages/signup_team.jsx
@@ -5,11 +5,13 @@ var SignupTeam = require('../components/signup_team.jsx');
var AsyncClient = require('../utils/async_client.jsx');
-global.window.setup_signup_team_page = function() {
+global.window.setup_signup_team_page = function(authServices) {
AsyncClient.getConfig();
+ var services = JSON.parse(authServices);
+
React.render(
- <SignupTeam />,
+ <SignupTeam services={services} />,
document.getElementById('signup-team')
);
};
diff --git a/web/react/pages/signup_user_oauth.jsx b/web/react/pages/signup_user_oauth.jsx
deleted file mode 100644
index 6a0707702..000000000
--- a/web/react/pages/signup_user_oauth.jsx
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-var SignupUserOAuth = require('../components/signup_user_oauth.jsx');
-
-global.window.setup_signup_user_oauth_page = function(user, team_name, team_display_name) {
- React.render(
- <SignupUserOAuth user={user} teamName={team_name} teamDisplayName={team_display_name} />,
- document.getElementById('signup-user-complete')
- );
-};
diff --git a/web/react/stores/channel_store.jsx b/web/react/stores/channel_store.jsx
index f7c23841c..678d50bbd 100644
--- a/web/react/stores/channel_store.jsx
+++ b/web/react/stores/channel_store.jsx
@@ -270,4 +270,5 @@ ChannelStore.dispatchToken = AppDispatcher.register(function(payload) {
}
});
+ChannelStore.setMaxListeners(11);
module.exports = ChannelStore;
diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx
index 4b0b90dc7..c03a0230b 100644
--- a/web/react/utils/async_client.jsx
+++ b/web/react/utils/async_client.jsx
@@ -412,6 +412,11 @@ function getPosts(id) {
return;
}
+ if (PostStore.getCurrentPosts() == null) {
+ module.exports.getPostsPage(true, id, Constants.POST_CHUNK_SIZE);
+ return;
+ }
+
var latestUpdate = PostStore.getLatestUpdate(channelId);
callTracker['getPosts_' + channelId] = utils.getTimestamp();
diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx
index 70220c71e..082f82a08 100644
--- a/web/react/utils/client.jsx
+++ b/web/react/utils/client.jsx
@@ -70,6 +70,21 @@ module.exports.createTeamFromSignup = function(teamSignup, success, error) {
});
};
+module.exports.createTeamWithSSO = function(team, service, success, error) {
+ $.ajax({
+ url: '/api/v1/teams/create_with_sso/' + service,
+ dataType: 'json',
+ contentType: 'application/json',
+ type: 'POST',
+ data: JSON.stringify(team),
+ success: success,
+ error: function onError(xhr, status, err) {
+ var e = handleError('createTeamWithSSO', xhr, status, err);
+ error(e);
+ }
+ });
+};
+
module.exports.createUser = function(user, data, emailHash, success, error) {
$.ajax({
url: '/api/v1/users/create?d=' + encodeURIComponent(data) + '&h=' + encodeURIComponent(emailHash),
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 82fc3da22..8721ced7c 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -60,7 +60,7 @@ module.exports = {
DEFAULT_CHANNEL: 'town-square',
OFFTOPIC_CHANNEL: 'off-topic',
GITLAB_SERVICE: 'gitlab',
- GOOGLE_SERVICE: 'google',
+ EMAIL_SERVICE: 'email',
POST_CHUNK_SIZE: 60,
MAX_POST_CHUNKS: 3,
POST_LOADING: 'loading',
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index f0cf17446..a1dc72ae2 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -260,10 +260,40 @@ module.exports.escapeRegExp = function(string) {
return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1');
};
+function handleYoutubeTime(link) {
+ var timeRegex = /[\\?&]t=([0-9hms]+)/;
+
+ var time = link.trim().match(timeRegex);
+ if (!time || !time[1]) {
+ return '';
+ }
+
+ var hours = time[1].match(/([0-9]+)h/);
+ var minutes = time[1].match(/([0-9]+)m/);
+ var seconds = time[1].match(/([0-9]+)s/);
+
+ var ticks = 0;
+
+ if (hours && hours[1]) {
+ ticks += parseInt(hours[1], 10) * 3600;
+ }
+
+ if (minutes && minutes[1]) {
+ ticks += parseInt(minutes[1], 10) * 60;
+ }
+
+ if (seconds && seconds[1]) {
+ ticks += parseInt(seconds[1], 10);
+ }
+
+ return '&start=' + ticks.toString();
+}
+
function getYoutubeEmbed(link) {
var regex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/;
var youtubeId = link.trim().match(regex)[1];
+ var time = handleYoutubeTime(link);
function onClick(e) {
var div = $(e.target).closest('.video-thumbnail__container')[0];
@@ -271,7 +301,8 @@ function getYoutubeEmbed(link) {
iframe.setAttribute('src',
'https://www.youtube.com/embed/' +
div.id +
- '?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1');
+ '?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1' +
+ time);
iframe.setAttribute('width', '480px');
iframe.setAttribute('height', '360px');
iframe.setAttribute('type', 'text/html');
@@ -286,10 +317,10 @@ function getYoutubeEmbed(link) {
return;
}
var metadata = data.items[0].snippet;
+ $('.video-type.' + youtubeId).html("Youtube - ")
$('.video-uploader.' + youtubeId).html(metadata.channelTitle);
$('.video-title.' + youtubeId).find('a').html(metadata.title);
$('.post-list-holder-by-time').scrollTop($('.post-list-holder-by-time')[0].scrollHeight);
- $('.post-list-holder-by-time').perfectScrollbar('update');
}
if (config.GoogleDeveloperKey) {
@@ -304,9 +335,11 @@ function getYoutubeEmbed(link) {
return (
<div className='post-comment'>
- <h4 className='video-type'>YouTube</h4>
+ <h4>
+ <span className={'video-type ' + youtubeId}>YouTube</span>
+ <span className={'video-title ' + youtubeId}><a href={link}></a></span>
+ </h4>
<h4 className={'video-uploader ' + youtubeId}></h4>
- <h4 className={'video-title ' + youtubeId}><a href={link}></a></h4>
<div className='video-div embed-responsive-item' id={youtubeId} onClick={onClick}>
<div className='embed-responsive embed-responsive-4by3 video-div__placeholder'>
<div id={youtubeId} className='video-thumbnail__container'>
@@ -457,9 +490,21 @@ module.exports.textToJsx = function(text, options) {
var mentionRegex = /^(?:@)([a-z0-9_]+)$/gi; // looks loop invariant but a weird JS bug needs it to be redefined here
var explicitMention = mentionRegex.exec(trimWord);
- if ((trimWord.toLowerCase().indexOf(searchTerm) > -1 || word.toLowerCase().indexOf(searchTerm) > -1) && searchTerm != '') {
-
- highlightSearchClass = ' search-highlight';
+ if (searchTerm !== '') {
+ let searchWords = searchTerm.split(' ');
+ for (let idx in searchWords) {
+ let searchWord = searchWords[idx];
+ if (searchWord === word.toLowerCase() || searchWord === trimWord.toLowerCase()) {
+ highlightSearchClass = ' search-highlight';
+ break;
+ } else if (searchWord.charAt(searchWord.length - 1) === '*') {
+ let searchWordPrefix = searchWord.slice(0,-1);
+ if (trimWord.toLowerCase().indexOf(searchWordPrefix) > -1 || word.toLowerCase().indexOf(searchWordPrefix) > -1) {
+ highlightSearchClass = ' search-highlight';
+ break;
+ }
+ }
+ }
}
if (explicitMention &&
@@ -1002,43 +1047,6 @@ module.exports.isBrowserEdge = function() {
return window.naviagtor && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('edge') > -1;
};
-// Gets text length consistent with maxlength property of textarea html tag
-module.exports.getLengthOfTextInTextarea = function(messageText) {
- // Need to get length with carriage returns counting as two characters to match textbox maxlength behavior
- // unless ie10/ie11/edge which already do
-
- var len = messageText.length;
- if (!module.exports.isBrowserIE() && !module.exports.isBrowserEdge()) {
- len = messageText.replace(/\r(?!\n)|\n(?!\r)/g, '--').length;
- }
-
- return len;
-};
-
-module.exports.checkMessageLengthError = function(message, currentError, newError) {
- var updatedError = currentError;
- var len = module.exports.getLengthOfTextInTextarea(message);
-
- if (!currentError && len >= Constants.MAX_POST_LEN) {
- updatedError = newError;
- } else if (currentError === newError && len < Constants.MAX_POST_LEN) {
- updatedError = '';
- }
-
- return updatedError;
-};
-
-// Necessary due to issues with textarea max length and pasting newlines
-module.exports.truncateText = function(message) {
- var lengthDifference = module.exports.getLengthOfTextInTextarea(message) - message.length;
-
- if (lengthDifference > 0) {
- return message.substring(0, Constants.MAX_POST_LEN - lengthDifference);
- }
-
- return message.substring(0, Constants.MAX_POST_LEN);
-};
-
// Used to get the id of the other user from a DM channel
module.exports.getUserIdFromChannelName = function(channel) {
var ids = channel.name.split('__');