summaryrefslogtreecommitdiffstats
path: root/web/react
diff options
context:
space:
mode:
Diffstat (limited to 'web/react')
-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/post_info.jsx17
-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/user_settings_general.jsx237
-rw-r--r--web/react/components/user_settings_notifications.jsx16
-rw-r--r--web/react/stores/channel_store.jsx1
-rw-r--r--web/react/utils/utils.jsx95
12 files changed, 294 insertions, 144 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/post_info.jsx b/web/react/components/post_info.jsx
index 73e897f62..c80b287a3 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -43,13 +43,16 @@ export default class PostInfo extends React.Component {
if (isOwner) {
dropdownContents.push(
- <li role='presentation'>
+ <li
+ key='editPost'
+ role='presentation'
+ >
<a
href='#'
role='menuitem'
data-toggle='modal'
data-target='#edit_post'
- data-refoucsid="#post_textbox"
+ data-refoucsid='#post_textbox'
data-title={type}
data-message={post.message}
data-postid={post.id}
@@ -64,7 +67,10 @@ export default class PostInfo extends React.Component {
if (isOwner || isAdmin) {
dropdownContents.push(
- <li role='presentation'>
+ <li
+ key='deletePost'
+ role='presentation'
+ >
<a
href='#'
role='menuitem'
@@ -83,7 +89,10 @@ export default class PostInfo extends React.Component {
if (this.props.allowReply === 'true') {
dropdownContents.push(
- <li role='presentation'>
+ <li
+ key='replyLink'
+ role='presentation'
+ >
<a
className='reply-link theme'
href='#'
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/user_settings_general.jsx b/web/react/components/user_settings_general.jsx
index fed11fbe9..ddd2fb607 100644
--- a/web/react/components/user_settings_general.jsx
+++ b/web/react/components/user_settings_general.jsx
@@ -11,10 +11,32 @@ var AsyncClient = require('../utils/async_client.jsx');
var utils = require('../utils/utils.jsx');
var assign = require('object-assign');
-module.exports = React.createClass({
- displayName: 'GeneralTab',
- submitActive: false,
- submitUsername: function(e) {
+export default class UserSettingsGeneralTab extends React.Component {
+ constructor(props) {
+ super(props);
+ this.submitActive = false;
+
+ this.submitUsername = this.submitUsername.bind(this);
+ this.submitNickname = this.submitNickname.bind(this);
+ this.submitName = this.submitName.bind(this);
+ this.submitEmail = this.submitEmail.bind(this);
+ this.submitUser = this.submitUser.bind(this);
+ this.submitPicture = this.submitPicture.bind(this);
+
+ this.updateUsername = this.updateUsername.bind(this);
+ this.updateFirstName = this.updateFirstName.bind(this);
+ this.updateLastName = this.updateLastName.bind(this);
+ this.updateNickname = this.updateNickname.bind(this);
+ this.updateEmail = this.updateEmail.bind(this);
+ this.updatePicture = this.updatePicture.bind(this);
+ this.updateSection = this.updateSection.bind(this);
+
+ this.handleClose = this.handleClose.bind(this);
+ this.setupInitialState = this.setupInitialState.bind(this);
+
+ this.state = this.setupInitialState(props);
+ }
+ submitUsername(e) {
e.preventDefault();
var user = this.props.user;
@@ -37,8 +59,8 @@ module.exports = React.createClass({
user.username = username;
this.submitUser(user);
- },
- submitNickname: function(e) {
+ }
+ submitNickname(e) {
e.preventDefault();
var user = UserStore.getCurrentUser();
@@ -52,8 +74,8 @@ module.exports = React.createClass({
user.nickname = nickname;
this.submitUser(user);
- },
- submitName: function(e) {
+ }
+ submitName(e) {
e.preventDefault();
var user = UserStore.getCurrentUser();
@@ -69,8 +91,8 @@ module.exports = React.createClass({
user.last_name = lastName;
this.submitUser(user);
- },
- submitEmail: function(e) {
+ }
+ submitEmail(e) {
e.preventDefault();
var user = UserStore.getCurrentUser();
@@ -88,15 +110,15 @@ module.exports = React.createClass({
user.email = email;
this.submitUser(user);
- },
- submitUser: function(user) {
+ }
+ submitUser(user) {
client.updateUser(user,
- function() {
+ function updateSuccess() {
this.updateSection('');
AsyncClient.getMe();
}.bind(this),
- function(err) {
- var state = this.getInitialState();
+ function updateFailure(err) {
+ var state = this.setupInitialState(this.props);
if (err.message) {
state.serverError = err.message;
} else {
@@ -105,8 +127,8 @@ module.exports = React.createClass({
this.setState(state);
}.bind(this)
);
- },
- submitPicture: function(e) {
+ }
+ submitPicture(e) {
e.preventDefault();
if (!this.state.picture) {
@@ -129,34 +151,34 @@ module.exports = React.createClass({
this.setState({loadingPicture: true});
client.uploadProfileImage(formData,
- function() {
+ function imageUploadSuccess() {
this.submitActive = false;
AsyncClient.getMe();
window.location.reload();
}.bind(this),
- function(err) {
- var state = this.getInitialState();
+ function imageUploadFailure(err) {
+ var state = this.setupInitialState(this.props);
state.serverError = err;
this.setState(state);
}.bind(this)
);
- },
- updateUsername: function(e) {
+ }
+ updateUsername(e) {
this.setState({username: e.target.value});
- },
- updateFirstName: function(e) {
+ }
+ updateFirstName(e) {
this.setState({firstName: e.target.value});
- },
- updateLastName: function(e) {
+ }
+ updateLastName(e) {
this.setState({lastName: e.target.value});
- },
- updateNickname: function(e) {
+ }
+ updateNickname(e) {
this.setState({nickname: e.target.value});
- },
- updateEmail: function(e) {
+ }
+ updateEmail(e) {
this.setState({email: e.target.value});
- },
- updatePicture: function(e) {
+ }
+ updatePicture(e) {
if (e.target.files && e.target.files[0]) {
this.setState({picture: e.target.files[0]});
@@ -165,34 +187,33 @@ module.exports = React.createClass({
} else {
this.setState({picture: null});
}
- },
- updateSection: function(section) {
- this.setState(assign({}, this.getInitialState(), {clientError: ''}));
+ }
+ updateSection(section) {
+ this.setState(assign({}, this.setupInitialState(this.props), {clientError: '', serverError: '', emailError: ''}));
this.submitActive = false;
this.props.updateSection(section);
- },
- handleClose: function() {
- $(this.getDOMNode()).find('.form-control').each(function() {
+ }
+ handleClose() {
+ $(this.getDOMNode()).find('.form-control').each(function clearForms() {
this.value = '';
});
- this.setState(assign({}, this.getInitialState(), {clientError: null, serverError: null, emailError: null}));
+ this.setState(assign({}, this.setupInitialState(this.props), {clientError: null, serverError: null, emailError: null}));
this.props.updateSection('');
- },
- componentDidMount: function() {
+ }
+ componentDidMount() {
$('#user_settings').on('hidden.bs.modal', this.handleClose);
- },
- componentWillUnmount: function() {
+ }
+ componentWillUnmount() {
$('#user_settings').off('hidden.bs.modal', this.handleClose);
- },
- getInitialState: function() {
- var user = this.props.user;
+ }
+ setupInitialState(props) {
+ var user = props.user;
var emailEnabled = !ConfigStore.getSettingAsBoolean('ByPassEmail', false);
-
return {username: user.username, firstName: user.first_name, lastName: user.last_name, nickname: user.nickname,
- email: user.email, picture: null, loadingPicture: false, emailEnabled: emailEnabled};
- },
- render: function() {
+ email: user.email, picture: null, loadingPicture: false, emailEnabled: emailEnabled};
+ }
+ render() {
var user = this.props.user;
var clientError = null;
@@ -214,19 +235,35 @@ module.exports = React.createClass({
if (this.props.activeSection === 'name') {
inputs.push(
- <div className='form-group'>
+ <div
+ key='firstNameSetting'
+ className='form-group'
+ >
<label className='col-sm-5 control-label'>First Name</label>
<div className='col-sm-7'>
- <input className='form-control' type='text' onChange={this.updateFirstName} value={this.state.firstName}/>
+ <input
+ className='form-control'
+ type='text'
+ onChange={this.updateFirstName}
+ value={this.state.firstName}
+ />
</div>
</div>
);
inputs.push(
- <div className='form-group'>
+ <div
+ key='lastNameSetting'
+ className='form-group'
+ >
<label className='col-sm-5 control-label'>Last Name</label>
<div className='col-sm-7'>
- <input className='form-control' type='text' onChange={this.updateLastName} value={this.state.lastName}/>
+ <input
+ className='form-control'
+ type='text'
+ onChange={this.updateLastName}
+ value={this.state.lastName}
+ />
</div>
</div>
);
@@ -238,7 +275,7 @@ module.exports = React.createClass({
submit={this.submitName}
server_error={serverError}
client_error={clientError}
- updateSection={function(e) {
+ updateSection={function clearSection(e) {
self.updateSection('');
e.preventDefault();
}}
@@ -259,7 +296,7 @@ module.exports = React.createClass({
<SettingItemMin
title='Full Name'
describe={fullName}
- updateSection={function() {
+ updateSection={function updateNameSection() {
self.updateSection('name');
}}
/>
@@ -268,11 +305,24 @@ module.exports = React.createClass({
var nicknameSection;
if (this.props.activeSection === 'nickname') {
+ let nicknameLabel = 'Nickname';
+ if (utils.isMobile()) {
+ nicknameLabel = '';
+ }
+
inputs.push(
- <div className='form-group'>
- <label className='col-sm-5 control-label'>{utils.isMobile() ? '' : 'Nickname'}</label>
+ <div
+ key='nicknameSetting'
+ className='form-group'
+ >
+ <label className='col-sm-5 control-label'>{nicknameLabel}</label>
<div className='col-sm-7'>
- <input className='form-control' type='text' onChange={this.updateNickname} value={this.state.nickname}/>
+ <input
+ className='form-control'
+ type='text'
+ onChange={this.updateNickname}
+ value={this.state.nickname}
+ />
</div>
</div>
);
@@ -284,7 +334,7 @@ module.exports = React.createClass({
submit={this.submitNickname}
server_error={serverError}
client_error={clientError}
- updateSection={function(e) {
+ updateSection={function clearSection(e) {
self.updateSection('');
e.preventDefault();
}}
@@ -295,7 +345,7 @@ module.exports = React.createClass({
<SettingItemMin
title='Nickname'
describe={UserStore.getCurrentUser().nickname}
- updateSection={function() {
+ updateSection={function updateNicknameSection() {
self.updateSection('nickname');
}}
/>
@@ -304,11 +354,24 @@ module.exports = React.createClass({
var usernameSection;
if (this.props.activeSection === 'username') {
+ let usernameLabel = 'Username';
+ if (utils.isMobile()) {
+ usernameLabel = '';
+ }
+
inputs.push(
- <div className='form-group'>
- <label className='col-sm-5 control-label'>{utils.isMobile() ? '' : 'Username'}</label>
+ <div
+ key='usernameSetting'
+ className='form-group'
+ >
+ <label className='col-sm-5 control-label'>{usernameLabel}</label>
<div className='col-sm-7'>
- <input className='form-control' type='text' onChange={this.updateUsername} value={this.state.username}/>
+ <input
+ className='form-control'
+ type='text'
+ onChange={this.updateUsername}
+ value={this.state.username}
+ />
</div>
</div>
);
@@ -320,7 +383,7 @@ module.exports = React.createClass({
submit={this.submitUsername}
server_error={serverError}
client_error={clientError}
- updateSection={function(e) {
+ updateSection={function clearSection(e) {
self.updateSection('');
e.preventDefault();
}}
@@ -331,7 +394,7 @@ module.exports = React.createClass({
<SettingItemMin
title='Username'
describe={UserStore.getCurrentUser().username}
- updateSection={function() {
+ updateSection={function updateUsernameSection() {
self.updateSection('username');
}}
/>
@@ -346,11 +409,16 @@ module.exports = React.createClass({
}
inputs.push(
- <div>
+ <div key='emailSetting'>
<div className='form-group'>
<label className='col-sm-5 control-label'>Primary Email</label>
<div className='col-sm-7'>
- <input className='form-control' type='text' onChange={this.updateEmail} value={this.state.email}/>
+ <input
+ className='form-control'
+ type='text'
+ onChange={this.updateEmail}
+ value={this.state.email}
+ />
</div>
</div>
{helpText}
@@ -364,7 +432,7 @@ module.exports = React.createClass({
submit={this.submitEmail}
server_error={serverError}
client_error={emailError}
- updateSection={function(e) {
+ updateSection={function clearSection(e) {
self.updateSection('');
e.preventDefault();
}}
@@ -375,7 +443,7 @@ module.exports = React.createClass({
<SettingItemMin
title='Email'
describe={UserStore.getCurrentUser().email}
- updateSection={function() {
+ updateSection={function updateEmailSection() {
self.updateSection('email');
}}
/>
@@ -391,7 +459,7 @@ module.exports = React.createClass({
src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update}
server_error={serverError}
client_error={clientError}
- updateSection={function(e) {
+ updateSection={function clearSection(e) {
self.updateSection('');
e.preventDefault();
}}
@@ -410,7 +478,7 @@ module.exports = React.createClass({
<SettingItemMin
title='Profile Picture'
describe={minMessage}
- updateSection={function() {
+ updateSection={function updatePictureSection() {
self.updateSection('picture');
}}
/>
@@ -419,8 +487,21 @@ 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>General Settings</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>
+ General Settings
+ </h4>
</div>
<div className='user-settings'>
<h3 className='tab-header'>General Settings</h3>
@@ -439,4 +520,10 @@ module.exports = React.createClass({
</div>
);
}
-});
+}
+
+UserSettingsGeneralTab.propTypes = {
+ user: React.PropTypes.object,
+ updateSection: React.PropTypes.func,
+ activeSection: 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/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/utils.jsx b/web/react/utils/utils.jsx
index 34a0d55da..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,6 +317,7 @@ 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);
@@ -303,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'>
@@ -456,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 &&
@@ -1001,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('__');