summaryrefslogtreecommitdiffstats
path: root/webapp
diff options
context:
space:
mode:
Diffstat (limited to 'webapp')
-rw-r--r--webapp/components/admin_console/admin_navbar_dropdown.jsx69
-rw-r--r--webapp/components/common/comment_icon.jsx55
-rw-r--r--webapp/components/common/post_flag_icon.jsx87
-rw-r--r--webapp/components/error_bar.jsx4
-rw-r--r--webapp/components/invite_member_modal.jsx21
-rw-r--r--webapp/components/more_direct_channels/more_direct_channels.jsx1
-rw-r--r--webapp/components/post_view/components/post.jsx9
-rw-r--r--webapp/components/post_view/components/post_body.jsx8
-rw-r--r--webapp/components/post_view/components/post_header.jsx2
-rw-r--r--webapp/components/post_view/components/post_info.jsx106
-rw-r--r--webapp/components/post_view/components/post_list.jsx2
-rw-r--r--webapp/components/post_view/components/post_message_container.jsx4
-rw-r--r--webapp/components/post_view/components/post_message_view.jsx13
-rw-r--r--webapp/components/rhs_comment.jsx80
-rw-r--r--webapp/components/rhs_root_post.jsx62
-rw-r--r--webapp/components/rhs_thread.jsx29
-rw-r--r--webapp/components/search_bar.jsx6
-rw-r--r--webapp/components/search_results.jsx6
-rw-r--r--webapp/components/search_results_item.jsx84
-rwxr-xr-xwebapp/i18n/en.json7
-rw-r--r--webapp/root.jsx11
-rw-r--r--webapp/sass/layout/_headers.scss10
-rw-r--r--webapp/sass/routes/_about-modal.scss105
-rw-r--r--webapp/stores/search_store.jsx2
-rw-r--r--webapp/utils/constants.jsx3
25 files changed, 429 insertions, 357 deletions
diff --git a/webapp/components/admin_console/admin_navbar_dropdown.jsx b/webapp/components/admin_console/admin_navbar_dropdown.jsx
index 00cbbdb0c..d46732d40 100644
--- a/webapp/components/admin_console/admin_navbar_dropdown.jsx
+++ b/webapp/components/admin_console/admin_navbar_dropdown.jsx
@@ -6,6 +6,7 @@ import ReactDOM from 'react-dom';
import TeamStore from 'stores/team_store.jsx';
import Constants from 'utils/constants.jsx';
+import AboutBuildModal from 'components/about_build_modal.jsx';
import {sortTeamsByDisplayName} from 'utils/team_utils.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
@@ -22,10 +23,13 @@ export default class AdminNavbarDropdown extends React.Component {
super(props);
this.blockToggle = false;
this.onTeamChange = this.onTeamChange.bind(this);
+ this.handleAboutModal = this.handleAboutModal.bind(this);
+ this.aboutModalDismissed = this.aboutModalDismissed.bind(this);
this.state = {
teams: TeamStore.getAll(),
- teamMembers: TeamStore.getMyTeamMembers()
+ teamMembers: TeamStore.getMyTeamMembers(),
+ showAboutModal: false
};
}
@@ -45,6 +49,16 @@ export default class AdminNavbarDropdown extends React.Component {
TeamStore.removeChangeListener(this.onTeamChange);
}
+ handleAboutModal(e) {
+ e.preventDefault();
+
+ this.setState({showAboutModal: true});
+ }
+
+ aboutModalDismissed() {
+ this.setState({showAboutModal: false});
+ }
+
onTeamChange() {
this.setState({
teams: TeamStore.getAll(),
@@ -53,6 +67,7 @@ export default class AdminNavbarDropdown extends React.Component {
}
render() {
+ const config = global.window.mm_config;
var teamsArray = []; // Array of team objects
var teams = []; // Array of team components
let switchTeams;
@@ -138,6 +153,54 @@ export default class AdminNavbarDropdown extends React.Component {
className='divider'
/>
<li>
+ <Link
+ to={config.AdministratorsGuideLink}
+ rel='noopener noreferrer'
+ target='_blank'
+ >
+ <FormattedMessage
+ id='admin.nav.administratorsGuide'
+ defaultMessage='Administrator Guide'
+ />
+ </Link>
+ </li>
+ <li>
+ <Link
+ to={config.TroubleshootingForumLink}
+ rel='noopener noreferrer'
+ target='_blank'
+ >
+ <FormattedMessage
+ id='admin.nav.troubleshootingForum'
+ defaultMessage='Troubleshooting Forum'
+ />
+ </Link>
+ </li>
+ <li>
+ <Link
+ to={config.CommercialSupportLink}
+ rel='noopener noreferrer'
+ target='_blank'
+ >
+ <FormattedMessage
+ id='admin.nav.commercialSupport'
+ defaultMessage='Commercial Support'
+ />
+ </Link>
+ </li>
+ <li>
+ <a
+ href='#'
+ onClick={this.handleAboutModal}
+ >
+ <FormattedMessage
+ id='navbar_dropdown.about'
+ defaultMessage='About Mattermost'
+ />
+ </a>
+ </li>
+ <li className='divider'/>
+ <li>
<a
href='#'
id='logout'
@@ -149,6 +212,10 @@ export default class AdminNavbarDropdown extends React.Component {
/>
</a>
</li>
+ <AboutBuildModal
+ show={this.state.showAboutModal}
+ onModalDismissed={this.aboutModalDismissed}
+ />
</ul>
</li>
</ul>
diff --git a/webapp/components/common/comment_icon.jsx b/webapp/components/common/comment_icon.jsx
new file mode 100644
index 000000000..e8be773e5
--- /dev/null
+++ b/webapp/components/common/comment_icon.jsx
@@ -0,0 +1,55 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+import Constants from 'utils/constants.jsx';
+import * as Utils from 'utils/utils.jsx';
+
+export default function CommentIcon(props) {
+ let commentCountSpan = '';
+ let iconStyle = 'comment-icon__container';
+ if (props.commentCount > 0) {
+ iconStyle += ' icon--show';
+ commentCountSpan = (
+ <span className='comment-count'>
+ {props.commentCount}
+ </span>
+ );
+ } else if (props.searchStyle !== '') {
+ iconStyle = iconStyle + ' ' + props.searchStyle;
+ }
+
+ let commentIconId = props.idPrefix;
+ if (props.idCount > -1) {
+ commentIconId += props.idCount;
+ }
+
+ return (
+ <a
+ id={Utils.createSafeId(commentIconId)}
+ href='#'
+ className={iconStyle}
+ onClick={props.handleCommentClick}
+ >
+ <span
+ className='comment-icon'
+ dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
+ />
+ {commentCountSpan}
+ </a>
+ );
+}
+
+CommentIcon.propTypes = {
+ idPrefix: React.PropTypes.string.isRequired,
+ idCount: React.PropTypes.number,
+ handleCommentClick: React.PropTypes.func.isRequired,
+ searchStyle: React.PropTypes.string,
+ commentCount: React.PropTypes.number
+};
+
+CommentIcon.defaultProps = {
+ idCount: -1,
+ searchStyle: '',
+ commentCount: 0
+}; \ No newline at end of file
diff --git a/webapp/components/common/post_flag_icon.jsx b/webapp/components/common/post_flag_icon.jsx
new file mode 100644
index 000000000..eb993d9f6
--- /dev/null
+++ b/webapp/components/common/post_flag_icon.jsx
@@ -0,0 +1,87 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+import {FormattedMessage} from 'react-intl';
+import {Tooltip, OverlayTrigger} from 'react-bootstrap';
+
+import {flagPost, unflagPost} from 'actions/post_actions.jsx';
+import Constants from 'utils/constants.jsx';
+import * as Utils from 'utils/utils.jsx';
+
+function flagToolTip(isFlagged) {
+ return (
+ <Tooltip id='flagTooltip'>
+ <FormattedMessage
+ id={isFlagged ? 'flag_post.unflag' : 'flag_post.flag'}
+ defaultMessage={isFlagged ? 'Unflag' : 'Flag for follow up'}
+ />
+ </Tooltip>
+ );
+}
+
+function flagIcon() {
+ return (
+ <span
+ className='icon'
+ dangerouslySetInnerHTML={{__html: Constants.FLAG_ICON_SVG}}
+ />
+ );
+}
+
+export default function PostFlagIcon(props) {
+ function onFlagPost(e) {
+ e.preventDefault();
+ flagPost(props.postId);
+ }
+
+ function onUnflagPost(e) {
+ e.preventDefault();
+ unflagPost(props.postId);
+ }
+
+ const flagFunc = props.isFlagged ? onUnflagPost : onFlagPost;
+ const flagVisible = props.isFlagged ? 'visible' : '';
+
+ let flagIconId = null;
+ if (props.idCount > -1) {
+ flagIconId = Utils.createSafeId(props.idPrefix + props.idCount);
+ }
+
+ if (!props.isEphemeral) {
+ return (
+ <OverlayTrigger
+ key={'flagtooltipkey' + flagVisible}
+ delayShow={Constants.OVERLAY_TIME_DELAY}
+ placement='top'
+ overlay={flagToolTip(props.isFlagged)}
+ >
+ <a
+ id={flagIconId}
+ href='#'
+ className={'flag-icon__container ' + flagVisible}
+ onClick={flagFunc}
+ >
+ {flagIcon()}
+ </a>
+ </OverlayTrigger>
+ );
+ }
+
+ return null;
+}
+
+PostFlagIcon.propTypes = {
+ idPrefix: React.PropTypes.string.isRequired,
+ idCount: React.PropTypes.number,
+ postId: React.PropTypes.string.isRequired,
+ isFlagged: React.PropTypes.bool.isRequired,
+ isEphemeral: React.PropTypes.bool
+};
+
+PostFlagIcon.defaultProps = {
+ idCount: -1,
+ postId: '',
+ isFlagged: false,
+ isEphemeral: false
+};
diff --git a/webapp/components/error_bar.jsx b/webapp/components/error_bar.jsx
index 94555ce04..97fbbdca0 100644
--- a/webapp/components/error_bar.jsx
+++ b/webapp/components/error_bar.jsx
@@ -151,7 +151,7 @@ export default class ErrorBar extends React.Component {
message = (
<FormattedHTMLMessage
id={ErrorBarTypes.LICENSE_EXPIRING}
- defaultMessage='Enterprise license expires on {date}. <a href="{link}" target="_blank">Please renew.</a>'
+ defaultMessage='Enterprise license expires on {date}. <a href="{link}" target="_blank">Please renew</a>.'
values={{
date: displayExpiryDate(),
link: renewalLink
@@ -162,7 +162,7 @@ export default class ErrorBar extends React.Component {
message = (
<FormattedHTMLMessage
id={ErrorBarTypes.LICENSE_EXPIRED}
- defaultMessage='Enterprise license is expired and some features may be disabled. <a href="{link}" target="_blank">Please renew.</a>'
+ defaultMessage='Enterprise license is expired and some features may be disabled. <a href="{link}" target="_blank">Please renew</a>.'
values={{
link: renewalLink
}}
diff --git a/webapp/components/invite_member_modal.jsx b/webapp/components/invite_member_modal.jsx
index 6426a6a2b..f4b2d0555 100644
--- a/webapp/components/invite_member_modal.jsx
+++ b/webapp/components/invite_member_modal.jsx
@@ -115,21 +115,20 @@ class InviteMemberModal extends React.Component {
var valid = true;
for (var i = 0; i < count; i++) {
- var index = inviteIds[i];
var invite = {};
+ var index = inviteIds[i];
invite.email = ReactDOM.findDOMNode(this.refs['email' + index]).value.trim();
- if (!invite.email || !utils.isEmail(invite.email)) {
- emailErrors[index] = this.props.intl.formatMessage(holders.emailError);
- valid = false;
- } else {
- emailErrors[index] = '';
- }
-
invite.firstName = ReactDOM.findDOMNode(this.refs['first_name' + index]).value.trim();
-
invite.lastName = ReactDOM.findDOMNode(this.refs['last_name' + index]).value.trim();
-
- invites.push(invite);
+ if (invite.email !== '' || index === 0) {
+ if (!invite.email || !utils.isEmail(invite.email)) {
+ emailErrors[index] = this.props.intl.formatMessage(holders.emailError);
+ valid = false;
+ } else {
+ emailErrors[index] = '';
+ }
+ invites.push(invite);
+ }
}
this.setState({emailErrors, firstNameErrors, lastNameErrors});
diff --git a/webapp/components/more_direct_channels/more_direct_channels.jsx b/webapp/components/more_direct_channels/more_direct_channels.jsx
index 768e802a3..16a45aa9a 100644
--- a/webapp/components/more_direct_channels/more_direct_channels.jsx
+++ b/webapp/components/more_direct_channels/more_direct_channels.jsx
@@ -226,6 +226,7 @@ export default class MoreDirectChannels extends React.Component {
>
<ProfilePicture
src={`${Client.getUsersRoute()}/${option.id}/image?time=${option.last_picture_update}`}
+ status={`${UserStore.getStatus(option.id)}`}
width='32'
height='32'
/>
diff --git a/webapp/components/post_view/components/post.jsx b/webapp/components/post_view/components/post.jsx
index 8ca84aa20..38f95a85b 100644
--- a/webapp/components/post_view/components/post.jsx
+++ b/webapp/components/post_view/components/post.jsx
@@ -22,7 +22,7 @@ export default class Post extends Component {
sameUser: PropTypes.bool,
sameRoot: PropTypes.bool,
hideProfilePic: PropTypes.bool,
- isLastPost: PropTypes.bool,
+ lastPostCount: PropTypes.number,
isLastComment: PropTypes.bool,
shouldHighlight: PropTypes.bool,
displayNameType: PropTypes.string,
@@ -137,6 +137,10 @@ export default class Post extends Component {
return true;
}
+ if (nextProps.lastPostCount !== this.props.lastPostCount) {
+ return true;
+ }
+
return false;
}
@@ -288,6 +292,7 @@ export default class Post extends Component {
ref='header'
post={post}
sameRoot={this.props.sameRoot}
+ lastPostCount={this.props.lastPostCount}
commentCount={this.props.commentCount}
handleCommentClick={this.handleCommentClick}
handleDropdownOpened={this.handleDropdownOpened}
@@ -306,7 +311,7 @@ export default class Post extends Component {
post={post}
currentUser={this.props.currentUser}
sameRoot={this.props.sameRoot}
- isLastPost={this.props.isLastPost}
+ lastPostCount={this.props.lastPostCount}
parentPost={parentPost}
handleCommentClick={this.handleCommentClick}
compactDisplay={this.props.compactDisplay}
diff --git a/webapp/components/post_view/components/post_body.jsx b/webapp/components/post_view/components/post_body.jsx
index 108605eea..2ad9f96d1 100644
--- a/webapp/components/post_view/components/post_body.jsx
+++ b/webapp/components/post_view/components/post_body.jsx
@@ -51,6 +51,10 @@ export default class PostBody extends React.Component {
return true;
}
+ if (nextProps.lastPostCount !== this.props.lastPostCount) {
+ return true;
+ }
+
return false;
}
@@ -164,7 +168,7 @@ export default class PostBody extends React.Component {
>
{loading}
<PostMessageContainer
- isLastPost={this.props.isLastPost}
+ lastPostCount={this.props.lastPostCount}
post={this.props.post}
/>
</div>
@@ -208,7 +212,7 @@ PostBody.propTypes = {
currentUser: React.PropTypes.object.isRequired,
parentPost: React.PropTypes.object,
retryPost: React.PropTypes.func,
- isLastPost: React.PropTypes.bool,
+ lastPostCount: React.PropTypes.number,
handleCommentClick: React.PropTypes.func.isRequired,
compactDisplay: React.PropTypes.bool,
previewCollapsed: React.PropTypes.string,
diff --git a/webapp/components/post_view/components/post_header.jsx b/webapp/components/post_view/components/post_header.jsx
index 9de0b7e79..eccd092b5 100644
--- a/webapp/components/post_view/components/post_header.jsx
+++ b/webapp/components/post_view/components/post_header.jsx
@@ -79,6 +79,7 @@ export default class PostHeader extends React.Component {
<li className='col'>
<PostInfo
post={post}
+ lastPostCount={this.props.lastPostCount}
commentCount={this.props.commentCount}
handleCommentClick={this.props.handleCommentClick}
handleDropdownOpened={this.props.handleDropdownOpened}
@@ -105,6 +106,7 @@ PostHeader.propTypes = {
post: React.PropTypes.object.isRequired,
user: React.PropTypes.object,
currentUser: React.PropTypes.object.isRequired,
+ lastPostCount: React.PropTypes.number,
commentCount: React.PropTypes.number.isRequired,
isLastComment: React.PropTypes.bool.isRequired,
handleCommentClick: React.PropTypes.func.isRequired,
diff --git a/webapp/components/post_view/components/post_info.jsx b/webapp/components/post_view/components/post_info.jsx
index 0cb8ff5ac..3833f5058 100644
--- a/webapp/components/post_view/components/post_info.jsx
+++ b/webapp/components/post_view/components/post_info.jsx
@@ -5,15 +5,17 @@ import $ from 'jquery';
import ReactDOM from 'react-dom';
import PostTime from './post_time.jsx';
+import PostFlagIcon from 'components/common/post_flag_icon.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
import * as PostActions from 'actions/post_actions.jsx';
+import CommentIcon from 'components/common/comment_icon.jsx';
import * as Utils from 'utils/utils.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
import Constants from 'utils/constants.jsx';
import DelayedAction from 'utils/delayed_action.jsx';
-import {Tooltip, OverlayTrigger, Overlay} from 'react-bootstrap';
+import {Overlay} from 'react-bootstrap';
import EmojiPicker from 'components/emoji_picker/emoji_picker.jsx';
import React from 'react';
@@ -325,7 +327,11 @@ export default class PostInfo extends React.Component {
render() {
var post = this.props.post;
- const flagIcon = Constants.FLAG_ICON_SVG;
+
+ let idCount = -1;
+ if (this.props.lastPostCount >= 0 && this.props.lastPostCount < Constants.TEST_ID_COUNT) {
+ idCount = this.props.lastPostCount;
+ }
this.canDelete = PostUtils.canDeletePost(post);
this.canEdit = PostUtils.canEditPost(post, this.editDisableAction);
@@ -337,30 +343,13 @@ export default class PostInfo extends React.Component {
let comments = null;
let react = null;
if (!isEphemeral && !isPending && !isSystemMessage) {
- let showCommentClass;
- let commentCountText;
- if (this.props.commentCount >= 1) {
- showCommentClass = ' icon--show';
- commentCountText = this.props.commentCount;
- } else {
- showCommentClass = '';
- commentCountText = '';
- }
-
comments = (
- <a
- href='#'
- className={'comment-icon__container' + showCommentClass}
- onClick={this.props.handleCommentClick}
- >
- <span
- className='comment-icon'
- dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
- />
- <span className='comment-count'>
- {commentCountText}
- </span>
- </a>
+ <CommentIcon
+ idPrefix={'commentIcon'}
+ idCount={idCount}
+ handleCommentClick={this.props.handleCommentClick}
+ commentCount={this.props.commentCount}
+ />
);
if (Utils.isFeatureEnabled(Constants.PRE_RELEASE_FEATURES.EMOJI_PICKER_PREVIEW)) {
@@ -420,64 +409,6 @@ export default class PostInfo extends React.Component {
}
}
- let flag;
- let flagFunc;
- let flagVisible = '';
- let flagTooltip = (
- <Tooltip id='flagTooltip'>
- <FormattedMessage
- id='flag_post.flag'
- defaultMessage='Flag for follow up'
- />
- </Tooltip>
- );
- if (this.props.isFlagged) {
- flagVisible = 'visible';
- flag = (
- <span
- className='icon'
- dangerouslySetInnerHTML={{__html: flagIcon}}
- />
- );
- flagFunc = this.unflagPost;
- flagTooltip = (
- <Tooltip id='flagTooltip'>
- <FormattedMessage
- id='flag_post.unflag'
- defaultMessage='Unflag'
- />
- </Tooltip>
- );
- } else {
- flag = (
- <span
- className='icon'
- dangerouslySetInnerHTML={{__html: flagIcon}}
- />
- );
- flagFunc = this.flagPost;
- }
-
- let flagTrigger;
- if (!isEphemeral) {
- flagTrigger = (
- <OverlayTrigger
- key={'flagtooltipkey' + flagVisible}
- delayShow={Constants.OVERLAY_TIME_DELAY}
- placement='top'
- overlay={flagTooltip}
- >
- <a
- href='#'
- className={'flag-icon__container ' + flagVisible}
- onClick={flagFunc}
- >
- {flag}
- </a>
- </OverlayTrigger>
- );
- }
-
let pinnedBadge;
if (post.is_pinned) {
pinnedBadge = (
@@ -502,7 +433,13 @@ export default class PostInfo extends React.Component {
/>
{pinnedBadge}
{this.state.showEmojiPicker}
- {flagTrigger}
+ <PostFlagIcon
+ idPrefix={'centerPostFlag'}
+ idCount={idCount}
+ postId={post.id}
+ isFlagged={this.props.isFlagged}
+ isEphemeral={isEphemeral}
+ />
</li>
{options}
</ul>
@@ -518,6 +455,7 @@ PostInfo.defaultProps = {
};
PostInfo.propTypes = {
post: React.PropTypes.object.isRequired,
+ lastPostCount: React.PropTypes.number,
commentCount: React.PropTypes.number.isRequired,
isLastComment: React.PropTypes.bool.isRequired,
handleCommentClick: React.PropTypes.func.isRequired,
diff --git a/webapp/components/post_view/components/post_list.jsx b/webapp/components/post_view/components/post_list.jsx
index 483ff78c8..f233884ac 100644
--- a/webapp/components/post_view/components/post_list.jsx
+++ b/webapp/components/post_view/components/post_list.jsx
@@ -339,7 +339,7 @@ export default class PostList extends React.Component {
<Post
key={keyPrefix + 'postKey'}
ref={post.id}
- isLastPost={i === 0}
+ lastPostCount={(i >= 0 && i < Constants.TEST_ID_COUNT) ? i : -1}
sameUser={sameUser}
sameRoot={sameRoot}
post={post}
diff --git a/webapp/components/post_view/components/post_message_container.jsx b/webapp/components/post_view/components/post_message_container.jsx
index 5d324ba46..4cb3cb76c 100644
--- a/webapp/components/post_view/components/post_message_container.jsx
+++ b/webapp/components/post_view/components/post_message_container.jsx
@@ -16,7 +16,7 @@ export default class PostMessageContainer extends React.Component {
static propTypes = {
post: React.PropTypes.object.isRequired,
options: React.PropTypes.object,
- isLastPost: React.PropTypes.bool
+ lastPostCount: React.PropTypes.number
};
static defaultProps = {
@@ -91,7 +91,7 @@ export default class PostMessageContainer extends React.Component {
<PostMessageView
options={this.props.options}
post={this.props.post}
- isLastPost={this.props.isLastPost}
+ lastPostCount={this.props.lastPostCount}
emojis={this.state.emojis}
enableFormatting={this.state.enableFormatting}
mentionKeys={this.state.mentionKeys}
diff --git a/webapp/components/post_view/components/post_message_view.jsx b/webapp/components/post_view/components/post_message_view.jsx
index 5b0790f36..d6610f813 100644
--- a/webapp/components/post_view/components/post_message_view.jsx
+++ b/webapp/components/post_view/components/post_message_view.jsx
@@ -22,7 +22,7 @@ export default class PostMessageView extends React.Component {
usernameMap: React.PropTypes.object.isRequired,
channelNamesMap: React.PropTypes.object.isRequired,
team: React.PropTypes.object.isRequired,
- isLastPost: React.PropTypes.bool
+ lastPostCount: React.PropTypes.number
};
shouldComponentUpdate(nextProps) {
@@ -55,6 +55,10 @@ export default class PostMessageView extends React.Component {
return true;
}
+ if (nextProps.lastPostCount !== this.props.lastPostCount) {
+ return true;
+ }
+
// Don't check if props.usernameMap changes since it is very large and inefficient to do so.
// This mimics previous behaviour, but could be changed if we decide it's worth it.
// The same choice (and reasoning) is also applied to the this.props.channelNamesMap.
@@ -111,10 +115,15 @@ export default class PostMessageView extends React.Component {
return <div>{renderedSystemMessage}</div>;
}
+ let postId = null;
+ if (this.props.lastPostCount >= 0) {
+ postId = Utils.createSafeId('lastPostMessageText' + this.props.lastPostCount);
+ }
+
return (
<div>
<span
- id={this.props.isLastPost ? 'lastPostMessageText' : null}
+ id={postId}
className='post-message__text'
onClick={Utils.handleFormattedTextClick}
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, options)}}
diff --git a/webapp/components/rhs_comment.jsx b/webapp/components/rhs_comment.jsx
index fb0972804..88e8c1ca6 100644
--- a/webapp/components/rhs_comment.jsx
+++ b/webapp/components/rhs_comment.jsx
@@ -8,6 +8,7 @@ import PostMessageContainer from 'components/post_view/components/post_message_c
import ProfilePicture from 'components/profile_picture.jsx';
import ReactionListContainer from 'components/post_view/components/reaction_list_container.jsx';
import RhsDropdown from 'components/rhs_dropdown.jsx';
+import PostFlagIcon from 'components/common/post_flag_icon.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
import {flagPost, unflagPost, pinPost, unpinPost, addReaction} from 'actions/post_actions.jsx';
@@ -19,7 +20,7 @@ import * as PostUtils from 'utils/post_utils.jsx';
import Constants from 'utils/constants.jsx';
import DelayedAction from 'utils/delayed_action.jsx';
-import {Tooltip, OverlayTrigger, Overlay} from 'react-bootstrap';
+import {Overlay} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
@@ -128,6 +129,10 @@ export default class RhsComment extends React.Component {
return true;
}
+ if (nextProps.lastPostCount !== this.props.lastPostCount) {
+ return true;
+ }
+
return false;
}
@@ -384,9 +389,13 @@ export default class RhsComment extends React.Component {
render() {
const post = this.props.post;
- const flagIcon = Constants.FLAG_ICON_SVG;
const mattermostLogo = Constants.MATTERMOST_ICON_SVG;
+ let idCount = -1;
+ if (this.props.lastPostCount >= 0 && this.props.lastPostCount < Constants.TEST_ID_COUNT) {
+ idCount = this.props.lastPostCount;
+ }
+
const isEphemeral = Utils.isPostEphemeral(post);
const isPending = post.state === Constants.POST_FAILED || post.state === Constants.POST_LOADING;
const isSystemMessage = PostUtils.isSystemMessage(post);
@@ -523,64 +532,6 @@ export default class RhsComment extends React.Component {
);
}
- let flag;
- let flagFunc;
- let flagVisible = '';
- let flagTooltip = (
- <Tooltip id='flagTooltip'>
- <FormattedMessage
- id='flag_post.flag'
- defaultMessage='Flag for follow up'
- />
- </Tooltip>
- );
- if (this.props.isFlagged) {
- flagVisible = 'visible';
- flag = (
- <span
- className='icon'
- dangerouslySetInnerHTML={{__html: flagIcon}}
- />
- );
- flagFunc = this.unflagPost;
- flagTooltip = (
- <Tooltip id='flagTooltip'>
- <FormattedMessage
- id='flag_post.unflag'
- defaultMessage='Unflag'
- />
- </Tooltip>
- );
- } else {
- flag = (
- <span
- className='icon'
- dangerouslySetInnerHTML={{__html: flagIcon}}
- />
- );
- flagFunc = this.flagPost;
- }
-
- let flagTrigger;
- if (!isEphemeral) {
- flagTrigger = (
- <OverlayTrigger
- key={'commentflagtooltipkey' + flagVisible}
- delayShow={Constants.OVERLAY_TIME_DELAY}
- placement='top'
- overlay={flagTooltip}
- >
- <a
- href='#'
- className={'flag-icon__container ' + flagVisible}
- onClick={flagFunc}
- >
- {flag}
- </a>
- </OverlayTrigger>
- );
- }
-
let react;
let reactOverlay;
@@ -668,7 +619,13 @@ export default class RhsComment extends React.Component {
<li className='col'>
{this.renderTimeTag(post, timeOptions)}
{pinnedBadge}
- {flagTrigger}
+ <PostFlagIcon
+ idPrefix={'rhsCommentFlag'}
+ idCount={idCount}
+ postId={post.id}
+ isFlagged={this.props.isFlagged}
+ isEphemeral={isEphemeral}
+ />
</li>
{options}
</ul>
@@ -689,6 +646,7 @@ export default class RhsComment extends React.Component {
RhsComment.propTypes = {
post: React.PropTypes.object,
+ lastPostCount: React.PropTypes.number,
user: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
compactDisplay: React.PropTypes.bool,
diff --git a/webapp/components/rhs_root_post.jsx b/webapp/components/rhs_root_post.jsx
index 65bc52f73..bf9748636 100644
--- a/webapp/components/rhs_root_post.jsx
+++ b/webapp/components/rhs_root_post.jsx
@@ -8,6 +8,7 @@ import FileAttachmentListContainer from './file_attachment_list_container.jsx';
import ProfilePicture from 'components/profile_picture.jsx';
import ReactionListContainer from 'components/post_view/components/reaction_list_container.jsx';
import RhsDropdown from 'components/rhs_dropdown.jsx';
+import PostFlagIcon from 'components/common/post_flag_icon.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx';
@@ -24,7 +25,7 @@ import ReactDOM from 'react-dom';
import Constants from 'utils/constants.jsx';
import DelayedAction from 'utils/delayed_action.jsx';
-import {Tooltip, OverlayTrigger, Overlay} from 'react-bootstrap';
+import {Overlay} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
@@ -203,7 +204,6 @@ export default class RhsRootPost extends React.Component {
const mattermostLogo = Constants.MATTERMOST_ICON_SVG;
var timestamp = user ? user.last_picture_update : 0;
var channel = ChannelStore.get(post.channel_id);
- const flagIcon = Constants.FLAG_ICON_SVG;
this.canDelete = PostUtils.canDeletePost(post);
this.canEdit = PostUtils.canEditPost(post, this.editDisableAction);
@@ -530,44 +530,6 @@ export default class RhsRootPost extends React.Component {
const profilePicContainer = (<div className='post__img'>{profilePic}</div>);
- let flag;
- let flagFunc;
- let flagVisible = '';
- let flagTooltip = (
- <Tooltip id='flagTooltip'>
- <FormattedMessage
- id='flag_post.flag'
- defaultMessage='Flag for follow up'
- />
- </Tooltip>
- );
- if (this.props.isFlagged) {
- flagVisible = 'visible';
- flag = (
- <span
- className='icon'
- dangerouslySetInnerHTML={{__html: flagIcon}}
- />
- );
- flagFunc = this.unflagPost;
- flagTooltip = (
- <Tooltip id='flagTooltip'>
- <FormattedMessage
- id='flag_post.unflag'
- defaultMessage='Unflag'
- />
- </Tooltip>
- );
- } else {
- flag = (
- <span
- className='icon'
- dangerouslySetInnerHTML={{__html: flagIcon}}
- />
- );
- flagFunc = this.flagPost;
- }
-
let pinnedBadge;
if (post.is_pinned) {
pinnedBadge = (
@@ -601,20 +563,11 @@ export default class RhsRootPost extends React.Component {
<li className='col'>
{this.renderTimeTag(post, timeOptions)}
{pinnedBadge}
- <OverlayTrigger
- key={'rootpostflagtooltipkey' + flagVisible}
- delayShow={Constants.OVERLAY_TIME_DELAY}
- placement='top'
- overlay={flagTooltip}
- >
- <a
- href='#'
- className={'flag-icon__container ' + flagVisible}
- onClick={flagFunc}
- >
- {flag}
- </a>
- </OverlayTrigger>
+ <PostFlagIcon
+ idPrefix={'rhsRootPostFlag'}
+ postId={post.id}
+ isFlagged={this.props.isFlagged}
+ />
</li>
<li className='col col__reply'>
{reactOverlay}
@@ -645,6 +598,7 @@ RhsRootPost.defaultProps = {
};
RhsRootPost.propTypes = {
post: React.PropTypes.object.isRequired,
+ lastPostCount: React.PropTypes.number,
user: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
commentCount: React.PropTypes.number,
diff --git a/webapp/components/rhs_thread.jsx b/webapp/components/rhs_thread.jsx
index 1b4eb720f..82e54f6ff 100644
--- a/webapp/components/rhs_thread.jsx
+++ b/webapp/components/rhs_thread.jsx
@@ -321,18 +321,15 @@ export default class RhsThread extends React.Component {
}
render() {
- const postsArray = this.state.postsArray;
- const selected = this.state.selected;
- const profiles = this.state.profiles || {};
-
- if (postsArray == null || selected == null) {
+ if (this.state.postsArray == null || this.state.selected == null) {
return (
<div/>
);
}
- const rootPostDay = Utils.getDateForUnixTicks(selected.create_at);
- let previousPostDay = rootPostDay;
+ const postsArray = this.state.postsArray;
+ const selected = this.state.selected;
+ const profiles = this.state.profiles || {};
let profile;
if (UserStore.getCurrentId() === selected.user_id) {
@@ -351,8 +348,12 @@ export default class RhsThread extends React.Component {
rootStatus = this.state.statuses[selected.user_id] || 'offline';
}
+ const rootPostDay = Utils.getDateForUnixTicks(selected.create_at);
+ let previousPostDay = rootPostDay;
+
const commentsLists = [];
- for (let i = 0; i < postsArray.length; i++) {
+ const postsLength = postsArray.length;
+ for (let i = 0; i < postsLength; i++) {
const comPost = postsArray[i];
let p;
if (UserStore.getCurrentId() === comPost.user_id) {
@@ -371,10 +372,7 @@ export default class RhsThread extends React.Component {
status = this.state.statuses[p.id] || 'offline';
}
- const keyPrefix = comPost.id ? comPost.id : comPost.pending_post_id;
-
const currentPostDay = Utils.getDateForUnixTicks(comPost.create_at);
-
if (currentPostDay.toDateString() !== previousPostDay.toDateString()) {
previousPostDay = currentPostDay;
commentsLists.push(
@@ -383,11 +381,14 @@ export default class RhsThread extends React.Component {
/>);
}
+ const keyPrefix = comPost.id ? comPost.id : comPost.pending_post_id;
+ const reverseCount = postsLength - i - 1;
commentsLists.push(
<div key={keyPrefix + 'commentKey'}>
<Comment
ref={comPost.id}
post={comPost}
+ lastPostCount={(reverseCount >= 0 && reverseCount < Constants.TEST_ID_COUNT) ? reverseCount : -1}
user={p}
currentUser={this.props.currentUser}
compactDisplay={this.state.compactDisplay}
@@ -431,12 +432,12 @@ export default class RhsThread extends React.Component {
className='post-right__scroll'
>
<DateSeparator
- date={rootPostDay.toDateString()}
+ date={rootPostDay}
/>
<RootPost
ref={selected.id}
post={selected}
- commentCount={postsArray.length}
+ commentCount={postsLength}
user={profile}
currentUser={this.props.currentUser}
compactDisplay={this.state.compactDisplay}
@@ -456,7 +457,7 @@ export default class RhsThread extends React.Component {
<CreateComment
channelId={selected.channel_id}
rootId={selected.id}
- latestPostId={postsArray.length > 0 ? postsArray[postsArray.length - 1].id : selected.id}
+ latestPostId={postsLength > 0 ? postsArray[postsLength - 1].id : selected.id}
/>
</div>
</div>
diff --git a/webapp/components/search_bar.jsx b/webapp/components/search_bar.jsx
index 23f7d1762..1ed8445f8 100644
--- a/webapp/components/search_bar.jsx
+++ b/webapp/components/search_bar.jsx
@@ -248,6 +248,8 @@ export default class SearchBar extends React.Component {
let mentionBtn;
let flagBtn;
if (this.props.showMentionFlagBtns) {
+ var mentionBtnClass = SearchStore.isMentionSearch ? 'active' : '';
+
mentionBtn = (
<div
className='dropdown channel-header__links'
@@ -262,6 +264,7 @@ export default class SearchBar extends React.Component {
href='#'
type='button'
onClick={this.searchMentions}
+ className={mentionBtnClass}
>
{'@'}
</a>
@@ -269,6 +272,8 @@ export default class SearchBar extends React.Component {
</div>
);
+ var flagBtnClass = SearchStore.isFlaggedPosts ? 'active' : '';
+
flagBtn = (
<div
className='dropdown channel-header__links'
@@ -283,6 +288,7 @@ export default class SearchBar extends React.Component {
href='#'
type='button'
onClick={this.getFlagged}
+ className={flagBtnClass}
>
<span
className='icon icon__flag'
diff --git a/webapp/components/search_results.jsx b/webapp/components/search_results.jsx
index 682b04e2a..64e5a7c93 100644
--- a/webapp/components/search_results.jsx
+++ b/webapp/components/search_results.jsx
@@ -267,7 +267,7 @@ export default class SearchResults extends React.Component {
</div>
);
} else {
- ctls = results.order.map(function mymap(id) {
+ ctls = results.order.map(function searchResults(id, idx, arr) {
const post = results.posts[id];
let profile;
if (UserStore.getCurrentId() === post.user_id) {
@@ -285,12 +285,16 @@ export default class SearchResults extends React.Component {
if (this.state.flaggedPosts) {
isFlagged = this.state.flaggedPosts.get(post.id) === 'true';
}
+
+ const reverseCount = arr.length - idx - 1;
+
return (
<SearchResultsItem
key={post.id}
channel={this.state.channels.get(post.channel_id)}
compactDisplay={this.state.compactDisplay}
post={post}
+ lastPostCount={(reverseCount >= 0 && reverseCount < Constants.TEST_ID_COUNT) ? reverseCount : -1}
user={profile}
term={searchTerm}
isMentionSearch={this.props.isMentionSearch}
diff --git a/webapp/components/search_results_item.jsx b/webapp/components/search_results_item.jsx
index 09ea8c427..d3b7cfe00 100644
--- a/webapp/components/search_results_item.jsx
+++ b/webapp/components/search_results_item.jsx
@@ -6,6 +6,7 @@ import PostMessageContainer from 'components/post_view/components/post_message_c
import UserProfile from './user_profile.jsx';
import FileAttachmentListContainer from './file_attachment_list_container.jsx';
import ProfilePicture from './profile_picture.jsx';
+import CommentIcon from 'components/common/comment_icon.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
@@ -13,12 +14,12 @@ import UserStore from 'stores/user_store.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
import {flagPost, unflagPost} from 'actions/post_actions.jsx';
+import PostFlagIcon from 'components/common/post_flag_icon.jsx';
import * as Utils from 'utils/utils.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
import Constants from 'utils/constants.jsx';
-import {Tooltip, OverlayTrigger} from 'react-bootstrap';
const ActionTypes = Constants.ActionTypes;
import React from 'react';
@@ -114,7 +115,11 @@ export default class SearchResultsItem extends React.Component {
const timestamp = UserStore.getCurrentUser().last_picture_update;
const user = this.props.user || {};
const post = this.props.post;
- const flagIcon = Constants.FLAG_ICON_SVG;
+
+ let idCount = -1;
+ if (this.props.lastPostCount >= 0 && this.props.lastPostCount < Constants.TEST_ID_COUNT) {
+ idCount = this.props.lastPostCount;
+ }
if (channel) {
channelName = channel.display_name;
@@ -185,73 +190,23 @@ export default class SearchResultsItem extends React.Component {
</p>
);
} else {
- let flag;
- let flagFunc;
- let flagVisible = '';
- let flagTooltip = (
- <Tooltip id='flagTooltip'>
- <FormattedMessage
- id='flag_post.flag'
- defaultMessage='Flag for follow up'
- />
- </Tooltip>
- );
-
- if (this.props.isFlagged) {
- flagVisible = 'visible';
- flagTooltip = (
- <Tooltip id='flagTooltip'>
- <FormattedMessage
- id='flag_post.unflag'
- defaultMessage='Unflag'
- />
- </Tooltip>
- );
- flagFunc = this.unflagPost;
- flag = (
- <span
- className='icon'
- dangerouslySetInnerHTML={{__html: flagIcon}}
- />
- );
- } else {
- flag = (
- <span
- className='icon'
- dangerouslySetInnerHTML={{__html: flagIcon}}
- />
- );
- flagFunc = this.flagPost;
- }
-
flagContent = (
- <OverlayTrigger
- delayShow={Constants.OVERLAY_TIME_DELAY}
- placement='top'
- overlay={flagTooltip}
- >
- <a
- href='#'
- className={'flag-icon__container ' + flagVisible}
- onClick={flagFunc}
- >
- {flag}
- </a>
- </OverlayTrigger>
+ <PostFlagIcon
+ idPrefix={'searchPostFlag'}
+ idCount={idCount}
+ postId={post.id}
+ isFlagged={this.props.isFlagged}
+ />
);
rhsControls = (
<li className='col__controls'>
- <a
- href='#'
- className='comment-icon__container search-item__comment'
- onClick={this.handleFocusRHSClick}
- >
- <span
- className='comment-icon'
- dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
- />
- </a>
+ <CommentIcon
+ idPrefix={'searchCommentIcon'}
+ idCount={idCount}
+ handleCommentClick={this.handleFocusRHSClick}
+ searchStyle={'search-item__comment'}
+ />
<a
onClick={
() => {
@@ -364,6 +319,7 @@ export default class SearchResultsItem extends React.Component {
SearchResultsItem.propTypes = {
post: React.PropTypes.object,
+ lastPostCount: React.PropTypes.number,
user: React.PropTypes.object,
channel: React.PropTypes.object,
compactDisplay: React.PropTypes.bool,
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index f92bd13f0..fd3c2ef85 100755
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -545,10 +545,13 @@
"admin.mfa.bannerDesc": "<a href='https://docs.mattermost.com/deployment/auth.html' target='_blank'>Multi-factor authentication</a> is available for accounts with AD/LDAP or email login. If other login methods are used, MFA should be configured with the authentication provider.",
"admin.mfa.cluster": "High",
"admin.mfa.title": "Multi-factor Authentication",
+ "admin.nav.administratorsGuide": "Administrator's Guide",
+ "admin.nav.commercialSupport": "Commercial Support",
"admin.nav.help": "Help",
"admin.nav.logout": "Logout",
"admin.nav.report": "Report a Problem",
"admin.nav.switch": "Team Selection",
+ "admin.nav.troubleshootingForum": "Troubleshooting Forum",
"admin.notifications.email": "Email",
"admin.notifications.push": "Mobile Push",
"admin.notifications.title": "Notification Settings",
@@ -1313,8 +1316,8 @@
"error.not_found.link_message": "Back to Mattermost",
"error.not_found.message": "The page you were trying to reach does not exist",
"error.not_found.title": "Page not found",
- "error_bar.expired": "Enterprise license is expired and some features may be disabled. <a href='{link}' target='_blank'>Please renew.</a>",
- "error_bar.expiring": "Enterprise license expires on {date}. <a href='{link}' target='_blank'>Please renew.</a>",
+ "error_bar.expired": "Enterprise license is expired and some features may be disabled. <a href='{link}' target='_blank'>Please renew</a>.",
+ "error_bar.expiring": "Enterprise license expires on {date}. <a href='{link}' target='_blank'>Please renew</a>.",
"error_bar.past_grace": "Enterprise license is expired and some features may be disabled. Please contact your System Administrator for details.",
"error_bar.preview_mode": "Preview Mode: Email notifications have not been configured",
"error_bar.site_url": "Please configure your {docsLink} in the {link}.",
diff --git a/webapp/root.jsx b/webapp/root.jsx
index 94645b661..6c7643f17 100644
--- a/webapp/root.jsx
+++ b/webapp/root.jsx
@@ -101,6 +101,17 @@ function preRenderSetup(callwhendone) {
} else {
I18n.safariFix(afterIntl);
}
+
+ // Prevent drag and drop files from navigating away from the app
+ document.addEventListener('drop', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ });
+
+ document.addEventListener('dragover', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ });
}
function renderRootComponent() {
diff --git a/webapp/sass/layout/_headers.scss b/webapp/sass/layout/_headers.scss
index 79bba495d..e4325c149 100644
--- a/webapp/sass/layout/_headers.scss
+++ b/webapp/sass/layout/_headers.scss
@@ -464,12 +464,20 @@
text-decoration: none;
&:hover {
- @include opacity(1);
+ @include opacity(0.7);
}
&:focus {
color: inherit;
}
+
+ &.active {
+ color: $primary-color;
+ @include opacity(1);
+ .icon {
+ fill: $primary-color;
+ }
+ }
}
}
diff --git a/webapp/sass/routes/_about-modal.scss b/webapp/sass/routes/_about-modal.scss
index 4506eac4e..46c1676ea 100644
--- a/webapp/sass/routes/_about-modal.scss
+++ b/webapp/sass/routes/_about-modal.scss
@@ -1,9 +1,13 @@
@charset 'UTF-8';
-.app__body {
+.modal {
+ .modal-content {
+ @include box-shadow(0 0 10px rgba($black, .5));
+ border-radius: $border-rad;
+ }
- .modal {
- .about-modal {
+ .about-modal {
+ .modal-content {
.modal-header {
background: transparent;
border: none;
@@ -25,69 +29,68 @@
margin: 0 4px 0 8px;
}
}
+ }
- .modal-body {
- padding: 20px 25px 5px;
- }
+ .modal-body {
+ padding: 20px 25px 10px;
+ }
- &.large {
- .modal-body {
- padding-bottom: 20px;
- }
+ &.large {
+ .modal-body {
+ padding-bottom: 20px;
}
+ }
- .about-modal__content {
- @include clearfix;
- @include display-flex;
- @include flex-direction(row);
- padding: 1em 0 3em;
- }
+ .about-modal__content {
+ @include clearfix;
+ @include display-flex;
+ @include flex-direction(row);
+ padding: 1em 0 3em;
+ }
- .about-modal__copyright {
- @include opacity(.6);
- margin-top: .5em;
- }
+ .about-modal__copyright {
+ @include opacity(.6);
+ margin-top: .5em;
+ }
- .about-modal__footer {
- font-size: 13.5px;
- }
+ .about-modal__footer {
+ font-size: 13.5px;
+ }
- .about-modal__title {
- line-height: 1.5;
- margin: 0 0 10px;
- }
+ .about-modal__title {
+ line-height: 1.5;
+ margin: 0 0 10px;
+ }
- .about-modal__subtitle {
- @include opacity(.6);
- }
+ .about-modal__subtitle {
+ @include opacity(.6);
+ }
- .about-modal__hash {
- @include opacity(.4);
- font-size: .75em;
- text-align: right;
+ .about-modal__hash {
+ @include opacity(.4);
+ font-size: .75em;
+ text-align: right;
- p {
- &:first-child {
- float: left;
- text-align: left;
- }
+ p {
+ &:first-child {
+ float: left;
+ text-align: left;
}
}
+ }
- .about-modal__logo {
- @include opacity(.9);
- padding: 0 40px 0 20px;
+ .about-modal__logo {
+ @include opacity(.9);
+ padding: 0 40px 0 20px;
- svg {
- height: 125px;
- width: 125px;
- }
+ svg {
+ height: 125px;
+ width: 125px;
+ }
- path {
- fill: inherit;
- }
+ path {
+ fill: inherit;
}
}
}
-
-}
+} \ No newline at end of file
diff --git a/webapp/stores/search_store.jsx b/webapp/stores/search_store.jsx
index 2ccf02f94..dd4b97522 100644
--- a/webapp/stores/search_store.jsx
+++ b/webapp/stores/search_store.jsx
@@ -122,7 +122,7 @@ class SearchStoreClass extends EventEmitter {
togglePinPost(postId, isPinned) {
const results = this.getSearchResults();
- if (results == null) {
+ if (results == null || results.posts == null) {
return;
}
diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx
index 2b6c5d6be..619d37a74 100644
--- a/webapp/utils/constants.jsx
+++ b/webapp/utils/constants.jsx
@@ -952,7 +952,8 @@ export const Constants = {
AUTOCOMPLETE_TIMEOUT: 100,
ANIMATION_TIMEOUT: 1000,
SEARCH_TIMEOUT_MILLISECONDS: 100,
- DIAGNOSTICS_SEGMENT_KEY: 'fwb7VPbFeQ7SKp3wHm1RzFUuXZudqVok'
+ DIAGNOSTICS_SEGMENT_KEY: 'fwb7VPbFeQ7SKp3wHm1RzFUuXZudqVok',
+ TEST_ID_COUNT: 10
};
export default Constants;