PLT-3506 Added flagged posts functionality (#3679)
* Added flagged posts functionality * UI Improvements to flags (#3697) * Added flag functionality for mobile * Updating flagged text (#3699) * Add back button to RHS thread when coming from flagged posts * Updating position of flags (#3708) * Plt 3506 - Reverting flag position (#3724) * Revert "Updating position of flags (#3708)" This reverts commit aaa05632c5d9eda35a048300a5bd7e99584c5b58. * Fixing the icon in search * Help text and white space improvements (#3730) * Updatng help text and some white spacing. * Updating help text
Этот коммит содержится в:
коммит произвёл
enahum
родитель
9b50b50283
Коммит
0184d6059b
@@ -26,19 +26,18 @@ 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;
|
||||
|
||||
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';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import {browserHistory} from 'react-router/es6';
|
||||
import {Tooltip, OverlayTrigger, Popover} from 'react-bootstrap';
|
||||
|
||||
export default class ChannelHeader extends React.Component {
|
||||
constructor(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>
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
});
|
||||
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
|
||||
});
|
||||
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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -344,6 +351,18 @@ export default class SidebarRightMenu extends React.Component {
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
<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='#'
|
||||
|
||||
Ссылка в новой задаче
Block a user