summaryrefslogtreecommitdiffstats
path: root/web/react
diff options
context:
space:
mode:
Diffstat (limited to 'web/react')
-rw-r--r--web/react/components/activity_log_modal.jsx5
-rw-r--r--web/react/components/admin_console/user_item.jsx1
-rw-r--r--web/react/components/channel_notifications.jsx4
-rw-r--r--web/react/components/create_comment.jsx3
-rw-r--r--web/react/components/error_bar.jsx72
-rw-r--r--web/react/components/member_list_team_item.jsx44
-rw-r--r--web/react/components/navbar_dropdown.jsx39
-rw-r--r--web/react/components/post_body.jsx2
-rw-r--r--web/react/components/post_deleted_modal.jsx36
-rw-r--r--web/react/components/post_info.jsx14
-rw-r--r--web/react/components/rhs_comment.jsx129
-rw-r--r--web/react/components/rhs_thread.jsx11
-rw-r--r--web/react/components/sidebar.jsx2
-rw-r--r--web/react/components/user_settings/import_theme_modal.jsx1
-rw-r--r--web/react/components/user_settings/user_settings_appearance.jsx14
-rw-r--r--web/react/package.json21
-rw-r--r--web/react/stores/browser_store.jsx8
-rw-r--r--web/react/stores/socket_store.jsx6
-rw-r--r--web/react/utils/client.jsx12
-rw-r--r--web/react/utils/constants.jsx32
-rw-r--r--web/react/utils/utils.jsx17
21 files changed, 287 insertions, 186 deletions
diff --git a/web/react/components/activity_log_modal.jsx b/web/react/components/activity_log_modal.jsx
index aee2541b5..ff370c32e 100644
--- a/web/react/components/activity_log_modal.jsx
+++ b/web/react/components/activity_log_modal.jsx
@@ -31,6 +31,11 @@ export default class ActivityLogModal extends React.Component {
}
submitRevoke(altId, e) {
e.preventDefault();
+ var modalContent = $(e.target).closest('.modal-content');
+ modalContent.addClass('animation--highlight');
+ setTimeout(() => {
+ modalContent.removeClass('animation--highlight');
+ }, 1500);
Client.revokeSession(altId,
function handleRevokeSuccess() {
AsyncClient.getSessions();
diff --git a/web/react/components/admin_console/user_item.jsx b/web/react/components/admin_console/user_item.jsx
index 32812e875..c5c6e19d4 100644
--- a/web/react/components/admin_console/user_item.jsx
+++ b/web/react/components/admin_console/user_item.jsx
@@ -127,7 +127,6 @@ export default class UserItem extends React.Component {
if (user.delete_at > 0) {
currentRoles = 'Inactive';
- currentRoles = 'Inactive';
showMakeMember = false;
showMakeAdmin = false;
showMakeSystemAdmin = false;
diff --git a/web/react/components/channel_notifications.jsx b/web/react/components/channel_notifications.jsx
index fed8e789e..ed76b7bce 100644
--- a/web/react/components/channel_notifications.jsx
+++ b/web/react/components/channel_notifications.jsx
@@ -195,9 +195,7 @@ export default class ChannelNotifications extends React.Component {
const extraInfo = (
<span>
- {'Selecting an option other than "Default" will override the global notification settings.'}
- <br/>
- {'Desktop notifications are available on Firefox, Safari, and Chrome.'}
+ {'Selecting an option other than "Default" will override the global notification settings. Desktop notifications are available on Firefox, Safari, and Chrome.'}
</span>
);
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index 9c233ea26..550f85d3d 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -106,10 +106,11 @@ export default class CreateComment extends React.Component {
let state = {};
if (err.message === 'Invalid RootId parameter') {
+ PostStore.removePendingPost(post.channel_id, post.pending_post_id);
+
if ($('#post_deleted').length > 0) {
$('#post_deleted').modal('show');
}
- PostStore.removePendingPost(post.pending_post_id);
} else {
post.state = Constants.POST_FAILED;
PostStore.updatePendingPost(post);
diff --git a/web/react/components/error_bar.jsx b/web/react/components/error_bar.jsx
index 05726e860..5aa55be93 100644
--- a/web/react/components/error_bar.jsx
+++ b/web/react/components/error_bar.jsx
@@ -9,28 +9,71 @@ export default class ErrorBar extends React.Component {
this.onErrorChange = this.onErrorChange.bind(this);
this.handleClose = this.handleClose.bind(this);
+ this.resize = this.resize.bind(this);
this.prevTimer = null;
this.state = ErrorStore.getLastError();
- if (this.state && this.state.message) {
+ if (this.isValidError(this.state)) {
this.prevTimer = setTimeout(this.handleClose, 10000);
}
}
+ isValidError(s) {
+ if (!s) {
+ return false;
+ }
+
+ if (!s.message) {
+ return false;
+ }
+
+ if (s.connErrorCount && s.connErrorCount >= 1 && s.connErrorCount < 7) {
+ return false;
+ }
+
+ return true;
+ }
+
+ isConnectionError(s) {
+ if (!s.connErrorCount || s.connErrorCount === 0) {
+ return false;
+ }
+
+ if (s.connErrorCount > 7) {
+ return true;
+ }
+
+ return false;
+ }
+
+ resize() {
+ if (this.isValidError(this.state)) {
+ var height = $(React.findDOMNode(this)).outerHeight();
+ height = height < 30 ? 30 : height;
+ $('body').css('padding-top', height + 'px');
+ } else {
+ $('body').css('padding-top', '0');
+ }
+ }
+
componentDidMount() {
ErrorStore.addChangeListener(this.onErrorChange);
- $('body').css('padding-top', $(React.findDOMNode(this)).outerHeight());
+
$(window).resize(() => {
- if (this.state && this.state.message) {
- $('body').css('padding-top', $(React.findDOMNode(this)).outerHeight());
- }
+ this.resize();
});
+
+ this.resize();
}
componentWillUnmount() {
ErrorStore.removeChangeListener(this.onErrorChange);
}
+ componentDidUpdate() {
+ this.resize();
+ }
+
onErrorChange() {
var newState = ErrorStore.getLastError();
@@ -41,7 +84,9 @@ export default class ErrorBar extends React.Component {
if (newState) {
this.setState(newState);
- this.prevTimer = setTimeout(this.handleClose, 10000);
+ if (!this.isConnectionError(newState)) {
+ this.prevTimer = setTimeout(this.handleClose, 10000);
+ }
} else {
this.setState({message: null});
}
@@ -52,22 +97,11 @@ export default class ErrorBar extends React.Component {
e.preventDefault();
}
- ErrorStore.storeLastError(null);
- ErrorStore.emitChange();
-
- $('body').css('padding-top', '0');
+ this.setState({message: null});
}
render() {
- if (!this.state) {
- return <div/>;
- }
-
- if (!this.state.message) {
- return <div/>;
- }
-
- if (this.state.connErrorCount < 7) {
+ if (!this.isValidError(this.state)) {
return <div/>;
}
diff --git a/web/react/components/member_list_team_item.jsx b/web/react/components/member_list_team_item.jsx
index b7e81f843..629fb2ec4 100644
--- a/web/react/components/member_list_team_item.jsx
+++ b/web/react/components/member_list_team_item.jsx
@@ -24,32 +24,32 @@ export default class MemberListTeamItem extends React.Component {
};
Client.updateRoles(data,
- function handleMakeMemberSuccess() {
+ () => {
AsyncClient.getProfiles();
},
- function handleMakeMemberError(err) {
+ (err) => {
this.setState({serverError: err.message});
- }.bind(this)
+ }
);
}
handleMakeActive() {
Client.updateActive(this.props.user.id, true,
- function handleMakeActiveSuccess() {
+ () => {
AsyncClient.getProfiles();
},
- function handleMakeActiveError(err) {
+ (err) => {
this.setState({serverError: err.message});
- }.bind(this)
+ }
);
}
handleMakeNotActive() {
Client.updateActive(this.props.user.id, false,
- function handleMakeNotActiveSuccess() {
+ () => {
AsyncClient.getProfiles();
},
- function handleMakeNotActiveError(err) {
+ (err) => {
this.setState({serverError: err.message});
- }.bind(this)
+ }
);
}
handleMakeAdmin() {
@@ -59,12 +59,12 @@ export default class MemberListTeamItem extends React.Component {
};
Client.updateRoles(data,
- function handleMakeAdminSuccess() {
+ () => {
AsyncClient.getProfiles();
},
- function handleMakeAdmitError(err) {
+ (err) => {
this.setState({serverError: err.message});
- }.bind(this)
+ }
);
}
render() {
@@ -82,14 +82,18 @@ export default class MemberListTeamItem extends React.Component {
const timestamp = UserStore.getCurrentUser().update_at;
if (user.roles.length > 0) {
- currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
+ if (user.roles.indexOf('system_admin') > -1) {
+ currentRoles = 'System Admin';
+ } else {
+ currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
+ }
}
const email = user.email;
- let showMakeMember = user.roles === 'admin';
- let showMakeAdmin = user.roles === '';
+ let showMakeMember = user.roles === 'admin' || user.roles === 'system_admin';
+ let showMakeAdmin = user.roles === '' || user.roles === 'system_admin';
let showMakeActive = false;
- let showMakeNotActive = true;
+ let showMakeNotActive = user.roles !== 'system_admin';
if (user.delete_at > 0) {
currentRoles = 'Inactive';
@@ -108,7 +112,7 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeAdmin}
>
- Make Admin
+ {'Make Admin'}
</a>
</li>
);
@@ -123,7 +127,7 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeMember}
>
- Make Member
+ {'Make Member'}
</a>
</li>
);
@@ -138,7 +142,7 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeActive}
>
- Make Active
+ {'Make Active'}
</a>
</li>
);
@@ -153,7 +157,7 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeNotActive}
>
- Make Inactive
+ {'Make Inactive'}
</a>
</li>
);
diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx
index b90ce4170..ff7a53848 100644
--- a/web/react/components/navbar_dropdown.jsx
+++ b/web/react/components/navbar_dropdown.jsx
@@ -145,30 +145,35 @@ export default class NavbarDropdown extends React.Component {
var teams = [];
- teams.push(
- <li
- className='divider'
- key='div'
- >
- </li>
- );
-
if (this.state.teams.length > 1) {
+ teams.push(
+ <li
+ className='divider'
+ key='div'
+ >
+ </li>
+ );
+
this.state.teams.forEach((teamName) => {
if (teamName !== this.props.teamName) {
teams.push(<li key={teamName}><a href={Utils.getWindowLocationOrigin() + '/' + teamName}>{'Switch to ' + teamName}</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>);
+
+ if (global.window.config.EnableTeamCreation === 'true') {
+ 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/post_body.jsx b/web/react/components/post_body.jsx
index 6cfd243de..1d94cab47 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -215,7 +215,7 @@ export default class PostBody extends React.Component {
comment = (
<p className='post-link'>
<span>
- {'Commented on '}{name}{apostrophe}{' message:'}
+ {'Commented on '}{name}{apostrophe}{' message: '}
<a
className='theme'
onClick={this.props.handleCommentClick}
diff --git a/web/react/components/post_deleted_modal.jsx b/web/react/components/post_deleted_modal.jsx
index d284a9d1b..3f487d20f 100644
--- a/web/react/components/post_deleted_modal.jsx
+++ b/web/react/components/post_deleted_modal.jsx
@@ -2,13 +2,41 @@
// See License.txt for license information.
var UserStore = require('../stores/user_store.jsx');
+var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
+var Constants = require('../utils/constants.jsx');
+var ActionTypes = Constants.ActionTypes;
export default class PostDeletedModal extends React.Component {
constructor(props) {
super(props);
+ this.handleClose = this.handleClose.bind(this);
+
this.state = {};
}
+ componentDidMount() {
+ $(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', () => {
+ this.handleClose();
+ });
+ }
+ handleClose() {
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECIEVED_SEARCH,
+ results: null
+ });
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECIEVED_SEARCH_TERM,
+ term: null,
+ do_search: false,
+ is_mention_search: false
+ });
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECIEVED_POST_SELECTED,
+ results: null
+ });
+ }
render() {
var currentUser = UserStore.getCurrentUser();
@@ -31,17 +59,17 @@ export default class PostDeletedModal extends React.Component {
data-dismiss='modal'
aria-label='Close'
>
- <span aria-hidden='true'>&times;</span>
+ <span aria-hidden='true'>{'×'}</span>
</button>
<h4
className='modal-title'
id='myModalLabel'
>
- Comment could not be posted
+ {'Comment could not be posted'}
</h4>
</div>
<div className='modal-body'>
- <p>Someone deleted the message on which you tried to post a comment.</p>
+ <p>{'Someone deleted the message on which you tried to post a comment.'}</p>
</div>
<div className='modal-footer'>
<button
@@ -49,7 +77,7 @@ export default class PostDeletedModal extends React.Component {
className='btn btn-primary'
data-dismiss='modal'
>
- Okay
+ {'Okay'}
</button>
</div>
</div>
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index dba75ac5f..c1e8979a4 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -13,12 +13,6 @@ export default class PostInfo extends React.Component {
super(props);
this.state = {};
}
- shouldShowComment(state, type, isOwner) {
- if (state === Constants.POST_FAILED || state === Constants.POST_LOADING) {
- return false;
- }
- return isOwner || (this.props.allowReply === 'true' && type !== 'Comment');
- }
createDropdown() {
var post = this.props.post;
var isOwner = UserStore.getCurrentId() === post.user_id;
@@ -33,10 +27,6 @@ export default class PostInfo extends React.Component {
type = 'Comment';
}
- if (!this.shouldShowComment(post.state, type, isOwner)) {
- return '';
- }
-
var dropdownContents = [];
var dataComments = 0;
if (type === 'Post') {
@@ -106,6 +96,10 @@ export default class PostInfo extends React.Component {
);
}
+ if (dropdownContents.length === 0) {
+ return '';
+ }
+
return (
<div>
<a
diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx
index 5b4694eb1..aa355f8cc 100644
--- a/web/react/components/rhs_comment.jsx
+++ b/web/react/components/rhs_comment.jsx
@@ -70,19 +70,84 @@ export default class RhsComment extends React.Component {
componentDidUpdate() {
this.parseEmojis();
}
- render() {
+ createDropdown() {
var post = this.props.post;
- var currentUserCss = '';
- if (UserStore.getCurrentId() === post.user_id) {
- currentUserCss = 'current--user';
+ if (post.state === Constants.POST_FAILED || post.state === Constants.POST_LOADING || post.state === Constants.POST_DELETED) {
+ return '';
}
var isOwner = UserStore.getCurrentId() === post.user_id;
+ var isAdmin = Utils.isAdmin(UserStore.getCurrentUser().roles);
+
+ var dropdownContents = [];
+
+ if (isOwner) {
+ dropdownContents.push(
+ <li role='presentation'>
+ <a
+ href='#'
+ role='menuitem'
+ data-toggle='modal'
+ data-target='#edit_post'
+ data-title='Comment'
+ data-message={post.message}
+ data-postid={post.id}
+ data-channelid={post.channel_id}
+ >
+ Edit
+ </a>
+ </li>
+ );
+ }
- var type = 'Post';
- if (post.root_id.length > 0) {
- type = 'Comment';
+ if (isOwner || isAdmin) {
+ dropdownContents.push(
+ <li role='presentation'>
+ <a
+ href='#'
+ role='menuitem'
+ data-toggle='modal'
+ data-target='#delete_post'
+ data-title='Comment'
+ data-postid={post.id}
+ data-channelid={post.channel_id}
+ data-comments={0}
+ >
+ Delete
+ </a>
+ </li>
+ );
+ }
+
+ if (dropdownContents.length === 0) {
+ return '';
+ }
+
+ return (
+ <div className='dropdown'>
+ <a
+ href='#'
+ className='dropdown-toggle theme'
+ type='button'
+ data-toggle='dropdown'
+ aria-expanded='false'
+ />
+ <ul
+ className='dropdown-menu'
+ role='menu'
+ >
+ {dropdownContents}
+ </ul>
+ </div>
+ );
+ }
+ render() {
+ var post = this.props.post;
+
+ var currentUserCss = '';
+ if (UserStore.getCurrentId() === post.user_id) {
+ currentUserCss = 'current--user';
}
var timestamp = UserStore.getCurrentUser().update_at;
@@ -110,53 +175,7 @@ export default class RhsComment extends React.Component {
);
}
- var ownerOptions;
- if (isOwner && post.state !== Constants.POST_FAILED && post.state !== Constants.POST_LOADING) {
- ownerOptions = (
- <div className='dropdown'>
- <a
- href='#'
- className='dropdown-toggle theme'
- type='button'
- data-toggle='dropdown'
- aria-expanded='false'
- />
- <ul
- className='dropdown-menu'
- role='menu'
- >
- <li role='presentation'>
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-title={type}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- >
- Edit
- </a>
- </li>
- <li role='presentation'>
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#delete_post'
- data-title={type}
- data-postid={post.id}
- data-channelid={post.channel_id}
- data-comments={0}
- >
- Delete
- </a>
- </li>
- </ul>
- </div>
- );
- }
+ var dropdown = this.createDropdown();
var fileAttachment;
if (post.filenames && post.filenames.length > 0) {
@@ -190,7 +209,7 @@ export default class RhsComment extends React.Component {
</time>
</li>
<li className='post-header-col post-header__reply'>
- {ownerOptions}
+ {dropdown}
</li>
</ul>
<div className='post-body'>
diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx
index 2f23d80d9..27a784701 100644
--- a/web/react/components/rhs_thread.jsx
+++ b/web/react/components/rhs_thread.jsx
@@ -23,7 +23,7 @@ export default class RhsThread extends React.Component {
}
getStateFromStores() {
var postList = PostStore.getSelectedPost();
- if (!postList || postList.order.length < 1) {
+ if (!postList || postList.order.length < 1 || !postList.posts[postList.order[0]]) {
return {postList: {}};
}
@@ -49,7 +49,10 @@ export default class RhsThread extends React.Component {
}.bind(this));
}
componentDidUpdate() {
- $('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
+ if ($('.post-right__scroll')[0]) {
+ $('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
+ }
+
$('.post-right__scroll').perfectScrollbar('update');
this.resize();
}
@@ -67,7 +70,7 @@ export default class RhsThread extends React.Component {
// if something was changed in the channel like adding a
// comment or post then lets refresh the sidebar list
var currentSelected = PostStore.getSelectedPost();
- if (!currentSelected || currentSelected.order.length === 0) {
+ if (!currentSelected || currentSelected.order.length === 0 || !currentSelected.posts[currentSelected.order[0]]) {
return;
}
@@ -103,7 +106,7 @@ export default class RhsThread extends React.Component {
render() {
var postList = this.state.postList;
- if (postList == null) {
+ if (postList == null || !postList.order) {
return (
<div></div>
);
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
index b696f4b53..88eaed335 100644
--- a/web/react/components/sidebar.jsx
+++ b/web/react/components/sidebar.jsx
@@ -205,7 +205,7 @@ export default class Sidebar extends React.Component {
const user = UserStore.getCurrentUser();
const member = ChannelStore.getMember(msg.channel_id);
- var notifyLevel = member.notify_props.desktop;
+ var notifyLevel = member && member.notify_props ? member.notify_props.desktop : 'default';
if (notifyLevel === 'default') {
notifyLevel = user.notify_props.desktop;
}
diff --git a/web/react/components/user_settings/import_theme_modal.jsx b/web/react/components/user_settings/import_theme_modal.jsx
index 4e8ee03fa..3301c6596 100644
--- a/web/react/components/user_settings/import_theme_modal.jsx
+++ b/web/react/components/user_settings/import_theme_modal.jsx
@@ -49,7 +49,6 @@ export default class ImportThemeModal extends React.Component {
theme.sidebarText = colors[5];
theme.sidebarUnreadText = colors[5];
theme.sidebarTextHoverBg = colors[4];
- theme.sidebarTextHoverColor = colors[5];
theme.sidebarTextActiveBg = colors[2];
theme.sidebarTextActiveColor = colors[3];
theme.sidebarHeaderBg = colors[1];
diff --git a/web/react/components/user_settings/user_settings_appearance.jsx b/web/react/components/user_settings/user_settings_appearance.jsx
index c4a137ed8..be6cf1f42 100644
--- a/web/react/components/user_settings/user_settings_appearance.jsx
+++ b/web/react/components/user_settings/user_settings_appearance.jsx
@@ -214,14 +214,14 @@ export default class UserSettingsAppearance extends React.Component {
<div className='divider-dark first'/>
{themeUI}
<div className='divider-dark'/>
+ <br/>
+ <a
+ className='theme'
+ onClick={this.handleImportModal}
+ >
+ {'Import theme colors from Slack'}
+ </a>
</div>
- <br/>
- <a
- className='theme'
- onClick={this.handleImportModal}
- >
- {'Import theme colors from Slack'}
- </a>
</div>
);
}
diff --git a/web/react/package.json b/web/react/package.json
index a097b6aa8..e6a662375 100644
--- a/web/react/package.json
+++ b/web/react/package.json
@@ -5,7 +5,6 @@
"dependencies": {
"autolinker": "0.18.1",
"babel-runtime": "5.8.24",
- "bootstrap-colorpicker": "2.2.0",
"flux": "2.1.1",
"keymirror": "0.1.1",
"marked": "0.3.5",
@@ -13,18 +12,18 @@
"twemoji": "1.4.1"
},
"devDependencies": {
- "browserify": "11.0.1",
- "envify": "3.4.0",
- "babelify": "6.1.3",
+ "browserify": "11.2.0",
+ "babelify": "6.3.0",
"uglify-js": "2.4.24",
- "watchify": "3.3.1",
+ "watchify": "3.4.0",
"eslint": "1.6.0",
"eslint-plugin-react": "3.5.1"
},
"scripts": {
- "start": "watchify --extension=jsx -o ../static/js/bundle.js -v -d ./**/*.jsx",
- "build": "NODE_ENV=production browserify ./**/*.jsx | uglifyjs -c -m --screw-ie8 > ../static/js/bundle.min.js",
- "test": "jest"
+ "check": "",
+ "build-libs": "browserify -r crypto -r autolinker -r flux -r keymirror -r marked -r object-assign -r twemoji | uglifyjs -c -m --screw-ie8 > ../static/js/libs.min.js",
+ "start": "watchify --fast -x crypto -x node -x autolinker -x flux -x keymirror -x marked -x object-assign -x twemoji -o ../static/js/bundle.js -v -d ./**/*.jsx",
+ "build": "browserify -x crypto -x autolinker -x flux -x keymirror -x marked -x object-assign -x twemoji ./**/*.jsx | uglifyjs -c -m --screw-ie8 > ../static/js/bundle.min.js"
},
"browserify": {
"transform": [
@@ -35,11 +34,7 @@
"runtime"
]
}
- ],
- "envify"
+ ]
]
- },
- "jest": {
- "rootDir": "."
}
}
diff --git a/web/react/stores/browser_store.jsx b/web/react/stores/browser_store.jsx
index e45d3d981..27a74fb2b 100644
--- a/web/react/stores/browser_store.jsx
+++ b/web/react/stores/browser_store.jsx
@@ -41,7 +41,13 @@ class BrowserStoreClass {
}
setGlobalItem(name, value) {
- localStorage.setItem(name, JSON.stringify(value));
+ try {
+ localStorage.setItem(name, JSON.stringify(value));
+ } catch (err) {
+ console.log('An error occurred while setting local storage, clearing all props'); //eslint-disable-line no-console
+ localStorage.clear();
+ window.location.href = window.location.href;
+ }
}
getGlobalItem(name, defaultValue) {
diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx
index 1d853f979..9f354965e 100644
--- a/web/react/stores/socket_store.jsx
+++ b/web/react/stores/socket_store.jsx
@@ -50,8 +50,10 @@ class SocketStoreClass extends EventEmitter {
}
this.failCount = 0;
- ErrorStore.storeLastError(null);
- ErrorStore.emitChange();
+ if (ErrorStore.getLastError()) {
+ ErrorStore.storeLastError(null);
+ ErrorStore.emitChange();
+ }
};
conn.onclose = () => {
diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx
index d9f486009..6dccfcdeb 100644
--- a/web/react/utils/client.jsx
+++ b/web/react/utils/client.jsx
@@ -2,6 +2,7 @@
var BrowserStore = require('../stores/browser_store.jsx');
var TeamStore = require('../stores/team_store.jsx');
+var ErrorStore = require('../stores/error_store.jsx');
export function track(category, action, label, prop, val) {
global.window.analytics.track(action, {category: category, label: label, property: prop, value: val});
@@ -27,7 +28,16 @@ function handleError(methodName, xhr, status, err) {
msg = 'error in ' + methodName + ' status=' + status + ' statusCode=' + xhr.status + ' err=' + err;
if (xhr.status === 0) {
- e = {message: 'There appears to be a problem with your internet connection', connErrorCount: 1};
+ let errorCount = 1;
+ const oldError = ErrorStore.getLastError();
+ let connectError = 'There appears to be a problem with your internet connection';
+
+ if (oldError && oldError.connErrorCount) {
+ errorCount += oldError.connErrorCount;
+ connectError = 'We cannot reach the Mattermost service. The service may be down or misconfigured. Please contact an administrator to make sure the WebSocket port is configured properly.';
+ }
+
+ e = {message: connectError, connErrorCount: errorCount};
} else {
e = {message: 'We received an unexpected status code from the server (' + xhr.status + ')'};
}
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index aba63b91c..8fd0ab79b 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -122,10 +122,9 @@ module.exports = {
default: {
type: 'Mattermost',
sidebarBg: '#fafafa',
- sidebarText: '#999999',
+ sidebarText: '#333333',
sidebarUnreadText: '#333333',
sidebarTextHoverBg: '#e6f2fa',
- sidebarTextHoverColor: '#999999',
sidebarTextActiveBg: '#e1e1e1',
sidebarTextActiveColor: '#111111',
sidebarHeaderBg: '#2389d7',
@@ -139,15 +138,15 @@ module.exports = {
linkColor: '#2389d7',
buttonBg: '#2389d7',
buttonColor: '#FFFFFF',
- mentionHighlightBg: '#fff2bb'
+ mentionHighlightBg: '#fff2bb',
+ mentionHighlightLink: '#2f81b7'
},
organization: {
type: 'Organization',
sidebarBg: '#2071a7',
- sidebarText: '#bfcde8',
+ sidebarText: '#fff',
sidebarUnreadText: '#fff',
sidebarTextHoverBg: '#136197',
- sidebarTextHoverColor: '#bfcde8',
sidebarTextActiveBg: '#136197',
sidebarTextActiveColor: '#FFFFFF',
sidebarHeaderBg: '#2f81b7',
@@ -161,15 +160,15 @@ module.exports = {
linkColor: '#2f81b7',
buttonBg: '#1dacfc',
buttonColor: '#FFFFFF',
- mentionHighlightBg: '#fff2bb'
+ mentionHighlightBg: '#fff2bb',
+ mentionHighlightLink: '#2f81b7'
},
mattermostDark: {
type: 'Mattermost Dark',
sidebarBg: '#1B2C3E',
- sidebarText: '#bbbbbb',
+ sidebarText: '#fff',
sidebarUnreadText: '#fff',
sidebarTextHoverBg: '#4A5664',
- sidebarTextHoverColor: '#bbbbbb',
sidebarTextActiveBg: '#39769C',
sidebarTextActiveColor: '#FFFFFF',
sidebarHeaderBg: '#1B2C3E',
@@ -183,15 +182,15 @@ module.exports = {
linkColor: '#A4FFEB',
buttonBg: '#4CBBA4',
buttonColor: '#FFFFFF',
- mentionHighlightBg: '#338886'
+ mentionHighlightBg: '#984063',
+ mentionHighlightLink: '#A4FFEB'
},
windows10: {
type: 'Windows Dark',
sidebarBg: '#171717',
- sidebarText: '#999',
+ sidebarText: '#fff',
sidebarUnreadText: '#fff',
sidebarTextHoverBg: '#302e30',
- sidebarTextHoverColor: '#999',
sidebarTextActiveBg: '#484748',
sidebarTextActiveColor: '#FFFFFF',
sidebarHeaderBg: '#1f1f1f',
@@ -205,7 +204,8 @@ module.exports = {
linkColor: '#0177e7',
buttonBg: '#0177e7',
buttonColor: '#FFFFFF',
- mentionHighlightBg: '#276198'
+ mentionHighlightBg: '#784098',
+ mentionHighlightLink: '#A4FFEB'
}
},
THEME_ELEMENTS: [
@@ -234,10 +234,6 @@ module.exports = {
uiName: 'Sidebar Text Hover BG'
},
{
- id: 'sidebarTextHoverColor',
- uiName: 'Sidebar Text Hover Color'
- },
- {
id: 'sidebarTextActiveBg',
uiName: 'Sidebar Text Active BG'
},
@@ -284,6 +280,10 @@ module.exports = {
{
id: 'mentionHighlightBg',
uiName: 'Mention Highlight BG'
+ },
+ {
+ id: 'mentionHighlightLink',
+ uiName: 'Mention Highlight Link'
}
]
};
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 3a04f3623..f79f3492f 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -399,9 +399,9 @@ export function applyTheme(theme) {
}
if (theme.sidebarText) {
- changeCss('.sidebar--left .nav-pills__container li>a, .sidebar--right, .settings-modal .nav-pills>li a, .sidebar--menu', 'color:' + theme.sidebarText, 1);
+ changeCss('.sidebar--left .nav-pills__container li>a, .sidebar--right, .settings-modal .nav-pills>li a, .sidebar--menu', 'color:' + changeOpacity(theme.sidebarText, 0.6), 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 .nav-pills__container li>h4, .sidebar--left .add-channel-btn', 'color:' + changeOpacity(theme.sidebarText, 0.6), 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);
@@ -417,11 +417,6 @@ export function applyTheme(theme) {
changeCss('@media(max-width: 768px){.settings-modal .settings-table .nav>li:hover a', 'background:' + theme.sidebarTextHoverBg, 1);
}
- if (theme.sidebarTextHoverColor) {
- 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-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);
}
@@ -495,7 +490,7 @@ export function applyTheme(theme) {
changeCss('.post-image__column a, .post-image__column a:hover, .post-image__column a:focus', 'color:' + theme.centerChannelColor, 1);
changeCss('.search-bar__container .search__form .search-bar, .form-control', 'color:' + theme.centerChannelColor, 2);
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, .form-control', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
+ changeCss('.input-group-addon, .search-bar__container .search__form, .form-control', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
changeCss('.form-control:focus', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1);
changeCss('.channel-intro .channel-intro__content', 'background:' + changeOpacity(theme.centerChannelColor, 0.05), 1);
changeCss('.date-separator .separator__text', 'color:' + theme.centerChannelColor, 2);
@@ -503,7 +498,7 @@ export function applyTheme(theme) {
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, .modal .more-channel-table tbody>tr td, .member-div:first-child, .member-div', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.1), 2);
+ 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, .modal .more-channel-table tbody>tr td, .member-div:first-child, .member-div, .access-history__table .access__report, .activity-log__table', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.1), 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);
@@ -536,6 +531,10 @@ export function applyTheme(theme) {
if (theme.mentionHighlightBg) {
changeCss('.mention-highlight, .search-highlight', 'background:' + theme.mentionHighlightBg, 1);
}
+
+ if (theme.mentionHighlightLink) {
+ changeCss('.mention-highlight .mention-link', 'color:' + theme.mentionHighlightLink, 1);
+ }
}
export function changeCss(className, classValue, classRepeat) {
// we need invisible container to store additional css definitions