summaryrefslogtreecommitdiffstats
path: root/web/react
diff options
context:
space:
mode:
Diffstat (limited to 'web/react')
-rw-r--r--web/react/.eslintrc19
-rw-r--r--web/react/components/access_history_modal.jsx24
-rw-r--r--web/react/components/activity_log_modal.jsx24
-rw-r--r--web/react/components/admin_console/privacy_settings.jsx34
-rw-r--r--web/react/components/admin_console/service_settings.jsx70
-rw-r--r--web/react/components/channel_header.jsx40
-rw-r--r--web/react/components/channel_members.jsx16
-rw-r--r--web/react/components/channel_notifications.jsx35
-rw-r--r--web/react/components/command_list.jsx2
-rw-r--r--web/react/components/create_comment.jsx12
-rw-r--r--web/react/components/create_post.jsx12
-rw-r--r--web/react/components/delete_channel_modal.jsx16
-rw-r--r--web/react/components/delete_post_modal.jsx24
-rw-r--r--web/react/components/edit_channel_modal.jsx10
-rw-r--r--web/react/components/file_upload.jsx2
-rw-r--r--web/react/components/get_link_modal.jsx18
-rw-r--r--web/react/components/login.jsx6
-rw-r--r--web/react/components/mention.jsx6
-rw-r--r--web/react/components/mention_list.jsx91
-rw-r--r--web/react/components/more_channels.jsx98
-rw-r--r--web/react/components/more_direct_channels.jsx32
-rw-r--r--web/react/components/navbar_dropdown.jsx12
-rw-r--r--web/react/components/popover_list_members.jsx3
-rw-r--r--web/react/components/post.jsx9
-rw-r--r--web/react/components/post_body.jsx2
-rw-r--r--web/react/components/post_header.jsx20
-rw-r--r--web/react/components/post_list.jsx29
-rw-r--r--web/react/components/rename_channel_modal.jsx20
-rw-r--r--web/react/components/sidebar.jsx36
-rw-r--r--web/react/components/sidebar_header.jsx3
-rw-r--r--web/react/components/sidebar_right.jsx6
-rw-r--r--web/react/components/signup_user_complete.jsx27
-rw-r--r--web/react/components/team_signup_email_item.jsx9
-rw-r--r--web/react/components/team_signup_send_invites_page.jsx6
-rw-r--r--web/react/components/team_signup_url_page.jsx30
-rw-r--r--web/react/components/team_signup_welcome_page.jsx3
-rw-r--r--web/react/components/user_profile.jsx16
-rw-r--r--web/react/components/user_settings/import_theme_modal.jsx4
-rw-r--r--web/react/components/user_settings/premade_theme_chooser.jsx2
-rw-r--r--web/react/package.json4
-rw-r--r--web/react/pages/authorize.jsx16
-rw-r--r--web/react/pages/channel.jsx4
-rw-r--r--web/react/stores/browser_store.jsx2
-rw-r--r--web/react/stores/team_store.jsx8
-rw-r--r--web/react/utils/client.jsx2
-rw-r--r--web/react/utils/constants.jsx2
-rw-r--r--web/react/utils/utils.jsx60
47 files changed, 567 insertions, 359 deletions
diff --git a/web/react/.eslintrc b/web/react/.eslintrc
index c0d0bb200..c4167829d 100644
--- a/web/react/.eslintrc
+++ b/web/react/.eslintrc
@@ -42,12 +42,13 @@
"valid-typeof": 2,
"block-scoped-var": 2,
- "complexity": [1, 8],
+ "complexity": [0, 8],
"consistent-return": 2,
"curly": [2, "all"],
"dot-location": [2, "object"],
"dot-notation": 2,
"eqeqeq": [2, "smart"],
+ "global-require": 2,
"guard-for-in": 2,
"no-alert": 2,
"no-array-constructor": 2,
@@ -109,30 +110,34 @@
"func-names": 2,
"func-style": [2, "declaration"],
"indent": [2, 4, {"SwitchCase": 0}],
+ "jsx-quotes": [2, "prefer-single"],
"key-spacing": [2, {"beforeColon": false, "afterColon": true}],
- "lines-around-comment": [2, { "beforeBlockComment": true, "beforeLineComment": true, "allowBlockStart": true, "allowBlockEnd": true }],
"linebreak-style": 2,
+ "lines-around-comment": [2, { "beforeBlockComment": true, "beforeLineComment": true, "allowBlockStart": true, "allowBlockEnd": true }],
"new-cap": 2,
"new-parens": 2,
"no-lonely-if": 2,
"no-mixed-spaces-and-tabs": 2,
"no-multiple-empty-lines": [2, {"max": 1}],
+ "no-negated-condition": 2,
+ "no-nested-ternary": 2,
"no-spaced-func": 2,
- "no-ternary": 2,
+ "no-ternary": 0,
"no-trailing-spaces": [2, { "skipBlankLines": false }],
"no-underscore-dangle": 2,
- "no-unneeded-ternary": 2,
+ "no-unneeded-ternary": [2, {"defaultAssignment": false}],
"object-curly-spacing": [2, "never"],
"one-var": [2, "never"],
"operator-linebreak": [2, "after"],
"padded-blocks": [2, "never"],
"quote-props": [2, "as-needed"],
"quotes": [2, "single", "avoid-escape"],
- "semi-spacing": [2, {"before": false, "after": true}],
"semi": [2, "always"],
+ "semi-spacing": [2, {"before": false, "after": true}],
"space-after-keywords": [2, "always"],
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, "never"],
+ "space-before-keywords": [2, "always"],
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-return-throw-case": 2,
@@ -167,7 +172,6 @@
"react/jsx-no-duplicate-props": [2, { "ignoreCase": false }],
"react/jsx-no-literals": 1,
"react/jsx-no-undef": 2,
- "react/jsx-quotes": [2, "single", "avoid-escape"],
"react/jsx-uses-react": 2,
"react/jsx-uses-vars": 2,
"react/no-danger": 0,
@@ -179,6 +183,7 @@
"react/prop-types": 2,
"react/self-closing-comp": 2,
"react/sort-comp": 0,
- "react/wrap-multilines": 2
+ "react/wrap-multilines": 2,
+ "react/no-direct-mutation-state": 2
}
}
diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx
index a080150dd..0dfd36717 100644
--- a/web/react/components/access_history_modal.jsx
+++ b/web/react/components/access_history_modal.jsx
@@ -12,25 +12,31 @@ export default class AccessHistoryModal extends React.Component {
this.onAuditChange = this.onAuditChange.bind(this);
this.handleMoreInfo = this.handleMoreInfo.bind(this);
+ this.onHide = this.onHide.bind(this);
+ this.onShow = this.onShow.bind(this);
- this.state = this.getStateFromStoresForAudits();
- this.state.moreInfo = [];
+ let state = this.getStateFromStoresForAudits();
+ state.moreInfo = [];
+
+ this.state = state;
}
getStateFromStoresForAudits() {
return {
audits: UserStore.getAudits()
};
}
+ onShow() {
+ AsyncClient.getAudits();
+ }
+ onHide() {
+ $('#user_settings').modal('show');
+ this.setState({moreInfo: []});
+ }
componentDidMount() {
UserStore.addAuditsChangeListener(this.onAuditChange);
- $(React.findDOMNode(this.refs.modal)).on('shown.bs.modal', function show() {
- AsyncClient.getAudits();
- });
+ $(React.findDOMNode(this.refs.modal)).on('shown.bs.modal', this.onShow);
- $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function hide() {
- $('#user_settings').modal('show');
- this.setState({moreInfo: []});
- }.bind(this));
+ $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.onHide);
}
componentWillUnmount() {
UserStore.removeAuditsChangeListener(this.onAuditChange);
diff --git a/web/react/components/activity_log_modal.jsx b/web/react/components/activity_log_modal.jsx
index fe40385a0..aee2541b5 100644
--- a/web/react/components/activity_log_modal.jsx
+++ b/web/react/components/activity_log_modal.jsx
@@ -14,9 +14,13 @@ export default class ActivityLogModal extends React.Component {
this.submitRevoke = this.submitRevoke.bind(this);
this.onListenerChange = this.onListenerChange.bind(this);
this.handleMoreInfo = this.handleMoreInfo.bind(this);
+ this.onHide = this.onHide.bind(this);
+ this.onShow = this.onShow.bind(this);
- this.state = this.getStateFromStores();
- this.state.moreInfo = [];
+ let state = this.getStateFromStores();
+ state.moreInfo = [];
+
+ this.state = state;
}
getStateFromStores() {
return {
@@ -38,16 +42,18 @@ export default class ActivityLogModal extends React.Component {
}.bind(this)
);
}
+ onShow() {
+ AsyncClient.getSessions();
+ }
+ onHide() {
+ $('#user_settings').modal('show');
+ this.setState({moreInfo: []});
+ }
componentDidMount() {
UserStore.addSessionsChangeListener(this.onListenerChange);
- $(React.findDOMNode(this.refs.modal)).on('shown.bs.modal', function handleShow() {
- AsyncClient.getSessions();
- });
+ $(React.findDOMNode(this.refs.modal)).on('shown.bs.modal', this.onShow);
- $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function handleHide() {
- $('#user_settings').modal('show');
- this.setState({moreInfo: []});
- }.bind(this));
+ $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.onHide);
}
componentWillUnmount() {
UserStore.removeSessionsChangeListener(this.onListenerChange);
diff --git a/web/react/components/admin_console/privacy_settings.jsx b/web/react/components/admin_console/privacy_settings.jsx
index affd8ae11..c74d321e6 100644
--- a/web/react/components/admin_console/privacy_settings.jsx
+++ b/web/react/components/admin_console/privacy_settings.jsx
@@ -30,6 +30,7 @@ export default class PrivacySettings extends React.Component {
var config = this.props.config;
config.PrivacySettings.ShowEmailAddress = React.findDOMNode(this.refs.ShowEmailAddress).checked;
config.PrivacySettings.ShowFullName = React.findDOMNode(this.refs.ShowFullName).checked;
+ config.PrivacySettings.EnableDiagnostic = React.findDOMNode(this.refs.EnableDiagnostic).checked;
Client.saveConfig(
config,
@@ -137,6 +138,39 @@ export default class PrivacySettings extends React.Component {
</div>
<div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='EnableDiagnostic'
+ >
+ {'Send Error and Diagnostic: '}
+ </label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnableDiagnostic'
+ value='true'
+ ref='EnableDiagnostic'
+ defaultChecked={this.props.config.PrivacySettings.EnableDiagnostic}
+ onChange={this.handleChange}
+ />
+ {'true'}
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnableDiagnostic'
+ value='false'
+ defaultChecked={!this.props.config.PrivacySettings.EnableDiagnostic}
+ onChange={this.handleChange}
+ />
+ {'false'}
+ </label>
+ <p className='help-text'>{'When true, The server will periodically send error and diagnostic information to Mattermost.'}</p>
+ </div>
+ </div>
+
+ <div className='form-group'>
<div className='col-sm-12'>
{serverError}
<button
diff --git a/web/react/components/admin_console/service_settings.jsx b/web/react/components/admin_console/service_settings.jsx
index 245ffa871..b2d1b7b4d 100644
--- a/web/react/components/admin_console/service_settings.jsx
+++ b/web/react/components/admin_console/service_settings.jsx
@@ -37,6 +37,8 @@ export default class ServiceSettings extends React.Component {
config.ServiceSettings.GoogleDeveloperKey = React.findDOMNode(this.refs.GoogleDeveloperKey).value.trim();
//config.ServiceSettings.EnableOAuthServiceProvider = React.findDOMNode(this.refs.EnableOAuthServiceProvider).checked;
config.ServiceSettings.EnableIncomingWebhooks = React.findDOMNode(this.refs.EnableIncomingWebhooks).checked;
+ config.ServiceSettings.EnablePostUsernameOverride = React.findDOMNode(this.refs.EnablePostUsernameOverride).checked;
+ config.ServiceSettings.EnablePostIconOverride = React.findDOMNode(this.refs.EnablePostIconOverride).checked;
config.ServiceSettings.EnableTesting = React.findDOMNode(this.refs.EnableTesting).checked;
var MaximumLoginAttempts = 10;
@@ -199,7 +201,73 @@ export default class ServiceSettings extends React.Component {
/>
{'false'}
</label>
- <p className='help-text'>{'When true, incoming webhooks will be allowed.'}</p>
+ <p className='help-text'>{'When true, incoming webhooks will be allowed. To help combat phishing attacks, all posts from webhooks will be labelled by a BOT tag.'}</p>
+ </div>
+ </div>
+
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='EnablePostUsernameOverride'
+ >
+ {'Enable Overriding Usernames from Webhooks: '}
+ </label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnablePostUsernameOverride'
+ value='true'
+ ref='EnablePostUsernameOverride'
+ defaultChecked={this.props.config.ServiceSettings.EnablePostUsernameOverride}
+ onChange={this.handleChange}
+ />
+ {'true'}
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnablePostUsernameOverride'
+ value='false'
+ defaultChecked={!this.props.config.ServiceSettings.EnablePostUsernameOverride}
+ onChange={this.handleChange}
+ />
+ {'false'}
+ </label>
+ <p className='help-text'>{'When true, webhooks will be allowed to change the username they are posting as. Note, combined with allowing icon overriding, this could open users up to phishing attacks.'}</p>
+ </div>
+ </div>
+
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='EnablePostIconOverride'
+ >
+ {'Enable Overriding Icon from Webhooks: '}
+ </label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnablePostIconOverride'
+ value='true'
+ ref='EnablePostIconOverride'
+ defaultChecked={this.props.config.ServiceSettings.EnablePostIconOverride}
+ onChange={this.handleChange}
+ />
+ {'true'}
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnablePostIconOverride'
+ value='false'
+ defaultChecked={!this.props.config.ServiceSettings.EnablePostIconOverride}
+ onChange={this.handleChange}
+ />
+ {'false'}
+ </label>
+ <p className='help-text'>{'When true, webhooks will be allowed to change the icon they post with. Note, combined with allowing username overriding, this could open users up to phishing attacks.'}</p>
</div>
</div>
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index b81936b57..f15974d35 100644
--- a/web/react/components/channel_header.jsx
+++ b/web/react/components/channel_header.jsx
@@ -132,7 +132,26 @@ export default class ChannelHeader extends React.Component {
}
let dropdownContents = [];
- if (!isDirect) {
+ if (isDirect) {
+ dropdownContents.push(
+ <li
+ key='edit_description_direct'
+ role='presentation'
+ >
+ <a
+ role='menuitem'
+ href='#'
+ data-toggle='modal'
+ data-target='#edit_channel'
+ data-desc={channel.description}
+ data-title={channel.display_name}
+ data-channelid={channel.id}
+ >
+ Set Channel Description...
+ </a>
+ </li>
+ );
+ } else {
dropdownContents.push(
<li
key='view_info'
@@ -276,25 +295,6 @@ export default class ChannelHeader extends React.Component {
</li>
);
}
- } else {
- dropdownContents.push(
- <li
- key='edit_description_direct'
- role='presentation'
- >
- <a
- role='menuitem'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-desc={channel.description}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- Set Channel Description...
- </a>
- </li>
- );
}
return (
diff --git a/web/react/components/channel_members.jsx b/web/react/components/channel_members.jsx
index 1eda6a104..53c854eb7 100644
--- a/web/react/components/channel_members.jsx
+++ b/web/react/components/channel_members.jsx
@@ -15,6 +15,8 @@ export default class ChannelMembers extends React.Component {
this.getStateFromStores = this.getStateFromStores.bind(this);
this.onChange = this.onChange.bind(this);
this.handleRemove = this.handleRemove.bind(this);
+ this.onHide = this.onHide.bind(this);
+ this.onShow = this.onShow.bind(this);
this.state = this.getStateFromStores();
}
@@ -63,16 +65,18 @@ export default class ChannelMembers extends React.Component {
channelName: channelName
};
}
+ onHide() {
+ this.setState({renderMembers: false});
+ }
+ onShow() {
+ this.setState({renderMembers: true});
+ }
componentDidMount() {
ChannelStore.addExtraInfoChangeListener(this.onChange);
ChannelStore.addChangeListener(this.onChange);
- $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function handleHide() {
- this.setState({renderMembers: false});
- }.bind(this));
+ $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.onHide);
- $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function handleShow() {
- this.setState({renderMembers: true});
- }.bind(this));
+ $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
}
componentWillUnmount() {
ChannelStore.removeExtraInfoChangeListener(this.onChange);
diff --git a/web/react/components/channel_notifications.jsx b/web/react/components/channel_notifications.jsx
index 45981b295..fed8e789e 100644
--- a/web/react/components/channel_notifications.jsx
+++ b/web/react/components/channel_notifications.jsx
@@ -23,6 +23,7 @@ export default class ChannelNotifications extends React.Component {
this.handleSubmitMarkUnreadLevel = this.handleSubmitMarkUnreadLevel.bind(this);
this.handleUpdateMarkUnreadLevel = this.handleUpdateMarkUnreadLevel.bind(this);
this.createMarkUnreadLevelSection = this.createMarkUnreadLevelSection.bind(this);
+ this.onShow = this.onShow.bind(this);
this.state = {
notifyLevel: '',
@@ -32,30 +33,29 @@ export default class ChannelNotifications extends React.Component {
activeSection: ''
};
}
+ onShow(e) {
+ var button = e.relatedTarget;
+ var channelId = button.getAttribute('data-channelid');
+ const member = ChannelStore.getMember(channelId);
+ var notifyLevel = member.notify_props.desktop;
+ var markUnreadLevel = member.notify_props.mark_unread;
+
+ this.setState({
+ notifyLevel,
+ markUnreadLevel,
+ title: button.getAttribute('data-title'),
+ channelId
+ });
+ }
componentDidMount() {
ChannelStore.addChangeListener(this.onListenerChange);
- $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function showModal(e) {
- var button = e.relatedTarget;
- var channelId = button.getAttribute('data-channelid');
-
- const member = ChannelStore.getMember(channelId);
- var notifyLevel = member.notify_props.desktop;
- var markUnreadLevel = member.notify_props.mark_unread;
-
- this.setState({
- notifyLevel,
- markUnreadLevel,
- title: button.getAttribute('data-title'),
- channelId: channelId
- });
- }.bind(this));
+ $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
}
componentWillUnmount() {
ChannelStore.removeChangeListener(this.onListenerChange);
}
-
onListenerChange() {
if (!this.state.channelId) {
return;
@@ -76,7 +76,6 @@ export default class ChannelNotifications extends React.Component {
updateSection(section) {
this.setState({activeSection: section});
}
-
handleSubmitNotifyLevel() {
var channelId = this.state.channelId;
var notifyLevel = this.state.notifyLevel;
@@ -103,12 +102,10 @@ export default class ChannelNotifications extends React.Component {
}
);
}
-
handleUpdateNotifyLevel(notifyLevel) {
this.setState({notifyLevel});
React.findDOMNode(this.refs.modal).focus();
}
-
createNotifyLevelSection(serverError) {
var handleUpdateSection;
diff --git a/web/react/components/command_list.jsx b/web/react/components/command_list.jsx
index fea7085b7..e027e87ae 100644
--- a/web/react/components/command_list.jsx
+++ b/web/react/components/command_list.jsx
@@ -51,7 +51,7 @@ export default class CommandList extends React.Component {
this.setState({suggestions: data.suggestions, cmd: cmd});
}.bind(this),
function fail() {
- }
+ }
);
}
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index 5097b3aa5..9c233ea26 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -170,7 +170,9 @@ export default class CreateComment extends React.Component {
this.setState({uploadsInProgress: draft.uploadsInProgress, previews: draft.previews});
}
handleUploadError(err, clientId) {
- if (clientId !== -1) {
+ if (clientId === -1) {
+ this.setState({serverError: err});
+ } else {
let draft = PostStore.getCommentDraft(this.props.rootId);
const index = draft.uploadsInProgress.indexOf(clientId);
@@ -181,8 +183,6 @@ export default class CreateComment extends React.Component {
PostStore.storeCommentDraft(this.props.rootId, draft);
this.setState({uploadsInProgress: draft.uploadsInProgress, serverError: err});
- } else {
- this.setState({serverError: err});
}
}
handleTextDrop(text) {
@@ -196,15 +196,15 @@ export default class CreateComment extends React.Component {
// id can either be the path of an uploaded file or the client id of an in progress upload
let index = previews.indexOf(id);
- if (index !== -1) {
- previews.splice(index, 1);
- } else {
+ if (index === -1) {
index = uploadsInProgress.indexOf(id);
if (index !== -1) {
uploadsInProgress.splice(index, 1);
this.refs.fileUpload.cancelUpload(id);
}
+ } else {
+ previews.splice(index, 1);
}
let draft = PostStore.getCommentDraft(this.props.rootId);
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index 0cd14747d..6e83f4faf 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -222,7 +222,9 @@ export default class CreatePost extends React.Component {
this.setState({uploadsInProgress: draft.uploadsInProgress, previews: draft.previews});
}
handleUploadError(err, clientId) {
- if (clientId !== -1) {
+ if (clientId === -1) {
+ this.setState({serverError: err});
+ } else {
const draft = PostStore.getDraft(this.state.channelId);
const index = draft.uploadsInProgress.indexOf(clientId);
@@ -233,8 +235,6 @@ export default class CreatePost extends React.Component {
PostStore.storeDraft(this.state.channelId, draft);
this.setState({uploadsInProgress: draft.uploadsInProgress, serverError: err});
- } else {
- this.setState({serverError: err});
}
}
handleTextDrop(text) {
@@ -248,15 +248,15 @@ export default class CreatePost extends React.Component {
// id can either be the path of an uploaded file or the client id of an in progress upload
let index = previews.indexOf(id);
- if (index !== -1) {
- previews.splice(index, 1);
- } else {
+ if (index === -1) {
index = uploadsInProgress.indexOf(id);
if (index !== -1) {
uploadsInProgress.splice(index, 1);
this.refs.fileUpload.cancelUpload(id);
}
+ } else {
+ previews.splice(index, 1);
}
const draft = PostStore.getCurrentDraft();
diff --git a/web/react/components/delete_channel_modal.jsx b/web/react/components/delete_channel_modal.jsx
index 44c54db72..71c636921 100644
--- a/web/react/components/delete_channel_modal.jsx
+++ b/web/react/components/delete_channel_modal.jsx
@@ -11,6 +11,7 @@ export default class DeleteChannelModal extends React.Component {
super(props);
this.handleDelete = this.handleDelete.bind(this);
+ this.onShow = this.onShow.bind(this);
this.state = {
title: '',
@@ -32,14 +33,15 @@ export default class DeleteChannelModal extends React.Component {
}
);
}
+ onShow(e) {
+ var button = $(e.relatedTarget);
+ this.setState({
+ title: button.attr('data-title'),
+ channelId: button.attr('data-channelid')
+ });
+ }
componentDidMount() {
- $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function handleShow(e) {
- var button = $(e.relatedTarget);
- this.setState({
- title: button.attr('data-title'),
- channelId: button.attr('data-channelid')
- });
- }.bind(this));
+ $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
}
render() {
const channel = ChannelStore.getCurrent();
diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx
index 075f9c742..8e48a7a1c 100644
--- a/web/react/components/delete_post_modal.jsx
+++ b/web/react/components/delete_post_modal.jsx
@@ -16,6 +16,7 @@ export default class DeletePostModal extends React.Component {
this.handleDelete = this.handleDelete.bind(this);
this.onListenerChange = this.onListenerChange.bind(this);
+ this.onShow = this.onShow.bind(this);
this.state = {title: '', postId: '', channelId: '', selectedList: PostStore.getSelectedPost(), comments: 0};
}
@@ -60,18 +61,19 @@ export default class DeletePostModal extends React.Component {
}
);
}
+ onShow(e) {
+ var newState = {};
+ if (BrowserStore.getItem('edit_state_transfer')) {
+ newState = BrowserStore.getItem('edit_state_transfer');
+ BrowserStore.removeItem('edit_state_transfer');
+ } else {
+ var button = e.relatedTarget;
+ newState = {title: $(button).attr('data-title'), channelId: $(button).attr('data-channelid'), postId: $(button).attr('data-postid'), comments: $(button).attr('data-comments')};
+ }
+ this.setState(newState);
+ }
componentDidMount() {
- $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function freshOpen(e) {
- var newState = {};
- if (BrowserStore.getItem('edit_state_transfer')) {
- newState = BrowserStore.getItem('edit_state_transfer');
- BrowserStore.removeItem('edit_state_transfer');
- } else {
- var button = e.relatedTarget;
- newState = {title: $(button).attr('data-title'), channelId: $(button).attr('data-channelid'), postId: $(button).attr('data-postid'), comments: $(button).attr('data-comments')};
- }
- this.setState(newState);
- }.bind(this));
+ $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
PostStore.addSelectedPostChangeListener(this.onListenerChange);
}
componentWillUnmount() {
diff --git a/web/react/components/edit_channel_modal.jsx b/web/react/components/edit_channel_modal.jsx
index e93bab431..27219aba5 100644
--- a/web/react/components/edit_channel_modal.jsx
+++ b/web/react/components/edit_channel_modal.jsx
@@ -11,6 +11,7 @@ export default class EditChannelModal extends React.Component {
this.handleEdit = this.handleEdit.bind(this);
this.handleUserInput = this.handleUserInput.bind(this);
this.handleClose = this.handleClose.bind(this);
+ this.onShow = this.onShow.bind(this);
this.state = {
description: '',
@@ -50,11 +51,12 @@ export default class EditChannelModal extends React.Component {
handleClose() {
this.setState({description: '', serverError: ''});
}
+ onShow(e) {
+ const button = e.relatedTarget;
+ this.setState({description: $(button).attr('data-desc'), title: $(button).attr('data-title'), channelId: $(button).attr('data-channelid'), serverError: ''});
+ }
componentDidMount() {
- $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function handleShow(e) {
- const button = e.relatedTarget;
- this.setState({description: $(button).attr('data-desc'), title: $(button).attr('data-title'), channelId: $(button).attr('data-channelid'), serverError: ''});
- }.bind(this));
+ $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
$(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose);
}
componentWillUnmount() {
diff --git a/web/react/components/file_upload.jsx b/web/react/components/file_upload.jsx
index 3dc4e5de2..0e9297b7b 100644
--- a/web/react/components/file_upload.jsx
+++ b/web/react/components/file_upload.jsx
@@ -97,7 +97,7 @@ export default class FileUpload extends React.Component {
element[0].type = 'text';
element[0].type = 'file';
}
- } catch(e) {
+ } catch (e) {
// Do nothing
}
}
diff --git a/web/react/components/get_link_modal.jsx b/web/react/components/get_link_modal.jsx
index 5d8b13f00..1d4aac3e6 100644
--- a/web/react/components/get_link_modal.jsx
+++ b/web/react/components/get_link_modal.jsx
@@ -8,18 +8,22 @@ export default class GetLinkModal extends React.Component {
super(props);
this.handleClick = this.handleClick.bind(this);
+ this.onShow = this.onShow.bind(this);
+ this.onHide = this.onHide.bind(this);
this.state = {copiedLink: false};
}
+ onShow(e) {
+ var button = e.relatedTarget;
+ this.setState({title: $(button).attr('data-title'), value: $(button).attr('data-value')});
+ }
+ onHide() {
+ this.setState({copiedLink: false});
+ }
componentDidMount() {
if (this.refs.modal) {
- $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function show(e) {
- var button = e.relatedTarget;
- this.setState({title: $(button).attr('data-title'), value: $(button).attr('data-value')});
- }.bind(this));
- $(React.findDOMNode(this.refs.modal)).on('hide.bs.modal', function hide() {
- this.setState({copiedLink: false});
- }.bind(this));
+ $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
+ $(React.findDOMNode(this.refs.modal)).on('hide.bs.modal', this.onShow);
}
}
handleClick() {
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
index 8cc4f1483..70f7a5d6e 100644
--- a/web/react/components/login.jsx
+++ b/web/react/components/login.jsx
@@ -88,10 +88,10 @@ export default class Login extends React.Component {
let focusEmail = false;
let focusPassword = false;
- if (priorEmail !== '') {
- focusPassword = true;
- } else {
+ if (priorEmail === '') {
focusEmail = true;
+ } else {
+ focusPassword = true;
}
let loginMessage = [];
diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx
index 3a09e843d..ef7cec408 100644
--- a/web/react/components/mention.jsx
+++ b/web/react/components/mention.jsx
@@ -18,7 +18,9 @@ export default class Mention extends React.Component {
var timestamp = UserStore.getCurrentUser().update_at;
if (this.props.id === 'allmention' || this.props.id === 'channelmention') {
icon = <span><i className='mention-img fa fa-users fa-2x'></i></span>;
- } else if (this.props.id != null) {
+ } else if (this.props.id == null) {
+ icon = <span><i className='mention-img fa fa-users fa-2x'></i></span>;
+ } else {
icon = (
<span>
<img
@@ -27,8 +29,6 @@ export default class Mention extends React.Component {
/>
</span>
);
- } else {
- icon = <span><i className='mention-img fa fa-users fa-2x'></i></span>;
}
return (
<div
diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx
index 46a9d76ae..72f51013c 100644
--- a/web/react/components/mention_list.jsx
+++ b/web/react/components/mention_list.jsx
@@ -26,53 +26,64 @@ export default class MentionList extends React.Component {
this.addFirstMention = this.addFirstMention.bind(this);
this.isEmpty = this.isEmpty.bind(this);
this.scrollToMention = this.scrollToMention.bind(this);
+ this.onScroll = this.onScroll.bind(this);
+ this.onMentionListKey = this.onMentionListKey.bind(this);
+ this.onClick = this.onClick.bind(this);
this.state = {excludeUsers: [], mentionText: '-1', selectedMention: 0, selectedUsername: ''};
}
- componentDidMount() {
- PostStore.addMentionDataChangeListener(this.onListenerChange);
+ onScroll() {
+ if ($('.mentions--top').length) {
+ $('#reply_mention_tab .mentions--top').css({bottom: $(window).height() - $('.post-right__scroll #reply_textbox').offset().top});
+ }
+ }
+ onMentionListKey(e) {
+ if (!this.isEmpty() && this.state.mentionText !== '-1' && (e.which === 13 || e.which === 9)) {
+ e.stopPropagation();
+ e.preventDefault();
+ this.addCurrentMention();
+ } else if (!this.isEmpty() && this.state.mentionText !== '-1' && (e.which === 38 || e.which === 40)) {
+ e.stopPropagation();
+ e.preventDefault();
- $('.post-right__scroll').scroll(function onScroll() {
- if ($('.mentions--top').length) {
- $('#reply_mention_tab .mentions--top').css({bottom: $(window).height() - $('.post-right__scroll #reply_textbox').offset().top});
+ if (e.which === 38) {
+ if (this.getSelection(this.state.selectedMention - 1)) {
+ this.setState({selectedMention: this.state.selectedMention - 1, selectedUsername: this.refs['mention' + (this.state.selectedMention - 1)].props.username});
+ }
+ } else if (e.which === 40) {
+ if (this.getSelection(this.state.selectedMention + 1)) {
+ this.setState({selectedMention: this.state.selectedMention + 1, selectedUsername: this.refs['mention' + (this.state.selectedMention + 1)].props.username});
+ }
}
- });
- $('body').on('keydown.mentionlist', '#' + this.props.id,
- function onMentionListKey(e) {
- if (!this.isEmpty() && this.state.mentionText !== '-1' && (e.which === 13 || e.which === 9)) {
- e.stopPropagation();
- e.preventDefault();
- this.addCurrentMention();
- } else if (!this.isEmpty() && this.state.mentionText !== '-1' && (e.which === 38 || e.which === 40)) {
- e.stopPropagation();
- e.preventDefault();
+ this.scrollToMention(e.which);
+ }
+ }
+ onClick(e) {
+ if (!($('#' + this.props.id).is(e.target) || $('#' + this.props.id).has(e.target).length ||
+ ('mentionlist' in this.refs && $(React.findDOMNode(this.refs.mentionlist)).has(e.target).length))) {
+ this.setState({mentionText: '-1'});
+ }
+ }
+ componentDidMount() {
+ PostStore.addMentionDataChangeListener(this.onListenerChange);
- if (e.which === 38) {
- if (this.getSelection(this.state.selectedMention - 1)) {
- this.setState({selectedMention: this.state.selectedMention - 1, selectedUsername: this.refs['mention' + (this.state.selectedMention - 1)].props.username});
- }
- } else if (e.which === 40) {
- if (this.getSelection(this.state.selectedMention + 1)) {
- this.setState({selectedMention: this.state.selectedMention + 1, selectedUsername: this.refs['mention' + (this.state.selectedMention + 1)].props.username});
- }
- }
+ $('.post-right__scroll').scroll(this.onScroll);
- this.scrollToMention(e.which);
- }
- }.bind(this)
- );
- $(document).click(function onClick(e) {
- if (!($('#' + this.props.id).is(e.target) || $('#' + this.props.id).has(e.target).length ||
- ('mentionlist' in this.refs && $(React.findDOMNode(this.refs.mentionlist)).has(e.target).length))) {
- this.setState({mentionText: '-1'});
- }
- }.bind(this));
+ $('body').on('keydown.mentionlist', '#' + this.props.id, this.onMentionListKey);
+ $(document).click(this.onClick);
}
componentWillUnmount() {
PostStore.removeMentionDataChangeListener(this.onListenerChange);
$('body').off('keydown.mentionlist', '#' + this.props.id);
}
+
+ /*
+ * This component is poorly designed, nessesitating some state modification
+ * in the componentDidUpdate function. This is generally discouraged as it
+ * is a performance issue and breaks with good react design. This component
+ * should be redesigned.
+ */
componentDidUpdate() {
if (this.state.mentionText !== '-1') {
if (this.state.selectedUsername !== '' && (!this.getSelection(this.state.selectedMention) || this.state.selectedUsername !== this.refs['mention' + this.state.selectedMention].props.username)) {
@@ -80,17 +91,17 @@ export default class MentionList extends React.Component {
var foundMatch = false;
while (tempSelectedMention < this.state.selectedMention && this.getSelection(++tempSelectedMention)) {
if (this.state.selectedUsername === this.refs['mention' + tempSelectedMention].props.username) {
- this.setState({selectedMention: tempSelectedMention});
+ this.setState({selectedMention: tempSelectedMention}); //eslint-disable-line react/no-did-update-set-state
foundMatch = true;
break;
}
}
if (this.getSelection(0) && !foundMatch) {
- this.setState({selectedMention: 0, selectedUsername: this.refs.mention0.props.username});
+ this.setState({selectedMention: 0, selectedUsername: this.refs.mention0.props.username}); //eslint-disable-line react/no-did-update-set-state
}
}
} else if (this.state.selectedMention !== 0) {
- this.setState({selectedMention: 0, selectedUsername: ''});
+ this.setState({selectedMention: 0, selectedUsername: ''}); //eslint-disable-line react/no-did-update-set-state
}
}
onListenerChange(id, mentionText) {
@@ -124,10 +135,10 @@ export default class MentionList extends React.Component {
return true;
}
addCurrentMention() {
- if (!this.getSelection(this.state.selectedMention)) {
- this.addFirstMention();
- } else {
+ if (this.getSelection(this.state.selectedMention)) {
this.refs['mention' + this.state.selectedMention].handleClick();
+ } else {
+ this.addFirstMention();
}
}
addFirstMention() {
diff --git a/web/react/components/more_channels.jsx b/web/react/components/more_channels.jsx
index 65cd40975..487192d91 100644
--- a/web/react/components/more_channels.jsx
+++ b/web/react/components/more_channels.jsx
@@ -60,9 +60,7 @@ export default class MoreChannels extends React.Component {
this.setState({joiningChannel: -1});
}.bind(this),
function joinFail(err) {
- this.setState({joiningChannel: -1});
- this.state.serverError = err.message;
- this.setState(this.state);
+ this.setState({joiningChannel: -1, serverError: err.message});
}.bind(this)
);
}
@@ -81,56 +79,54 @@ export default class MoreChannels extends React.Component {
if (this.state.channels != null) {
var channels = this.state.channels;
- if (!channels.loading) {
- if (channels.length) {
- moreChannels = (
- <table className='more-channel-table table'>
- <tbody>
- {channels.map(function cMap(channel, index) {
- var joinButton;
- if (self.state.joiningChannel === index) {
- joinButton = (
- <img
- className='join-channel-loading-gif'
- src='/static/images/load.gif'
- />
- );
- } else {
- joinButton = (
- <button
- onClick={self.handleJoin.bind(self, channel, index)}
- className='btn btn-primary'
- >
- Join
- </button>
- );
- }
+ if (channels.loading) {
+ moreChannels = <LoadingScreen />;
+ } else if (channels.length) {
+ moreChannels = (
+ <table className='more-channel-table table'>
+ <tbody>
+ {channels.map(function cMap(channel, index) {
+ var joinButton;
+ if (self.state.joiningChannel === index) {
+ joinButton = (
+ <img
+ className='join-channel-loading-gif'
+ src='/static/images/load.gif'
+ />
+ );
+ } else {
+ joinButton = (
+ <button
+ onClick={self.handleJoin.bind(self, channel, index)}
+ className='btn btn-primary'
+ >
+ Join
+ </button>
+ );
+ }
- return (
- <tr key={channel.id}>
- <td>
- <p className='more-channel-name'>{channel.display_name}</p>
- <p className='more-channel-description'>{channel.description}</p>
- </td>
- <td className='td--action'>
- {joinButton}
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- );
- } else {
- moreChannels = (
- <div className='no-channel-message'>
- <p className='primary-message'>No more channels to join</p>
- <p className='secondary-message'>Click 'Create New Channel' to make a new one</p>
- </div>
- );
- }
+ return (
+ <tr key={channel.id}>
+ <td>
+ <p className='more-channel-name'>{channel.display_name}</p>
+ <p className='more-channel-description'>{channel.description}</p>
+ </td>
+ <td className='td--action'>
+ {joinButton}
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ );
} else {
- moreChannels = <LoadingScreen />;
+ moreChannels = (
+ <div className='no-channel-message'>
+ <p className='primary-message'>No more channels to join</p>
+ <p className='secondary-message'>Click 'Create New Channel' to make a new one</p>
+ </div>
+ );
}
}
diff --git a/web/react/components/more_direct_channels.jsx b/web/react/components/more_direct_channels.jsx
index 54d77c358..c71abd43a 100644
--- a/web/react/components/more_direct_channels.jsx
+++ b/web/react/components/more_direct_channels.jsx
@@ -31,22 +31,7 @@ export default class MoreDirectChannels extends React.Component {
var active = '';
var handleClick = null;
- if (!channel.fake) {
- if (channel.id === ChannelStore.getCurrentId()) {
- active = 'active';
- }
-
- if (channel.unread) {
- badge = <span className='badge pull-right small'>{channel.unread}</span>;
- titleClass = 'unread-title';
- }
-
- handleClick = function clickHandler(e) {
- e.preventDefault();
- utils.switchChannel(channel);
- $(React.findDOMNode(self.refs.modal)).modal('hide');
- };
- } else {
+ if (channel.fake) {
// It's a direct message channel that doesn't exist yet so let's create it now
var otherUserId = utils.getUserIdFromChannelName(channel);
@@ -78,6 +63,21 @@ export default class MoreDirectChannels extends React.Component {
);
};
}
+ } else {
+ if (channel.id === ChannelStore.getCurrentId()) {
+ active = 'active';
+ }
+
+ if (channel.unread) {
+ badge = <span className='badge pull-right small'>{channel.unread}</span>;
+ titleClass = 'unread-title';
+ }
+
+ handleClick = function clickHandler(e) {
+ e.preventDefault();
+ utils.switchChannel(channel);
+ $(React.findDOMNode(self.refs.modal)).modal('hide');
+ };
}
return (
diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx
index 57a78a0d4..78057d10b 100644
--- a/web/react/components/navbar_dropdown.jsx
+++ b/web/react/components/navbar_dropdown.jsx
@@ -9,7 +9,7 @@ var TeamStore = require('../stores/team_store.jsx');
var Constants = require('../utils/constants.jsx');
function getStateFromStores() {
- return {teams: UserStore.getTeams(), currentTeam: TeamStore.getCurrent()};
+ return {teams: UserStore.getTeams()};
}
export default class NavbarDropdown extends React.Component {
@@ -142,10 +142,10 @@ export default class NavbarDropdown extends React.Component {
>
</li>
);
- if (this.state.teams.length > 1 && this.state.currentTeam) {
- var curTeamName = this.state.currentTeam.name;
+
+ if (this.state.teams.length > 1) {
this.state.teams.forEach((teamName) => {
- if (teamName !== curTeamName) {
+ if (teamName !== this.props.teamName) {
teams.push(<li key={teamName}><a href={Utils.getWindowLocationOrigin() + '/' + teamName}>{'Switch to ' + teamName}</a></li>);
}
});
@@ -234,5 +234,7 @@ NavbarDropdown.defaultProps = {
teamType: ''
};
NavbarDropdown.propTypes = {
- teamType: React.PropTypes.string
+ teamType: React.PropTypes.string,
+ teamDisplayName: React.PropTypes.string,
+ teamName: React.PropTypes.string
};
diff --git a/web/react/components/popover_list_members.jsx b/web/react/components/popover_list_members.jsx
index 95a88c3d6..aaaea3c6f 100644
--- a/web/react/components/popover_list_members.jsx
+++ b/web/react/components/popover_list_members.jsx
@@ -67,9 +67,6 @@ export default class PopoverListMembers extends React.Component {
>
<div
id='member_tooltip'
- data-placement='left'
- data-toggle='tooltip'
- title='View Channel Members'
>
{countText}
<span
diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx
index 9127f00de..ac9c9252e 100644
--- a/web/react/components/post.jsx
+++ b/web/react/components/post.jsx
@@ -158,11 +158,18 @@ export default class Post extends React.Component {
var profilePic = null;
if (!this.props.hideProfilePic) {
+ let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp;
+ if (post.props && post.props.from_webhook && global.window.config.EnablePostIconOverride === 'true') {
+ if (post.props.override_icon_url) {
+ src = post.props.override_icon_url;
+ }
+ }
+
profilePic = (
<div className='post-profile-img__container'>
<img
className='post-profile-img'
- src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp}
+ src={src}
height='36'
width='36'
/>
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index 48b268700..6cfd243de 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -125,7 +125,7 @@ export default class PostBody extends React.Component {
url: 'https://www.googleapis.com/youtube/v3/videos',
type: 'GET',
data: {part: 'snippet', id: youtubeId, key: global.window.config.GoogleDeveloperKey},
- success
+ success: success.bind(this)
});
}
diff --git a/web/react/components/post_header.jsx b/web/react/components/post_header.jsx
index 9dc525e03..dd79b3e36 100644
--- a/web/react/components/post_header.jsx
+++ b/web/react/components/post_header.jsx
@@ -12,9 +12,27 @@ export default class PostHeader extends React.Component {
render() {
var post = this.props.post;
+ let userProfile = <UserProfile userId={post.user_id} />;
+ let botIndicator;
+
+ if (post.props && post.props.from_webhook) {
+ if (post.props.override_username && global.window.config.EnablePostUsernameOverride === 'true') {
+ userProfile = (
+ <UserProfile
+ userId={post.user_id}
+ overwriteName={post.props.override_username}
+ disablePopover={true}
+ />
+ );
+ }
+
+ botIndicator = <li className='post-header-col post-header__name bot-indicator'>{'BOT'}</li>;
+ }
+
return (
<ul className='post-header post-header-post'>
- <li className='post-header-col post-header__name'><strong><UserProfile userId={post.user_id} /></strong></li>
+ <li className='post-header-col post-header__name'><strong>{userProfile}</strong></li>
+ {botIndicator}
<li className='post-info--hidden'>
<PostInfo
post={post}
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx
index a31967257..6741a9bdd 100644
--- a/web/react/components/post_list.jsx
+++ b/web/react/components/post_list.jsx
@@ -37,9 +37,11 @@ export default class PostList extends React.Component {
this.deactivate = this.deactivate.bind(this);
this.resize = this.resize.bind(this);
- this.state = this.getStateFromStores(props.channelId);
- this.state.numToDisplay = Constants.POST_CHUNK_SIZE;
- this.state.isFirstLoadComplete = false;
+ const state = this.getStateFromStores(props.channelId);
+ state.numToDisplay = Constants.POST_CHUNK_SIZE;
+ state.isFirstLoadComplete = false;
+
+ this.state = state;
}
getStateFromStores(id) {
var postList = PostStore.getPosts(id);
@@ -449,10 +451,10 @@ export default class PostList extends React.Component {
}
var createMessage;
- if (creatorName !== '') {
- createMessage = (<span>This is the start of the <strong>{uiName}</strong> {uiType}, created by <strong>{creatorName}</strong> on <strong>{utils.displayDate(channel.create_at)}</strong></span>);
- } else {
+ if (creatorName === '') {
createMessage = 'This is the start of the ' + uiName + ' ' + uiType + ', created on ' + utils.displayDate(channel.create_at) + '.';
+ } else {
+ createMessage = (<span>This is the start of the <strong>{uiName}</strong> {uiType}, created by <strong>{creatorName}</strong> on <strong>{utils.displayDate(channel.create_at)}</strong></span>);
}
return (
@@ -516,8 +518,19 @@ export default class PostList extends React.Component {
sameRoot = utils.isComment(post) && (prevPost.id === post.root_id || prevPost.root_id === post.root_id);
- // we only hide the profile pic if the previous post is not a comment, the current post is not a comment, and the previous post was made by the same user as the current post
- hideProfilePic = (prevPost.user_id === post.user_id) && !utils.isComment(prevPost) && !utils.isComment(post);
+ // hide the profile pic if:
+ // the previous post was made by the same user as the current post,
+ // the previous post is not a comment,
+ // the current post is not a comment,
+ // the current post is not from a webhook
+ // and the previous post is not from a webhook
+ if ((prevPost.user_id === post.user_id) &&
+ !utils.isComment(prevPost) &&
+ !utils.isComment(post) &&
+ (!post.props || !post.props.from_webhook) &&
+ (!prevPost.props || !prevPost.props.from_webhook)) {
+ hideProfilePic = true;
+ }
}
// check if it's the last comment in a consecutive string of comments on the same post
diff --git a/web/react/components/rename_channel_modal.jsx b/web/react/components/rename_channel_modal.jsx
index 9d514c741..d60206ecf 100644
--- a/web/react/components/rename_channel_modal.jsx
+++ b/web/react/components/rename_channel_modal.jsx
@@ -15,6 +15,7 @@ export default class RenameChannelModal extends React.Component {
this.onDisplayNameChange = this.onDisplayNameChange.bind(this);
this.displayNameKeyUp = this.displayNameKeyUp.bind(this);
this.handleClose = this.handleClose.bind(this);
+ this.handleShow = this.handleShow.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
@@ -59,11 +60,11 @@ export default class RenameChannelModal extends React.Component {
state.invalid = true;
} else {
let cleanedName = Utils.cleanUpUrlable(channel.name);
- if (cleanedName !== channel.name) {
+ if (cleanedName === channel.name) {
+ state.nameError = '';
+ } else {
state.nameError = 'Must be lowercase alphanumeric characters';
state.invalid = true;
- } else {
- state.nameError = '';
}
}
@@ -103,7 +104,7 @@ export default class RenameChannelModal extends React.Component {
this.setState({channelName: channelName});
}
handleClose() {
- this.state = {
+ this.setState({
displayName: '',
channelName: '',
channelId: '',
@@ -111,13 +112,14 @@ export default class RenameChannelModal extends React.Component {
nameError: '',
displayNameError: '',
invalid: false
- };
+ });
+ }
+ handleShow(e) {
+ const button = $(e.relatedTarget);
+ this.setState({displayName: button.attr('data-display'), channelName: button.attr('data-name'), channelId: button.attr('data-channelid')});
}
componentDidMount() {
- $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function handleShow(e) {
- const button = $(e.relatedTarget);
- this.setState({displayName: button.attr('data-display'), channelName: button.attr('data-name'), channelId: button.attr('data-channelid')});
- }.bind(this));
+ $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.handleShow);
$(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose);
}
componentWillUnmount() {
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
index 6d4b56b7b..b696f4b53 100644
--- a/web/react/components/sidebar.jsx
+++ b/web/react/components/sidebar.jsx
@@ -29,9 +29,11 @@ export default class Sidebar extends React.Component {
this.updateUnreadIndicators = this.updateUnreadIndicators.bind(this);
this.createChannelElement = this.createChannelElement.bind(this);
- this.state = this.getStateFromStores();
- this.state.modal = '';
- this.state.loadingDMChannel = -1;
+ const state = this.getStateFromStores();
+ state.modal = '';
+ state.loadingDMChannel = -1;
+
+ this.state = state;
}
getStateFromStores() {
var members = ChannelStore.getAllMembers();
@@ -65,7 +67,18 @@ export default class Sidebar extends React.Component {
var channel = ChannelStore.getByName(channelName);
- if (channel != null) {
+ if (channel == null) {
+ var tempChannel = {};
+ tempChannel.fake = true;
+ tempChannel.name = channelName;
+ 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;
+ tempChannel.type = 'D';
+ readDirectChannels.push(tempChannel);
+ } else {
channel.display_name = teammate.username;
channel.teammate_username = teammate.username;
@@ -80,17 +93,6 @@ export default class Sidebar extends React.Component {
} else {
readDirectChannels.push(channel);
}
- } else {
- var tempChannel = {};
- tempChannel.fake = true;
- tempChannel.name = channelName;
- 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;
- tempChannel.type = 'D';
- readDirectChannels.push(tempChannel);
}
}
@@ -512,6 +514,7 @@ export default class Sidebar extends React.Component {
/>
<SidebarHeader
teamDisplayName={this.props.teamDisplayName}
+ teamName={this.props.teamName}
teamType={this.props.teamType}
/>
<SearchBox />
@@ -591,5 +594,6 @@ Sidebar.defaultProps = {
};
Sidebar.propTypes = {
teamType: React.PropTypes.string,
- teamDisplayName: React.PropTypes.string
+ teamDisplayName: React.PropTypes.string,
+ teamName: React.PropTypes.string
};
diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
index 072c14e0a..33de35064 100644
--- a/web/react/components/sidebar_header.jsx
+++ b/web/react/components/sidebar_header.jsx
@@ -52,6 +52,8 @@ export default class SidebarHeader extends React.Component {
<NavbarDropdown
ref='dropdown'
teamType={this.props.teamType}
+ teamDisplayName={this.props.teamDisplayName}
+ teamName={this.props.teamName}
/>
</div>
);
@@ -64,5 +66,6 @@ SidebarHeader.defaultProps = {
};
SidebarHeader.propTypes = {
teamDisplayName: React.PropTypes.string,
+ teamName: React.PropTypes.string,
teamType: React.PropTypes.string
};
diff --git a/web/react/components/sidebar_right.jsx b/web/react/components/sidebar_right.jsx
index 708cd04cb..573515a46 100644
--- a/web/react/components/sidebar_right.jsx
+++ b/web/react/components/sidebar_right.jsx
@@ -30,11 +30,11 @@ export default class SidebarRight extends React.Component {
PostStore.removeSelectedPostChangeListener(this.onSelectedChange);
}
componentDidUpdate() {
- if (!this.plScrolledToBottom) {
- $('.top-visible-post')[0].scrollIntoView();
- } else {
+ if (this.plScrolledToBottom) {
var postHolder = $('.post-list-holder-by-time').not('.inactive');
postHolder.scrollTop(postHolder[0].scrollHeight);
+ } else {
+ $('.top-visible-post')[0].scrollIntoView();
}
}
onSelectedChange(fromSearch) {
diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx
index 495159efc..ae3075495 100644
--- a/web/react/components/signup_user_complete.jsx
+++ b/web/react/components/signup_user_complete.jsx
@@ -41,15 +41,13 @@ export default class SignupUserComplete extends React.Component {
return;
}
- this.state.user.email = providedEmail;
-
- this.state.user.username = React.findDOMNode(this.refs.name).value.trim().toLowerCase();
- if (!this.state.user.username) {
+ const providedUsername = React.findDOMNode(this.refs.name).value.trim().toLowerCase();
+ if (!providedUsername) {
this.setState({nameError: 'This field is required', emailError: '', passwordError: '', serverError: ''});
return;
}
- var usernameError = Utils.isValidUsername(this.state.user.username);
+ const usernameError = Utils.isValidUsername(this.state.user.username);
if (usernameError === 'Cannot use a reserved word as a username.') {
this.setState({nameError: 'This username is reserved, please choose a new one.', emailError: '', passwordError: '', serverError: ''});
return;
@@ -63,15 +61,24 @@ export default class SignupUserComplete extends React.Component {
return;
}
- this.state.user.password = React.findDOMNode(this.refs.password).value.trim();
- if (!this.state.user.password || this.state.user.password .length < 5) {
+ const providedPassword = React.findDOMNode(this.refs.password).value.trim();
+ if (!providedPassword || providedPassword.length < 5) {
this.setState({nameError: '', emailError: '', passwordError: 'Please enter at least 5 characters', serverError: ''});
return;
}
- this.setState({nameError: '', emailError: '', passwordError: '', serverError: ''});
-
- this.state.user.allow_marketing = true;
+ this.setState({
+ user: {
+ email: providedEmail,
+ username: providedUsername,
+ password: providedPassword,
+ allow_marketing: true
+ },
+ nameError: '',
+ emailError: '',
+ passwordError: '',
+ serverError: ''
+ });
client.createUser(this.state.user, this.state.data, this.state.hash,
function createUserSuccess() {
diff --git a/web/react/components/team_signup_email_item.jsx b/web/react/components/team_signup_email_item.jsx
index 10bb2d69e..01330a46c 100644
--- a/web/react/components/team_signup_email_item.jsx
+++ b/web/react/components/team_signup_email_item.jsx
@@ -23,17 +23,14 @@ export default class TeamSignupEmailItem extends React.Component {
}
if (!Utils.isEmail(email)) {
- this.state.emailError = 'Please enter a valid email address';
- this.setState(this.state);
+ this.setState({emailError: 'Please enter a valid email address'});
return false;
} else if (email === teamEmail) {
- this.state.emailError = 'Please use a different email than the one used at signup';
- this.setState(this.state);
+ this.setState({emailError: 'Please use a different email than the one used at signup'});
return false;
}
- this.state.emailError = '';
- this.setState(this.state);
+ this.setState({emailError: ''});
return true;
}
render() {
diff --git a/web/react/components/team_signup_send_invites_page.jsx b/web/react/components/team_signup_send_invites_page.jsx
index 524bd5b50..8d8fb92ff 100644
--- a/web/react/components/team_signup_send_invites_page.jsx
+++ b/web/react/components/team_signup_send_invites_page.jsx
@@ -36,10 +36,10 @@ export default class TeamSignupSendInvitesPage extends React.Component {
var emails = [];
for (var i = 0; i < this.props.state.invites.length; i++) {
- if (!this.refs['email_' + i].validate(this.props.state.team.email)) {
- valid = false;
- } else {
+ if (this.refs['email_' + i].validate(this.props.state.team.email)) {
emails.push(this.refs['email_' + i].getValue());
+ } else {
+ valid = false;
}
}
diff --git a/web/react/components/team_signup_url_page.jsx b/web/react/components/team_signup_url_page.jsx
index a3f89a217..a682bb49e 100644
--- a/web/react/components/team_signup_url_page.jsx
+++ b/web/react/components/team_signup_url_page.jsx
@@ -48,22 +48,20 @@ export default class TeamSignupUrlPage extends React.Component {
}
Client.findTeamByName(name,
- function success(data) {
- if (!data) {
- this.props.state.wizard = 'send_invites';
- this.props.state.team.type = 'O';
-
- this.props.state.team.name = name;
- this.props.updateParent(this.props.state);
- } else {
- this.state.nameError = 'This URL is unavailable. Please try another.';
- this.setState(this.state);
- }
- }.bind(this),
- function error(err) {
- this.state.nameError = err.message;
- this.setState(this.state);
- }.bind(this)
+ (data) => {
+ if (data) {
+ this.setState({nameError: 'This URL is unavailable. Please try another.'});
+ } else {
+ this.props.state.wizard = 'send_invites';
+ this.props.state.team.type = 'O';
+
+ this.props.state.team.name = name;
+ this.props.updateParent(this.props.state);
+ }
+ },
+ (err) => {
+ this.setState({nameError: err.message});
+ }
);
}
handleFocus(e) {
diff --git a/web/react/components/team_signup_welcome_page.jsx b/web/react/components/team_signup_welcome_page.jsx
index 626c6a17b..019456c9f 100644
--- a/web/react/components/team_signup_welcome_page.jsx
+++ b/web/react/components/team_signup_welcome_page.jsx
@@ -59,8 +59,7 @@ export default class TeamSignupWelcomePage extends React.Component {
}
}.bind(this),
function error(err) {
- this.state.serverError = err.message;
- this.setState(this.state);
+ this.setState({serverError: err.message});
}.bind(this)
);
}
diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx
index c5d028d31..ceb8f52a7 100644
--- a/web/react/components/user_profile.jsx
+++ b/web/react/components/user_profile.jsx
@@ -31,8 +31,10 @@ export default class UserProfile extends React.Component {
}
componentDidMount() {
UserStore.addChangeListener(this.onChange);
- $('#profile_' + this.uniqueId).popover({placement: 'right', container: 'body', trigger: 'hover', html: true, delay: {show: 200, hide: 100}});
- $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'});
+ if (!this.props.disablePopover) {
+ $('#profile_' + this.uniqueId).popover({placement: 'right', container: 'body', trigger: 'hover', html: true, delay: {show: 200, hide: 100}});
+ $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'});
+ }
}
componentWillUnmount() {
UserStore.removeChangeListener(this.onChange);
@@ -56,6 +58,10 @@ export default class UserProfile extends React.Component {
name = this.props.overwriteName;
}
+ if (this.props.disablePopover) {
+ return <div>{name}</div>;
+ }
+
var dataContent = '<img class="user-popover__image" src="/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at + '" height="128" width="128" />';
if (!global.window.config.ShowEmailAddress === 'true') {
dataContent += '<div class="text-nowrap">Email not shared</div>';
@@ -79,9 +85,11 @@ export default class UserProfile extends React.Component {
UserProfile.defaultProps = {
userId: '',
- overwriteName: ''
+ overwriteName: '',
+ disablePopover: false
};
UserProfile.propTypes = {
userId: React.PropTypes.string,
- overwriteName: React.PropTypes.string
+ overwriteName: React.PropTypes.string,
+ disablePopover: React.PropTypes.bool
};
diff --git a/web/react/components/user_settings/import_theme_modal.jsx b/web/react/components/user_settings/import_theme_modal.jsx
index 0f5cb59f5..4e8ee03fa 100644
--- a/web/react/components/user_settings/import_theme_modal.jsx
+++ b/web/react/components/user_settings/import_theme_modal.jsx
@@ -150,7 +150,9 @@ export default class ImportThemeModal extends React.Component {
className='form-control'
onChange={this.handleChange}
/>
- {this.state.inputError}
+ <div className='input__help'>
+ {this.state.inputError}
+ </div>
</div>
</div>
</Modal.Body>
diff --git a/web/react/components/user_settings/premade_theme_chooser.jsx b/web/react/components/user_settings/premade_theme_chooser.jsx
index f8f916bd0..8116bffcc 100644
--- a/web/react/components/user_settings/premade_theme_chooser.jsx
+++ b/web/react/components/user_settings/premade_theme_chooser.jsx
@@ -24,7 +24,7 @@ export default class PremadeThemeChooser extends React.Component {
premadeThemes.push(
<div
- className='col-sm-3 premade-themes'
+ className='col-xs-6 col-sm-3 premade-themes'
key={'premade-theme-key' + k}
>
<div
diff --git a/web/react/package.json b/web/react/package.json
index a9eba6c6c..31295873b 100644
--- a/web/react/package.json
+++ b/web/react/package.json
@@ -19,8 +19,8 @@
"babelify": "6.1.3",
"uglify-js": "2.4.24",
"watchify": "3.3.1",
- "eslint": "1.3.1",
- "eslint-plugin-react": "3.3.1"
+ "eslint": "1.6.0",
+ "eslint-plugin-react": "3.5.1"
},
"scripts": {
"start": "watchify --extension=jsx -o ../static/js/bundle.js -v -d ./**/*.jsx",
diff --git a/web/react/pages/authorize.jsx b/web/react/pages/authorize.jsx
index db42c8266..8ea8b13eb 100644
--- a/web/react/pages/authorize.jsx
+++ b/web/react/pages/authorize.jsx
@@ -3,16 +3,16 @@
var Authorize = require('../components/authorize.jsx');
-function setupAuthorizePage(teamName, appName, responseType, clientId, redirectUri, scope, state) {
+function setupAuthorizePage(props) {
React.render(
<Authorize
- teamName={teamName}
- appName={appName}
- responseType={responseType}
- clientId={clientId}
- redirectUri={redirectUri}
- scope={scope}
- state={state}
+ teamName={props.TeamName}
+ appName={props.AppName}
+ responseType={props.ResponseType}
+ clientId={props.ClientId}
+ redirectUri={props.RedirectUri}
+ scope={props.Scope}
+ state={props.State}
/>,
document.getElementById('authorize')
);
diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx
index 74259194a..c333fd57d 100644
--- a/web/react/pages/channel.jsx
+++ b/web/react/pages/channel.jsx
@@ -36,11 +36,14 @@ var RemovedFromChannelModal = require('../components/removed_from_channel_modal.
var FileUploadOverlay = require('../components/file_upload_overlay.jsx');
var RegisterAppModal = require('../components/register_app_modal.jsx');
var ImportThemeModal = require('../components/user_settings/import_theme_modal.jsx');
+var TeamStore = require('../stores/team_store.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
function setupChannelPage(props) {
+ TeamStore.setCurrentId(props.TeamId);
+
AppDispatcher.handleViewAction({
type: ActionTypes.CLICK_CHANNEL,
name: props.ChannelName,
@@ -71,6 +74,7 @@ function setupChannelPage(props) {
React.render(
<Sidebar
teamDisplayName={props.TeamDisplayName}
+ teamName={props.TeamName}
teamType={props.TeamType}
/>,
document.getElementById('sidebar-left')
diff --git a/web/react/stores/browser_store.jsx b/web/react/stores/browser_store.jsx
index d2dedb271..e45d3d981 100644
--- a/web/react/stores/browser_store.jsx
+++ b/web/react/stores/browser_store.jsx
@@ -4,7 +4,7 @@
var UserStore;
function getPrefix() {
if (!UserStore) {
- UserStore = require('./user_store.jsx');
+ UserStore = require('./user_store.jsx'); //eslint-disable-line global-require
}
return UserStore.getCurrentId() + '_';
}
diff --git a/web/react/stores/team_store.jsx b/web/react/stores/team_store.jsx
index 1f33fe03b..fd9117747 100644
--- a/web/react/stores/team_store.jsx
+++ b/web/react/stores/team_store.jsx
@@ -10,12 +10,12 @@ var BrowserStore = require('../stores/browser_store.jsx');
var CHANGE_EVENT = 'change';
-var utils;
+var Utils;
function getWindowLocationOrigin() {
- if (!utils) {
- utils = require('../utils/utils.jsx');
+ if (!Utils) {
+ Utils = require('../utils/utils.jsx'); //eslint-disable-line global-require
}
- return utils.getWindowLocationOrigin();
+ return Utils.getWindowLocationOrigin();
}
class TeamStoreClass extends EventEmitter {
diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx
index 5cb165b4c..d9f486009 100644
--- a/web/react/utils/client.jsx
+++ b/web/react/utils/client.jsx
@@ -15,7 +15,7 @@ function handleError(methodName, xhr, status, err) {
var e = null;
try {
e = JSON.parse(xhr.responseText);
- } catch(parseError) {
+ } catch (parseError) {
e = null;
}
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index da59f8e5a..67414dc3b 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -179,7 +179,7 @@ module.exports = {
centerChannelColor: '#DDDDDD',
newMessageSeparator: '#5de5da',
linkColor: '#A4FFEB',
- buttonBg: '#1dacfc',
+ buttonBg: '#4CBBA4',
buttonColor: '#FFFFFF'
},
windows10: {
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 8b20e2adf..307a311ab 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -231,10 +231,10 @@ function testUrlMatch(text) {
var matchText = match.getMatchedText();
linkData.text = matchText;
- if (matchText.trim().indexOf('http') !== 0) {
- linkData.link = 'http://' + matchText;
- } else {
+ if (matchText.trim().indexOf('http') === 0) {
linkData.link = matchText;
+ } else {
+ linkData.link = 'http://' + matchText;
}
result.push(linkData);
@@ -395,35 +395,39 @@ export function toTitleCase(str) {
export function applyTheme(theme) {
if (theme.sidebarBg) {
- changeCss('.sidebar--left', 'background:' + theme.sidebarBg, 1);
+ changeCss('.sidebar--left, .settings-modal .settings-table .settings-links, .sidebar--menu', 'background:' + theme.sidebarBg, 1);
}
if (theme.sidebarText) {
- changeCss('.sidebar--left .nav li>a, .sidebar--right', 'color:' + theme.sidebarText, 1);
- changeCss('.sidebar--left .nav li>h4, .sidebar--left .add-channel-btn', 'color:' + changeOpacity(theme.sidebarText, 0.8), 1);
+ changeCss('.sidebar--left .nav-pills__container li>a, .sidebar--right, .settings-modal .nav-pills>li a, .sidebar--menu', 'color:' + theme.sidebarText, 1);
+ changeCss('@media(max-width: 768px){.settings-modal .settings-table .nav>li>a', 'color:' + theme.sidebarText, 1);
+ changeCss('.sidebar--left .nav-pills__container li>h4, .sidebar--left .add-channel-btn', 'color:' + changeOpacity(theme.sidebarText, 0.8), 1);
changeCss('.sidebar--left .add-channel-btn:hover, .sidebar--left .add-channel-btn:focus', 'color:' + theme.sidebarText, 1);
changeCss('.sidebar--left, .sidebar--right .sidebar--right__header', 'border-color:' + changeOpacity(theme.sidebarText, 0.2), 1);
changeCss('.sidebar--left .status path', 'fill:' + changeOpacity(theme.sidebarText, 0.5), 1);
+ changeCss('@media(max-width: 768px){.settings-modal .settings-table .nav>li>a', 'border-color:' + changeOpacity(theme.sidebarText, 0.2), 2);
}
if (theme.sidebarUnreadText) {
- changeCss('.sidebar--left .nav li>a.unread-title', 'color:' + theme.sidebarUnreadText + '!important;', 1);
+ changeCss('.sidebar--left .nav-pills__container li>a.unread-title', 'color:' + theme.sidebarUnreadText + '!important;', 2);
}
if (theme.sidebarTextHoverBg) {
- changeCss('.sidebar--left .nav li>a:hover, .sidebar--left .nav li>a:focus', 'background:' + theme.sidebarTextHoverBg, 1);
+ changeCss('.sidebar--left .nav-pills__container li>a:hover, .sidebar--left .nav-pills__container li>a:focus, .settings-modal .nav-pills>li:hover a, .settings-modal .nav-pills>li:focus a', 'background:' + theme.sidebarTextHoverBg, 1);
+ changeCss('@media(max-width: 768px){.settings-modal .settings-table .nav>li:hover a', 'background:' + theme.sidebarTextHoverBg, 1);
}
if (theme.sidebarTextHoverColor) {
- changeCss('.sidebar--left .nav li>a:hover, .sidebar--left .nav li>a:focus', 'color:' + theme.sidebarTextHoverColor, 2);
+ changeCss('.sidebar--left .nav-pills__container li>a:hover, .sidebar--left .nav-pills__container li>a:focus, .settings-modal .nav-pills>li:hover a, .settings-modal .nav-pills>li:focus a', 'color:' + theme.sidebarTextHoverColor, 2);
+ changeCss('@media(max-width: 768px){.settings-modal .settings-table .nav>li:hover a', 'color:' + theme.sidebarTextHoverColor, 2);
}
if (theme.sidebarTextActiveBg) {
- changeCss('.sidebar--left .nav li.active a, .sidebar--left .nav li.active a:hover, .sidebar--left .nav li.active a:focus', 'background:' + theme.sidebarTextActiveBg, 1);
+ changeCss('.sidebar--left .nav-pills__container li.active a, .sidebar--left .nav-pills__container li.active a:hover, .sidebar--left .nav-pills__container li.active a:focus, .settings-modal .nav-pills>li.active a, .settings-modal .nav-pills>li.active a:hover, .settings-modal .nav-pills>li.active a:active', 'background:' + theme.sidebarTextActiveBg, 1);
}
if (theme.sidebarTextActiveColor) {
- changeCss('.sidebar--left .nav li.active a, .sidebar--left .nav li.active a:hover, .sidebar--left .nav li.active a:focus', 'color:' + theme.sidebarTextActiveColor, 2);
+ changeCss('.sidebar--left .nav-pills__container li.active a, .sidebar--left .nav-pills__container li.active a:hover, .sidebar--left .nav-pills__container li.active a:focus, .settings-modal .nav-pills>li.active a, .settings-modal .nav-pills>li.active a:hover, .settings-modal .nav-pills>li.active a:active', 'color:' + theme.sidebarTextActiveColor, 2);
}
if (theme.sidebarHeaderBg) {
@@ -458,45 +462,51 @@ export function applyTheme(theme) {
}
if (theme.centerChannelBg) {
- changeCss('.app__content, .markdown__table, .markdown__table tbody tr, .command-box', 'background:' + theme.centerChannelBg, 1);
+ changeCss('.app__content, .markdown__table, .markdown__table tbody tr, .command-box, .modal .modal-content, .mentions-name, .mentions--top .mentions-box', 'background:' + theme.centerChannelBg, 1);
changeCss('#post-list .post-list-holder-by-time', 'background:' + theme.centerChannelBg, 1);
changeCss('#post-create', 'background:' + theme.centerChannelBg, 1);
changeCss('.date-separator .separator__text, .new-separator .separator__text', 'background:' + theme.centerChannelBg, 1);
changeCss('.post-image__column .post-image__details', 'background:' + theme.centerChannelBg, 1);
- changeCss('.sidebar--right', 'background:' + theme.centerChannelBg, 1);
+ changeCss('.sidebar--right, .dropdown-menu, .popover', 'background:' + theme.centerChannelBg, 1);
}
if (theme.centerChannelColor) {
- changeCss('.app__content, .post-create__container .post-create-body .btn-file, .post-create__container .post-create-footer .msg-typing, .loading-screen .loading__content .round, .command-name', 'color:' + theme.centerChannelColor, 1);
+ changeCss('.app__content, .post-create__container .post-create-body .btn-file, .post-create__container .post-create-footer .msg-typing, .command-name, .modal .modal-content, .dropdown-menu, .popover, .mentions-name', 'color:' + theme.centerChannelColor, 1);
changeCss('#post-create', 'color:' + theme.centerChannelColor, 2);
changeCss('.mentions--top, .command-box', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 3);
changeCss('.mentions--top, .command-box', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 2);
changeCss('.mentions--top, .command-box', '-moz-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 1);
- changeCss('.post-body hr', 'background:' + theme.centerChannelColor, 1);
+ changeCss('.dropdown-menu, .popover ', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 3);
+ changeCss('.dropdown-menu, .popover ', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 2);
+ changeCss('.dropdown-menu, .popover ', '-moz-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 1);
+ changeCss('.post-body hr, .loading-screen .loading__content .round', 'background:' + theme.centerChannelColor, 1);
changeCss('.channel-header .heading', 'color:' + theme.centerChannelColor, 1);
changeCss('.markdown__table tbody tr:nth-child(2n)', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
changeCss('.channel-header__info>div.dropdown .header-dropdown__icon', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1);
changeCss('.channel-header #member_popover', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1);
- changeCss('.custom-textarea, .custom-textarea:focus, .preview-container .preview-div, .post-image__column .post-image__details, .sidebar--right .sidebar-right__body, .markdown__table th, .markdown__table td, .command-box', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
- changeCss('.command-name', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
+ changeCss('.custom-textarea, .custom-textarea:focus, .preview-container .preview-div, .post-image__column .post-image__details, .sidebar--right .sidebar-right__body, .markdown__table th, .markdown__table td, .command-box, .modal .modal-content, .settings-modal .settings-table .settings-content .divider-light, .dropdown-menu, .modal .modal-header, .popover, .mentions--top .mentions-box', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
+ changeCss('.command-name, .popover .popover-title', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
+ changeCss('.dropdown-menu .divider', 'background:' + theme.centerChannelColor, 1);
changeCss('.custom-textarea', 'color:' + theme.centerChannelColor, 1);
changeCss('.post-image__column', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 2);
changeCss('.post-image__column .post-image__details', 'color:' + theme.centerChannelColor, 2);
changeCss('.post-image__column a, .post-image__column a:hover, .post-image__column a:focus', 'color:' + theme.centerChannelColor, 1);
- changeCss('@media(max-width: 768px){.search-bar__container .search__form .search-bar', 'background:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
+ changeCss('.search-bar__container .search__form .search-bar', 'background: transparent; color:' + theme.centerChannelColor, 1);
+ changeCss('@media(max-width: 768px){.search-bar__container .search__form .search-bar', 'background:' + changeOpacity(theme.centerChannelColor, 0.2) + '; color: inherit;', 1);
changeCss('.search-bar__container .search__form', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
changeCss('.channel-intro .channel-intro__content', 'background:' + changeOpacity(theme.centerChannelColor, 0.05), 1);
changeCss('.date-separator .separator__text', 'color:' + theme.centerChannelColor, 2);
- changeCss('.date-separator .separator__hr, .post-right__container .post.post--root hr, .search-item-container', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
- changeCss('.channel-intro', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
+ changeCss('.date-separator .separator__hr, .modal-footer, .modal .custom-textarea, .post-right__container .post.post--root hr, .search-item-container', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
+ changeCss('.modal .custom-textarea:focus', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1);
+ changeCss('.channel-intro, .settings-modal .settings-table .settings-content .divider-dark, hr, .settings-modal .settings-table .settings-links', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
changeCss('.post.current--user .post-body, .post.post--comment.other--root.current--user .post-comment', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
changeCss('.post.current--user .post-body, .post.post--comment.other--root.current--user .post-comment, .post.post--comment.other--root .post-comment, .post.same--root .post-body', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2);
changeCss('@media(max-width: 1440px){.post.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2);
changeCss('@media(max-width: 1440px){.post.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2);
changeCss('@media(max-width: 1800px){.inner__wrap.move--left .post.post--comment.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2);
- changeCss('.post:hover, .sidebar--right .sidebar--right__header', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
- changeCss('.date-separator.hovered--before:after, .new-separator.hovered--before:after', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
- changeCss('.date-separator.hovered--after:before, .new-separator.hovered--after:before, .command-name:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
+ changeCss('.post:hover, .sidebar--right .sidebar--right__header, .settings-modal .settings-table .settings-content .section-min:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
+ changeCss('.date-separator.hovered--before:after, .date-separator.hovered--after:before, .new-separator.hovered--after:before, .new-separator.hovered--before:after', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
+ changeCss('.command-name:hover, .mentions-name:hover, .mentions-focus, .dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1);
changeCss('.post.current--user:hover .post-body ', 'background: none;', 1);
changeCss('.sidebar--right', 'color:' + theme.centerChannelColor, 2);
}
@@ -507,7 +517,7 @@ export function applyTheme(theme) {
}
if (theme.linkColor) {
- changeCss('a, a:focus, a:hover', 'color:' + theme.linkColor, 1);
+ changeCss('a, a:focus, a:hover, .btn, .btn:focus, .btn:hover', 'color:' + theme.linkColor, 1);
changeCss('.post .comment-icon__container', 'fill:' + theme.linkColor, 1);
}
@@ -630,7 +640,7 @@ export function isValidUsername(name) {
error = 'Must be between 3 and 15 characters';
} else if (!(/^[a-z0-9\.\-\_]+$/).test(name)) {
error = "Must contain only letters, numbers, and the symbols '.', '-', and '_'.";
- } else if (!(/[a-z]/).test(name.charAt(0))) {
+ } else if (!(/[a-z]/).test(name.charAt(0))) { //eslint-disable-line no-negated-condition
error = 'First character must be a letter.';
} else {
for (var i = 0; i < Constants.RESERVED_USERNAMES.length; i++) {