summaryrefslogtreecommitdiffstats
path: root/webapp/components
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/components')
-rw-r--r--webapp/components/channel_header.jsx49
-rw-r--r--webapp/components/post_view/components/post.jsx8
-rw-r--r--webapp/components/post_view/components/post_header.jsx4
-rw-r--r--webapp/components/post_view/components/post_info.jsx119
-rw-r--r--webapp/components/post_view/components/post_list.jsx9
-rw-r--r--webapp/components/post_view/post_focus_view_controller.jsx13
-rw-r--r--webapp/components/post_view/post_view_controller.jsx11
-rw-r--r--webapp/components/rhs_comment.jsx116
-rw-r--r--webapp/components/rhs_header_post.jsx59
-rw-r--r--webapp/components/rhs_root_post.jsx129
-rw-r--r--webapp/components/rhs_thread.jsx22
-rw-r--r--webapp/components/search_results.jsx42
-rw-r--r--webapp/components/search_results_header.jsx10
-rw-r--r--webapp/components/search_results_item.jsx86
-rw-r--r--webapp/components/sidebar_right.jsx20
-rw-r--r--webapp/components/sidebar_right_menu.jsx19
16 files changed, 646 insertions, 70 deletions
diff --git a/webapp/components/channel_header.jsx b/webapp/components/channel_header.jsx
index f26105c7a..66cd61245 100644
--- a/webapp/components/channel_header.jsx
+++ b/webapp/components/channel_header.jsx
@@ -26,20 +26,19 @@ import PreferenceStore from 'stores/preference_store.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as Utils from 'utils/utils.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
-import * as AsyncClient from 'utils/async_client.jsx';
import Client from 'client/web_client.jsx';
+import * as AsyncClient from 'utils/async_client.jsx';
+import {getFlaggedPosts} from 'actions/post_actions.jsx';
+
import Constants from 'utils/constants.jsx';
const UserStatuses = Constants.UserStatuses;
+const ActionTypes = Constants.ActionTypes;
+import React from 'react';
import {FormattedMessage} from 'react-intl';
import {browserHistory} from 'react-router/es6';
-
-const ActionTypes = Constants.ActionTypes;
-
import {Tooltip, OverlayTrigger, Popover} from 'react-bootstrap';
-import React from 'react';
-
export default class ChannelHeader extends React.Component {
constructor(props) {
super(props);
@@ -50,6 +49,7 @@ export default class ChannelHeader extends React.Component {
this.showRenameChannelModal = this.showRenameChannelModal.bind(this);
this.hideRenameChannelModal = this.hideRenameChannelModal.bind(this);
this.openRecentMentions = this.openRecentMentions.bind(this);
+ this.getFlagged = this.getFlagged.bind(this);
const state = this.getStateFromStores();
state.showEditChannelPurposeModal = false;
@@ -159,6 +159,11 @@ export default class ChannelHeader extends React.Component {
});
}
+ getFlagged(e) {
+ e.preventDefault();
+ getFlaggedPosts();
+ }
+
openRecentMentions(e) {
if (Utils.cmdOrCtrlPressed(e) && e.shiftKey && e.keyCode === Constants.KeyCodes.M) {
e.preventDefault();
@@ -220,6 +225,8 @@ export default class ChannelHeader extends React.Component {
}
render() {
+ const flagIcon = Constants.FLAG_ICON_OUTLINE_SVG;
+
if (!this.validState()) {
return null;
}
@@ -233,6 +240,16 @@ export default class ChannelHeader extends React.Component {
/>
</Tooltip>
);
+
+ const flaggedTooltip = (
+ <Tooltip id='flaggedTooltip'>
+ <FormattedMessage
+ id='channel_header.flagged'
+ defaultMessage='Flagged Posts'
+ />
+ </Tooltip>
+ );
+
const popoverContent = (
<Popover
id='header-popover'
@@ -592,6 +609,26 @@ export default class ChannelHeader extends React.Component {
</OverlayTrigger>
</div>
</th>
+ <th>
+ <div className='dropdown channel-header__links'>
+ <OverlayTrigger
+ delayShow={Constants.OVERLAY_TIME_DELAY}
+ placement='bottom'
+ overlay={flaggedTooltip}
+ >
+ <a
+ href='#'
+ type='button'
+ onClick={this.getFlagged}
+ >
+ <span
+ className='icon icon__flag'
+ dangerouslySetInnerHTML={{__html: flagIcon}}
+ />
+ </a>
+ </OverlayTrigger>
+ </div>
+ </th>
</tr>
</tbody>
</table>
diff --git a/webapp/components/post_view/components/post.jsx b/webapp/components/post_view/components/post.jsx
index 3fdd8094e..038bcab78 100644
--- a/webapp/components/post_view/components/post.jsx
+++ b/webapp/components/post_view/components/post.jsx
@@ -100,6 +100,10 @@ export default class Post extends React.Component {
return true;
}
+ if (nextProps.isFlagged !== this.props.isFlagged) {
+ return true;
+ }
+
if (!Utils.areObjectsEqual(nextProps.user, this.props.user)) {
return true;
}
@@ -245,6 +249,7 @@ export default class Post extends React.Component {
compactDisplay={this.props.compactDisplay}
displayNameType={this.props.displayNameType}
useMilitaryTime={this.props.useMilitaryTime}
+ isFlagged={this.props.isFlagged}
/>
<PostBody
post={post}
@@ -281,5 +286,6 @@ Post.propTypes = {
commentCount: React.PropTypes.number,
isCommentMention: React.PropTypes.bool,
useMilitaryTime: React.PropTypes.bool.isRequired,
- emojis: React.PropTypes.object.isRequired
+ emojis: React.PropTypes.object.isRequired,
+ isFlagged: React.PropTypes.bool
};
diff --git a/webapp/components/post_view/components/post_header.jsx b/webapp/components/post_view/components/post_header.jsx
index 07b601baf..6c356126d 100644
--- a/webapp/components/post_view/components/post_header.jsx
+++ b/webapp/components/post_view/components/post_header.jsx
@@ -72,6 +72,7 @@ export default class PostHeader extends React.Component {
currentUser={this.props.currentUser}
compactDisplay={this.props.compactDisplay}
useMilitaryTime={this.props.useMilitaryTime}
+ isFlagged={this.props.isFlagged}
/>
</li>
</ul>
@@ -97,5 +98,6 @@ PostHeader.propTypes = {
sameUser: React.PropTypes.bool.isRequired,
compactDisplay: React.PropTypes.bool,
displayNameType: React.PropTypes.string,
- useMilitaryTime: React.PropTypes.bool.isRequired
+ useMilitaryTime: React.PropTypes.bool.isRequired,
+ isFlagged: React.PropTypes.bool.isRequired
};
diff --git a/webapp/components/post_view/components/post_info.jsx b/webapp/components/post_view/components/post_info.jsx
index ba6a9a982..d48d97ba1 100644
--- a/webapp/components/post_view/components/post_info.jsx
+++ b/webapp/components/post_view/components/post_info.jsx
@@ -2,17 +2,21 @@
// See License.txt for license information.
import $ from 'jquery';
-import * as Utils from 'utils/utils.jsx';
+
import PostTime from './post_time.jsx';
+
import * as GlobalActions from 'actions/global_actions.jsx';
+import * as PostActions from 'actions/post_actions.jsx';
+
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
+import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
-
-import {FormattedMessage} from 'react-intl';
+import {Tooltip, OverlayTrigger} from 'react-bootstrap';
import React from 'react';
+import {FormattedMessage} from 'react-intl';
export default class PostInfo extends React.Component {
constructor(props) {
@@ -21,7 +25,10 @@ export default class PostInfo extends React.Component {
this.handleDropdownClick = this.handleDropdownClick.bind(this);
this.handlePermalink = this.handlePermalink.bind(this);
this.removePost = this.removePost.bind(this);
+ this.flagPost = this.flagPost.bind(this);
+ this.unflagPost = this.unflagPost.bind(this);
}
+
handleDropdownClick(e) {
var position = $('#post-list').height() - $(e.target).offset().top;
var dropdown = $(e.target).closest('.col__reply').find('.dropdown-menu');
@@ -29,10 +36,12 @@ export default class PostInfo extends React.Component {
dropdown.addClass('bottom');
}
}
+
componentDidMount() {
$('#post_dropdown' + this.props.post.id).on('shown.bs.dropdown', () => this.props.handleDropdownOpened(true));
$('#post_dropdown' + this.props.post.id).on('hidden.bs.dropdown', () => this.props.handleDropdownOpened(false));
}
+
createDropdown() {
var post = this.props.post;
var isOwner = this.props.currentUser.id === post.user_id;
@@ -74,6 +83,44 @@ export default class PostInfo extends React.Component {
);
}
+ if (Utils.isMobile()) {
+ if (this.props.isFlagged) {
+ dropdownContents.push(
+ <li
+ key='mobileFlag'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.unflagPost}
+ >
+ <FormattedMessage
+ id='rhs_root.mobile.unflag'
+ defaultMessage='Unflag'
+ />
+ </a>
+ </li>
+ );
+ } else {
+ dropdownContents.push(
+ <li
+ key='mobileFlag'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.flagPost}
+ >
+ <FormattedMessage
+ id='rhs_root.mobile.flag'
+ defaultMessage='Flag'
+ />
+ </a>
+ </li>
+ );
+ }
+ }
+
dropdownContents.push(
<li
key='copyLink'
@@ -186,12 +233,23 @@ export default class PostInfo extends React.Component {
);
}
+ flagPost(e) {
+ e.preventDefault();
+ PostActions.flagPost(this.props.post.id);
+ }
+
+ unflagPost(e) {
+ e.preventDefault();
+ PostActions.unflagPost(this.props.post.id);
+ }
+
render() {
var post = this.props.post;
var comments = '';
var showCommentClass = '';
var highlightMentionClass = '';
var commentCountText = this.props.commentCount;
+ const flagIcon = Constants.FLAG_ICON_SVG;
if (this.props.commentCount >= 1) {
showCommentClass = ' icon--show';
@@ -240,6 +298,44 @@ 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;
+ }
+
return (
<ul className='post__header--info'>
<li className='col'>
@@ -249,6 +345,20 @@ export default class PostInfo extends React.Component {
compactDisplay={this.props.compactDisplay}
useMilitaryTime={this.props.useMilitaryTime}
/>
+ <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>
</li>
{options}
</ul>
@@ -274,5 +384,6 @@ PostInfo.propTypes = {
sameUser: React.PropTypes.bool.isRequired,
currentUser: React.PropTypes.object.isRequired,
compactDisplay: React.PropTypes.bool,
- useMilitaryTime: React.PropTypes.bool.isRequired
+ useMilitaryTime: React.PropTypes.bool.isRequired,
+ isFlagged: React.PropTypes.bool
};
diff --git a/webapp/components/post_view/components/post_list.jsx b/webapp/components/post_view/components/post_list.jsx
index befd1a10d..95b30a9d7 100644
--- a/webapp/components/post_view/components/post_list.jsx
+++ b/webapp/components/post_view/components/post_list.jsx
@@ -284,6 +284,11 @@ export default class PostList extends React.Component {
}
}
+ let isFlagged = false;
+ if (this.props.flaggedPosts) {
+ isFlagged = this.props.flaggedPosts.get(post.id) === 'true';
+ }
+
const postCtl = (
<Post
key={keyPrefix + 'postKey'}
@@ -305,6 +310,7 @@ export default class PostList extends React.Component {
previewCollapsed={this.props.previewsCollapsed}
useMilitaryTime={this.props.useMilitaryTime}
emojis={this.props.emojis}
+ isFlagged={isFlagged}
/>
);
@@ -572,5 +578,6 @@ PostList.propTypes = {
previewsCollapsed: React.PropTypes.string,
useMilitaryTime: React.PropTypes.bool.isRequired,
isFocusPost: React.PropTypes.bool,
- emojis: React.PropTypes.object.isRequired
+ emojis: React.PropTypes.object.isRequired,
+ flaggedPosts: React.PropTypes.object
};
diff --git a/webapp/components/post_view/post_focus_view_controller.jsx b/webapp/components/post_view/post_focus_view_controller.jsx
index f8738e056..4a7d312f5 100644
--- a/webapp/components/post_view/post_focus_view_controller.jsx
+++ b/webapp/components/post_view/post_focus_view_controller.jsx
@@ -8,6 +8,7 @@ import EmojiStore from 'stores/emoji_store.jsx';
import PostStore from 'stores/post_store.jsx';
import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
+import PreferenceStore from 'stores/preference_store.jsx';
import Constants from 'utils/constants.jsx';
const ScrollTypes = Constants.ScrollTypes;
@@ -22,6 +23,7 @@ export default class PostFocusView extends React.Component {
this.onPostsChange = this.onPostsChange.bind(this);
this.onUserChange = this.onUserChange.bind(this);
this.onEmojiChange = this.onEmojiChange.bind(this);
+ this.onPreferenceChange = this.onPreferenceChange.bind(this);
this.onPostListScroll = this.onPostListScroll.bind(this);
const focusedPostId = PostStore.getFocusedPostId();
@@ -41,7 +43,8 @@ export default class PostFocusView extends React.Component {
scrollPostId: focusedPostId,
atTop: PostStore.getVisibilityAtTop(focusedPostId),
atBottom: PostStore.getVisibilityAtBottom(focusedPostId),
- emojis: EmojiStore.getEmojis()
+ emojis: EmojiStore.getEmojis(),
+ flaggedPosts: PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST)
};
}
@@ -50,6 +53,7 @@ export default class PostFocusView extends React.Component {
PostStore.addChangeListener(this.onPostsChange);
UserStore.addChangeListener(this.onUserChange);
EmojiStore.addChangeListener(this.onEmojiChange);
+ PreferenceStore.addChangeListener(this.onPreferenceChange);
}
componentWillUnmount() {
@@ -98,6 +102,12 @@ export default class PostFocusView extends React.Component {
});
}
+ onPreferenceChange() {
+ this.setState({
+ flaggedPosts: PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST)
+ });
+ }
+
onPostListScroll() {
this.setState({scrollType: ScrollTypes.FREE});
}
@@ -128,6 +138,7 @@ export default class PostFocusView extends React.Component {
postsToHighlight={postsToHighlight}
isFocusPost={true}
emojis={this.state.emojis}
+ flaggedPosts={this.state.flaggedPosts}
/>
);
}
diff --git a/webapp/components/post_view/post_view_controller.jsx b/webapp/components/post_view/post_view_controller.jsx
index a7583fa38..1dd5e9176 100644
--- a/webapp/components/post_view/post_view_controller.jsx
+++ b/webapp/components/post_view/post_view_controller.jsx
@@ -58,7 +58,8 @@ export default class PostViewController extends React.Component {
compactDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT,
previewsCollapsed: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.COLLAPSE_DISPLAY, 'false'),
useMilitaryTime: PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false),
- emojis: EmojiStore.getEmojis()
+ emojis: EmojiStore.getEmojis(),
+ flaggedPosts: PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST)
};
}
@@ -87,7 +88,8 @@ export default class PostViewController extends React.Component {
displayPostsInCenter: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_CENTERED,
compactDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT,
previewsCollapsed: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.COLLAPSE_DISPLAY, 'false') + previewSuffix,
- useMilitaryTime: PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false)
+ useMilitaryTime: PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false),
+ flaggedPosts: PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST)
});
}
@@ -224,6 +226,10 @@ export default class PostViewController extends React.Component {
return true;
}
+ if (!Utils.areObjectsEqual(nextState.flaggedPosts, this.state.flaggedPosts)) {
+ return true;
+ }
+
if (nextState.lastViewed !== this.state.lastViewed) {
return true;
}
@@ -292,6 +298,7 @@ export default class PostViewController extends React.Component {
compactDisplay={this.state.compactDisplay}
previewsCollapsed={this.state.previewsCollapsed}
useMilitaryTime={this.state.useMilitaryTime}
+ flaggedPosts={this.state.flaggedPosts}
lastViewed={this.state.lastViewed}
emojis={this.state.emojis}
ownNewMessage={this.state.ownNewMessage}
diff --git a/webapp/components/rhs_comment.jsx b/webapp/components/rhs_comment.jsx
index ed1f71b1e..a90380510 100644
--- a/webapp/components/rhs_comment.jsx
+++ b/webapp/components/rhs_comment.jsx
@@ -9,12 +9,14 @@ import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
+import {flagPost, unflagPost} from 'actions/post_actions.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
import * as Utils from 'utils/utils.jsx';
import Client from 'client/web_client.jsx';
import Constants from 'utils/constants.jsx';
+import {Tooltip, OverlayTrigger} from 'react-bootstrap';
import {FormattedMessage, FormattedDate} from 'react-intl';
@@ -27,13 +29,17 @@ export default class RhsComment extends React.Component {
super(props);
this.handlePermalink = this.handlePermalink.bind(this);
+ this.flagPost = this.flagPost.bind(this);
+ this.unflagPost = this.unflagPost.bind(this);
this.state = {};
}
+
handlePermalink(e) {
e.preventDefault();
GlobalActions.showGetPostLinkModal(this.props.post);
}
+
shouldComponentUpdate(nextProps) {
if (nextProps.compactDisplay !== this.props.compactDisplay) {
return true;
@@ -43,6 +49,10 @@ export default class RhsComment extends React.Component {
return true;
}
+ if (nextProps.isFlagged !== this.props.isFlagged) {
+ return true;
+ }
+
if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
return true;
}
@@ -53,6 +63,17 @@ export default class RhsComment extends React.Component {
return false;
}
+
+ flagPost(e) {
+ e.preventDefault();
+ flagPost(this.props.post.id);
+ }
+
+ unflagPost(e) {
+ e.preventDefault();
+ unflagPost(this.props.post.id);
+ }
+
createDropdown() {
var post = this.props.post;
@@ -66,6 +87,44 @@ export default class RhsComment extends React.Component {
var dropdownContents = [];
+ if (Utils.isMobile()) {
+ if (this.props.isFlagged) {
+ dropdownContents.push(
+ <li
+ key='mobileFlag'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.unflagPost}
+ >
+ <FormattedMessage
+ id='rhs_root.mobile.unflag'
+ defaultMessage='Unflag'
+ />
+ </a>
+ </li>
+ );
+ } else {
+ dropdownContents.push(
+ <li
+ key='mobileFlag'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.flagPost}
+ >
+ <FormattedMessage
+ id='rhs_root.mobile.flag'
+ defaultMessage='Flag'
+ />
+ </a>
+ </li>
+ );
+ }
+ }
+
dropdownContents.push(
<li
key='rhs-root-permalink'
@@ -151,8 +210,10 @@ export default class RhsComment extends React.Component {
</div>
);
}
+
render() {
var post = this.props.post;
+ const flagIcon = Constants.FLAG_ICON_SVG;
var currentUserCss = '';
if (this.props.currentUser === post.user_id) {
@@ -225,6 +286,44 @@ 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;
+ }
+
return (
<div className={'post post--thread ' + currentUserCss + ' ' + compactClass}>
<div className='post__content'>
@@ -247,6 +346,20 @@ export default class RhsComment extends React.Component {
minute='2-digit'
/>
</time>
+ <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>
</li>
<li className='col col__reply'>
{dropdown}
@@ -271,5 +384,6 @@ RhsComment.propTypes = {
user: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
compactDisplay: React.PropTypes.bool,
- useMilitaryTime: React.PropTypes.bool.isRequired
+ useMilitaryTime: React.PropTypes.bool.isRequired,
+ isFlagged: React.PropTypes.bool
};
diff --git a/webapp/components/rhs_header_post.jsx b/webapp/components/rhs_header_post.jsx
index 8e54016fb..7b71bd7cc 100644
--- a/webapp/components/rhs_header_post.jsx
+++ b/webapp/components/rhs_header_post.jsx
@@ -4,7 +4,9 @@
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import Constants from 'utils/constants.jsx';
import {Tooltip, OverlayTrigger} from 'react-bootstrap';
+
import * as GlobalActions from 'actions/global_actions.jsx';
+import {getFlaggedPosts} from 'actions/post_actions.jsx';
import {FormattedMessage} from 'react-intl';
@@ -34,17 +36,21 @@ export default class RhsHeaderPost extends React.Component {
handleBack(e) {
e.preventDefault();
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH_TERM,
- term: this.props.fromSearch,
- do_search: true,
- is_mention_search: this.props.isMentionSearch
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
+ if (this.props.fromSearch) {
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_SEARCH_TERM,
+ term: this.props.fromSearch,
+ do_search: true,
+ is_mention_search: this.props.isMentionSearch
+ });
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_POST_SELECTED,
+ postId: null
+ });
+ } else if (this.props.fromFlaggedPosts) {
+ getFlaggedPosts();
+ }
}
render() {
let back;
@@ -57,14 +63,26 @@ export default class RhsHeaderPost extends React.Component {
</Tooltip>
);
- const backToResultsTooltip = (
- <Tooltip id='backToResultsTooltip'>
- <FormattedMessage
- id='rhs_header.backToResultsTooltip'
- defaultMessage='Back to Search Results'
- />
- </Tooltip>
- );
+ let backToResultsTooltip;
+ if (this.props.fromSearch) {
+ backToResultsTooltip = (
+ <Tooltip id='backToResultsTooltip'>
+ <FormattedMessage
+ id='rhs_header.backToResultsTooltip'
+ defaultMessage='Back to Search Results'
+ />
+ </Tooltip>
+ );
+ } else if (this.props.fromFlaggedPosts) {
+ backToResultsTooltip = (
+ <Tooltip id='backToResultsTooltip'>
+ <FormattedMessage
+ id='rhs_header.backToFlaggedTooltip'
+ defaultMessage='Back to Flagged Posts'
+ />
+ </Tooltip>
+ );
+ }
const expandSidebarTooltip = (
<Tooltip id='expandSidebarTooltip'>
@@ -84,7 +102,7 @@ export default class RhsHeaderPost extends React.Component {
</Tooltip>
);
- if (this.props.fromSearch) {
+ if (this.props.fromSearch || this.props.fromFlaggedPosts) {
back = (
<a
href='#'
@@ -161,6 +179,7 @@ RhsHeaderPost.defaultProps = {
RhsHeaderPost.propTypes = {
isMentionSearch: React.PropTypes.bool,
fromSearch: React.PropTypes.string,
+ fromFlaggedPosts: React.PropTypes.bool,
toggleSize: React.PropTypes.function,
shrink: React.PropTypes.function
};
diff --git a/webapp/components/rhs_root_post.jsx b/webapp/components/rhs_root_post.jsx
index cf8c3201a..423abcf82 100644
--- a/webapp/components/rhs_root_post.jsx
+++ b/webapp/components/rhs_root_post.jsx
@@ -1,19 +1,23 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import ChannelStore from 'stores/channel_store.jsx';
import UserProfile from './user_profile.jsx';
+import PostBodyAdditionalContent from 'components/post_view/components/post_body_additional_content.jsx';
+import FileAttachmentList from './file_attachment_list.jsx';
+
+import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx';
import TeamStore from 'stores/team_store.jsx';
-import * as TextFormatting from 'utils/text_formatting.jsx';
-import FileAttachmentList from './file_attachment_list.jsx';
-import PostBodyAdditionalContent from 'components/post_view/components/post_body_additional_content.jsx';
+
import * as GlobalActions from 'actions/global_actions.jsx';
+import {flagPost, unflagPost} from 'actions/post_actions.jsx';
import * as Utils from 'utils/utils.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
+import * as TextFormatting from 'utils/text_formatting.jsx';
import Constants from 'utils/constants.jsx';
+import {Tooltip, OverlayTrigger} from 'react-bootstrap';
import {FormattedMessage, FormattedDate} from 'react-intl';
@@ -24,13 +28,17 @@ export default class RhsRootPost extends React.Component {
super(props);
this.handlePermalink = this.handlePermalink.bind(this);
+ this.flagPost = this.flagPost.bind(this);
+ this.unflagPost = this.unflagPost.bind(this);
this.state = {};
}
+
handlePermalink(e) {
e.preventDefault();
GlobalActions.showGetPostLinkModal(this.props.post);
}
+
shouldComponentUpdate(nextProps) {
if (nextProps.compactDisplay !== this.props.compactDisplay) {
return true;
@@ -40,6 +48,10 @@ export default class RhsRootPost extends React.Component {
return true;
}
+ if (nextProps.isFlagged !== this.props.isFlagged) {
+ return true;
+ }
+
if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
return true;
}
@@ -50,6 +62,17 @@ export default class RhsRootPost extends React.Component {
return false;
}
+
+ flagPost(e) {
+ e.preventDefault();
+ flagPost(this.props.post.id);
+ }
+
+ unflagPost(e) {
+ e.preventDefault();
+ unflagPost(this.props.post.id);
+ }
+
render() {
const post = this.props.post;
const user = this.props.user;
@@ -59,6 +82,7 @@ export default class RhsRootPost extends React.Component {
const isSystemMessage = post.type && post.type.startsWith(Constants.SYSTEM_MESSAGE_PREFIX);
var timestamp = UserStore.getProfile(post.user_id).update_at;
var channel = ChannelStore.get(post.channel_id);
+ const flagIcon = Constants.FLAG_ICON_SVG;
var type = 'Post';
if (post.root_id.length > 0) {
@@ -91,6 +115,44 @@ export default class RhsRootPost extends React.Component {
var dropdownContents = [];
+ if (Utils.isMobile()) {
+ if (this.props.isFlagged) {
+ dropdownContents.push(
+ <li
+ key='mobileFlag'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.unflagPost}
+ >
+ <FormattedMessage
+ id='rhs_root.mobile.unflag'
+ defaultMessage='Unflag'
+ />
+ </a>
+ </li>
+ );
+ } else {
+ dropdownContents.push(
+ <li
+ key='mobileFlag'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.flagPost}
+ >
+ <FormattedMessage
+ id='rhs_root.mobile.flag'
+ defaultMessage='Flag'
+ />
+ </a>
+ </li>
+ );
+ }
+ }
+
dropdownContents.push(
<li
key='rhs-root-permalink'
@@ -246,6 +308,44 @@ export default class RhsRootPost 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;
+ }
+
return (
<div className={'post post--root post--thread ' + userCss + ' ' + systemMessageClass + ' ' + compactClass}>
<div className='post-right-channel__name'>{channelName}</div>
@@ -267,11 +367,23 @@ export default class RhsRootPost extends React.Component {
minute='2-digit'
/>
</time>
+ <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>
</li>
<li className='col col__reply'>
- <div>
- {rootOptions}
- </div>
+ {rootOptions}
</li>
</ul>
<div className='post__body'>
@@ -297,5 +409,6 @@ RhsRootPost.propTypes = {
currentUser: React.PropTypes.object.isRequired,
commentCount: React.PropTypes.number,
compactDisplay: React.PropTypes.bool,
- useMilitaryTime: React.PropTypes.bool.isRequired
+ useMilitaryTime: React.PropTypes.bool.isRequired,
+ isFlagged: React.PropTypes.bool
};
diff --git a/webapp/components/rhs_thread.jsx b/webapp/components/rhs_thread.jsx
index d99ace6d4..856a686cb 100644
--- a/webapp/components/rhs_thread.jsx
+++ b/webapp/components/rhs_thread.jsx
@@ -62,6 +62,7 @@ export default class RhsThread extends React.Component {
state.windowHeight = Utils.windowHeight();
state.profiles = JSON.parse(JSON.stringify(UserStore.getProfiles()));
state.compactDisplay = PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT;
+ state.flaggedPosts = PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST);
this.state = state;
}
@@ -121,6 +122,10 @@ export default class RhsThread extends React.Component {
return true;
}
+ if (!Utils.areObjectsEqual(nextState.flaggedPosts, this.state.flaggedPosts)) {
+ return true;
+ }
+
if (!Utils.areObjectsEqual(nextState.profiles, this.state.profiles)) {
return true;
}
@@ -151,7 +156,8 @@ export default class RhsThread extends React.Component {
onPreferenceChange() {
this.setState({
- compactDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT
+ compactDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT,
+ flaggedPosts: PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST)
});
this.forceUpdateInfo();
}
@@ -240,12 +246,18 @@ export default class RhsThread extends React.Component {
profile = profiles[selected.user_id];
}
+ let isRootFlagged = false;
+ if (this.state.flaggedPosts) {
+ isRootFlagged = this.state.flaggedPosts.get(selected.id) === 'true';
+ }
+
return (
<div className='post-right__container'>
<FileUploadOverlay overlayType='right'/>
<div className='search-bar__container sidebar--right__search-header'>{searchForm}</div>
<div className='sidebar-right__body'>
<RhsHeaderPost
+ fromFlaggedPosts={this.props.fromFlaggedPosts}
fromSearch={this.props.fromSearch}
isMentionSearch={this.props.isMentionSearch}
toggleSize={this.props.toggleSize}
@@ -268,6 +280,7 @@ export default class RhsThread extends React.Component {
currentUser={this.props.currentUser}
compactDisplay={this.state.compactDisplay}
useMilitaryTime={this.props.useMilitaryTime}
+ isFlagged={isRootFlagged}
/>
<div className='post-right-comments-container'>
{postsArray.map((comPost) => {
@@ -277,6 +290,11 @@ export default class RhsThread extends React.Component {
} else {
p = profiles[comPost.user_id];
}
+
+ let isFlagged = false;
+ if (this.state.flaggedPosts) {
+ isFlagged = this.state.flaggedPosts.get(comPost.id) === 'true';
+ }
return (
<Comment
ref={comPost.id}
@@ -286,6 +304,7 @@ export default class RhsThread extends React.Component {
currentUser={this.props.currentUser}
compactDisplay={this.state.compactDisplay}
useMilitaryTime={this.props.useMilitaryTime}
+ isFlagged={isFlagged}
/>
);
})}
@@ -311,6 +330,7 @@ RhsThread.defaultProps = {
RhsThread.propTypes = {
fromSearch: React.PropTypes.string,
+ fromFlaggedPosts: React.PropTypes.bool,
isMentionSearch: React.PropTypes.bool,
currentUser: React.PropTypes.object.isRequired,
useMilitaryTime: React.PropTypes.bool.isRequired,
diff --git a/webapp/components/search_results.jsx b/webapp/components/search_results.jsx
index 6431ff2c2..9e3092cca 100644
--- a/webapp/components/search_results.jsx
+++ b/webapp/components/search_results.jsx
@@ -7,6 +7,7 @@ import SearchStore from 'stores/search_store.jsx';
import UserStore from 'stores/user_store.jsx';
import SearchBox from './search_bar.jsx';
import * as Utils from 'utils/utils.jsx';
+import Constants from 'utils/constants.jsx';
import SearchResultsHeader from './search_results_header.jsx';
import SearchResultsItem from './search_results_item.jsx';
@@ -122,10 +123,44 @@ export default class SearchResults extends React.Component {
var noResults = (!results || !results.order || !results.order.length);
const searchTerm = this.state.searchTerm;
const profiles = this.state.profiles || {};
+ const flagIcon = Constants.FLAG_ICON_SVG;
var ctls = null;
- if (!searchTerm && noResults) {
+ if (this.props.isFlaggedPosts && noResults) {
+ ctls = (
+ <div className='sidebar--right__subheader'>
+ <ul>
+ <li>
+ <FormattedHTMLMessage
+ id='search_results.usageFlag1'
+ defaultMessage="You haven't flagged any messages yet."
+ />
+ </li>
+ <li>
+ <FormattedHTMLMessage
+ id='search_results.usageFlag2'
+ defaultMessage='You can add a flag to messages and comments by clicking the '
+ />
+ <span
+ className='usage__icon'
+ dangerouslySetInnerHTML={{__html: flagIcon}}
+ />
+ <FormattedHTMLMessage
+ id='search_results.usageFlag3'
+ defaultMessage=' icon next to the timestamp.'
+ />
+ </li>
+ <li>
+ <FormattedHTMLMessage
+ id='search_results.usageFlag4'
+ defaultMessage='Flags are a way to mark messages for follow up. Your flags are personal, and cannot be seen by other users.'
+ />
+ </li>
+ </ul>
+ </div>
+ );
+ } else if (!searchTerm && noResults) {
ctls = (
<div className='sidebar--right__subheader'>
<FormattedHTMLMessage
@@ -172,6 +207,7 @@ export default class SearchResults extends React.Component {
isMentionSearch={this.props.isMentionSearch}
useMilitaryTime={this.props.useMilitaryTime}
shrink={this.props.shrink}
+ isFlagged={this.props.isFlaggedPosts}
/>
);
}, this);
@@ -185,6 +221,7 @@ export default class SearchResults extends React.Component {
isMentionSearch={this.props.isMentionSearch}
toggleSize={this.props.toggleSize}
shrink={this.props.shrink}
+ isFlaggedPosts={this.props.isFlaggedPosts}
/>
<div
id='search-items-container'
@@ -202,5 +239,6 @@ SearchResults.propTypes = {
isMentionSearch: React.PropTypes.bool,
useMilitaryTime: React.PropTypes.bool.isRequired,
toggleSize: React.PropTypes.function,
- shrink: React.PropTypes.function
+ shrink: React.PropTypes.function,
+ isFlaggedPosts: React.PropTypes.bool
};
diff --git a/webapp/components/search_results_header.jsx b/webapp/components/search_results_header.jsx
index 7cb072b70..e0d57494c 100644
--- a/webapp/components/search_results_header.jsx
+++ b/webapp/components/search_results_header.jsx
@@ -89,6 +89,13 @@ export default class SearchResultsHeader extends React.Component {
defaultMessage='Recent Mentions'
/>
);
+ } else if (this.props.isFlaggedPosts) {
+ title = (
+ <FormattedMessage
+ id='search_header.title3'
+ defaultMessage='Flagged Posts'
+ />
+ );
}
return (
@@ -140,5 +147,6 @@ export default class SearchResultsHeader extends React.Component {
SearchResultsHeader.propTypes = {
isMentionSearch: React.PropTypes.bool,
toggleSize: React.PropTypes.function,
- shrink: React.PropTypes.function
+ shrink: React.PropTypes.function,
+ isFlaggedPosts: React.PropTypes.bool
};
diff --git a/webapp/components/search_results_item.jsx b/webapp/components/search_results_item.jsx
index fb8b23a7f..db64463a9 100644
--- a/webapp/components/search_results_item.jsx
+++ b/webapp/components/search_results_item.jsx
@@ -7,16 +7,20 @@ import UserProfile from './user_profile.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
-import * as GlobalActions from 'actions/global_actions.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import * as GlobalActions from 'actions/global_actions.jsx';
+import {unflagPost, getFlaggedPosts} from 'actions/post_actions.jsx';
+
import * as TextFormatting from 'utils/text_formatting.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 {FormattedMessage, FormattedDate} from 'react-intl';
import React from 'react';
+import {FormattedMessage, FormattedDate} from 'react-intl';
import {browserHistory} from 'react-router/es6';
export default class SearchResultsItem extends React.Component {
@@ -25,6 +29,7 @@ export default class SearchResultsItem extends React.Component {
this.handleFocusRHSClick = this.handleFocusRHSClick.bind(this);
this.shrinkSidebar = this.shrinkSidebar.bind(this);
+ this.unflagPost = this.unflagPost.bind(this);
}
hideSidebar() {
@@ -42,12 +47,20 @@ export default class SearchResultsItem extends React.Component {
GlobalActions.emitPostFocusRightHandSideFromSearch(this.props.post, this.props.isMentionSearch);
}
+ unflagPost(e) {
+ e.preventDefault();
+ unflagPost(this.props.post.id,
+ () => getFlaggedPosts()
+ );
+ }
+
render() {
let channelName = null;
const channel = this.props.channel;
const timestamp = UserStore.getCurrentUser().update_at;
const user = this.props.user || {};
const post = this.props.post;
+ const flagIcon = Constants.FLAG_ICON_SVG;
if (channel) {
channelName = channel.display_name;
@@ -77,11 +90,50 @@ export default class SearchResultsItem extends React.Component {
}
let botIndicator;
-
if (post.props && post.props.from_webhook) {
botIndicator = <li className='bot-indicator'>{Constants.BOT_NAME}</li>;
}
+ let flag;
+ 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>
+ );
+ flag = (
+ <OverlayTrigger
+ delayShow={Constants.OVERLAY_TIME_DELAY}
+ placement='top'
+ overlay={flagTooltip}
+ >
+ <a
+ href='#'
+ className={'flag-icon__container ' + flagVisible}
+ onClick={this.unflagPost}
+ >
+ <span
+ className='icon'
+ dangerouslySetInnerHTML={{__html: flagIcon}}
+ />
+ </a>
+ </OverlayTrigger>
+ );
+ }
+
return (
<div className='search-item__container'>
<div className='date-separator'>
@@ -126,8 +178,19 @@ export default class SearchResultsItem extends React.Component {
minute='2-digit'
/>
</time>
+ {flag}
</li>
- <li>
+ <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>
<a
onClick={
() => {
@@ -163,18 +226,6 @@ export default class SearchResultsItem extends React.Component {
/>
</a>
</li>
- <li>
- <a
- href='#'
- className='comment-icon__container search-item__comment'
- onClick={this.handleFocusRHSClick}
- >
- <span
- className='comment-icon'
- dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
- />
- </a>
- </li>
</ul>
<div className='search-item-snippet'>
<span
@@ -197,5 +248,6 @@ SearchResultsItem.propTypes = {
isMentionSearch: React.PropTypes.bool,
term: React.PropTypes.string,
useMilitaryTime: React.PropTypes.bool.isRequired,
- shrink: React.PropTypes.function
+ shrink: React.PropTypes.function,
+ isFlagged: React.PropTypes.bool
};
diff --git a/webapp/components/sidebar_right.jsx b/webapp/components/sidebar_right.jsx
index 7cdb894cc..6d1184799 100644
--- a/webapp/components/sidebar_right.jsx
+++ b/webapp/components/sidebar_right.jsx
@@ -5,12 +5,16 @@ import $ from 'jquery';
import SearchResults from './search_results.jsx';
import RhsThread from './rhs_thread.jsx';
+
import SearchStore from 'stores/search_store.jsx';
import PostStore from 'stores/post_store.jsx';
import UserStore from 'stores/user_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
-import Constants from 'utils/constants.jsx';
+
+import {getFlaggedPosts} from 'actions/post_actions.jsx';
+
import * as Utils from 'utils/utils.jsx';
+import Constants from 'utils/constants.jsx';
import React from 'react';
@@ -101,15 +105,20 @@ export default class SidebarRight extends React.Component {
}
onPreferenceChange() {
+ if (this.state.isFlaggedPosts) {
+ getFlaggedPosts();
+ }
+
this.setState({
useMilitaryTime: PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, Constants.Preferences.USE_MILITARY_TIME, false)
});
}
- onSelectedChange(fromSearch) {
+ onSelectedChange(fromSearch, fromFlaggedPosts) {
this.setState({
postRightVisible: !!PostStore.getSelectedPost(),
- fromSearch
+ fromSearch,
+ fromFlaggedPosts
});
}
@@ -120,7 +129,8 @@ export default class SidebarRight extends React.Component {
onSearchChange() {
this.setState({
searchVisible: SearchStore.getSearchResults() !== null,
- isMentionSearch: SearchStore.getIsMentionSearch()
+ isMentionSearch: SearchStore.getIsMentionSearch(),
+ isFlaggedPosts: SearchStore.getIsFlaggedPosts()
});
}
@@ -154,6 +164,7 @@ export default class SidebarRight extends React.Component {
content = (
<SearchResults
isMentionSearch={this.state.isMentionSearch}
+ isFlaggedPosts={this.state.isFlaggedPosts}
useMilitaryTime={this.state.useMilitaryTime}
toggleSize={this.toggleSize}
shrink={this.onShrink}
@@ -162,6 +173,7 @@ export default class SidebarRight extends React.Component {
} else if (this.state.postRightVisible) {
content = (
<RhsThread
+ fromFlaggedPosts={this.state.fromFlaggedPosts}
fromSearch={this.state.fromSearch}
isMentionSearch={this.state.isMentionSearch}
currentUser={this.state.currentUser}
diff --git a/webapp/components/sidebar_right_menu.jsx b/webapp/components/sidebar_right_menu.jsx
index b36255a01..576181931 100644
--- a/webapp/components/sidebar_right_menu.jsx
+++ b/webapp/components/sidebar_right_menu.jsx
@@ -12,6 +12,7 @@ import TeamStore from 'stores/team_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
+import {getFlaggedPosts} from 'actions/post_actions.jsx';
import * as UserAgent from 'utils/user_agent.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
@@ -35,6 +36,7 @@ export default class SidebarRightMenu extends React.Component {
this.handleAboutModal = this.handleAboutModal.bind(this);
this.searchMentions = this.searchMentions.bind(this);
this.aboutModalDismissed = this.aboutModalDismissed.bind(this);
+ this.getFlagged = this.getFlagged.bind(this);
const state = this.getStateFromStores();
state.showUserSettingsModal = false;
@@ -53,6 +55,11 @@ export default class SidebarRightMenu extends React.Component {
this.setState({showAboutModal: false});
}
+ getFlagged(e) {
+ e.preventDefault();
+ getFlaggedPosts();
+ }
+
componentDidMount() {
PreferenceStore.addChangeListener(this.onPreferenceChange);
}
@@ -347,6 +354,18 @@ export default class SidebarRightMenu extends React.Component {
<li>
<a
href='#'
+ onClick={this.getFlagged}
+ >
+ <i className='icon fa fa-flag'></i>
+ <FormattedMessage
+ id='sidebar_right_menu.flagged'
+ defaultMessage='Flagged Posts'
+ />
+ </a>
+ </li>
+ <li>
+ <a
+ href='#'
onClick={() => this.setState({showUserSettingsModal: true})}
>
<i className='icon fa fa-cog'></i>