summaryrefslogtreecommitdiffstats
path: root/webapp
diff options
context:
space:
mode:
authorSaturnino Abril <saturnino.abril@gmail.com>2017-06-15 07:45:46 +0800
committerJoram Wilander <jwawilander@gmail.com>2017-06-14 19:45:46 -0400
commit0e89d9be1d6a2a1ca470f9ca92e0d59e5945ca18 (patch)
tree2fbfd8bc9f30e6ac84bcb14cc8c4c0a5e566be24 /webapp
parent5b017171cd42872a11a9c8b3e2e8bb9bd29c2b3a (diff)
downloadchat-0e89d9be1d6a2a1ca470f9ca92e0d59e5945ca18.tar.gz
chat-0e89d9be1d6a2a1ca470f9ca92e0d59e5945ca18.tar.bz2
chat-0e89d9be1d6a2a1ca470f9ca92e0d59e5945ca18.zip
create DotMenu components and add dotmenu IDs to posts at center and RHS (#6642)
Diffstat (limited to 'webapp')
-rw-r--r--webapp/components/dot_menu/dot_menu.jsx197
-rw-r--r--webapp/components/dot_menu/dot_menu_edit.jsx58
-rw-r--r--webapp/components/dot_menu/dot_menu_flag.jsx70
-rw-r--r--webapp/components/dot_menu/dot_menu_item.jsx107
-rw-r--r--webapp/components/post_view/components/post_info.jsx290
-rw-r--r--webapp/components/rhs_comment.jsx200
-rw-r--r--webapp/components/rhs_dropdown.jsx62
-rw-r--r--webapp/components/rhs_dropdown_button.jsx22
-rw-r--r--webapp/components/rhs_dropdown_menu.jsx22
-rw-r--r--webapp/components/rhs_root_post.jsx179
-rw-r--r--webapp/utils/constants.jsx5
11 files changed, 476 insertions, 736 deletions
diff --git a/webapp/components/dot_menu/dot_menu.jsx b/webapp/components/dot_menu/dot_menu.jsx
new file mode 100644
index 000000000..b5f9fde45
--- /dev/null
+++ b/webapp/components/dot_menu/dot_menu.jsx
@@ -0,0 +1,197 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import $ from 'jquery';
+import React, {Component} from 'react';
+import PropTypes from 'prop-types';
+
+import DotMenuFlag from './dot_menu_flag.jsx';
+import DotMenuItem from './dot_menu_item.jsx';
+import DotMenuEdit from './dot_menu_edit.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';
+
+export default class DotMenu extends Component {
+ static propTypes = {
+ idPrefix: PropTypes.string.isRequired,
+ idCount: PropTypes.number,
+ post: PropTypes.object.isRequired,
+ commentCount: PropTypes.number,
+ isFlagged: PropTypes.bool,
+ handleCommentClick: PropTypes.func,
+ handleDropdownOpened: PropTypes.func
+ }
+
+ static defaultProps = {
+ idCount: -1,
+ post: {},
+ commentCount: 0,
+ isFlagged: false
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.handleDropdownOpened = this.handleDropdownOpened.bind(this);
+ this.canDelete = false;
+ this.canEdit = false;
+ this.editDisableAction = new DelayedAction(this.handleEditDisable);
+ }
+
+ componentDidMount() {
+ $('#' + this.props.idPrefix + '_dropdown' + this.props.post.id).on('shown.bs.dropdown', this.handleDropdownOpened);
+ $('#' + this.props.idPrefix + '_dropdown' + this.props.post.id).on('hidden.bs.dropdown', () => this.props.handleDropdownOpened(false));
+ }
+
+ handleDropdownOpened() {
+ this.props.handleDropdownOpened(true);
+
+ const position = $('#post-list').height() - $(this.refs.dropdownToggle).offset().top;
+ const dropdown = $(this.refs.dropdown);
+
+ if (position < dropdown.height()) {
+ dropdown.addClass('bottom');
+ }
+ }
+
+ handleEditDisable() {
+ this.canEdit = false;
+ }
+
+ render() {
+ const isSystemMessage = PostUtils.isSystemMessage(this.props.post);
+ const isMobile = Utils.isMobile();
+ this.canDelete = PostUtils.canDeletePost(this.props.post);
+ this.canEdit = PostUtils.canEditPost(this.props.post, this.editDisableAction);
+
+ if (this.props.idPrefix === Constants.CENTER && (!isMobile && isSystemMessage && !this.canDelete && !this.canEdit)) {
+ return null;
+ }
+
+ if (this.props.idPrefix === Constants.RHS && (this.props.post.state === Constants.POST_FAILED || this.props.post.state === Constants.POST_LOADING)) {
+ return null;
+ }
+
+ let type = 'Post';
+ if (this.props.post.root_id && this.props.post.root_id.length > 0) {
+ type = 'Comment';
+ }
+
+ const idPrefix = this.props.idPrefix + 'DotMenu';
+
+ let dotMenuFlag = null;
+ if (isMobile) {
+ dotMenuFlag = (
+ <DotMenuFlag
+ idPrefix={idPrefix + 'Flag'}
+ idCount={this.props.idCount}
+ postId={this.props.post.id}
+ isFlagged={this.props.isFlagged}
+ />
+ );
+ }
+
+ let dotMenuReply = null;
+ let dotMenuPermalink = null;
+ let dotMenuPin = null;
+ if (!isSystemMessage) {
+ if (this.props.idPrefix === Constants.CENTER) {
+ dotMenuReply = (
+ <DotMenuItem
+ idPrefix={idPrefix + 'Reply'}
+ idCount={this.props.idCount}
+ handleOnClick={this.props.handleCommentClick}
+ />
+ );
+ }
+
+ dotMenuPermalink = (
+ <DotMenuItem
+ idPrefix={idPrefix + 'Permalink'}
+ idCount={this.props.idCount}
+ post={this.props.post}
+ />
+ );
+
+ dotMenuPin = (
+ <DotMenuItem
+ idPrefix={idPrefix + 'Pin'}
+ idCount={this.props.idCount}
+ post={this.props.post}
+ />
+ );
+ }
+
+ let dotMenuDelete = null;
+ if (this.canDelete) {
+ dotMenuDelete = (
+ <DotMenuItem
+ idPrefix={idPrefix + 'Delete'}
+ idCount={this.props.idCount}
+ post={this.props.post}
+ commentCount={type === 'Post' ? this.props.commentCount : 0}
+ />
+ );
+ }
+
+ let dotMenuEdit = null;
+ if (this.canEdit) {
+ dotMenuEdit = (
+ <DotMenuEdit
+ idPrefix={idPrefix + 'Edit'}
+ idCount={this.props.idCount}
+ post={this.props.post}
+ type={type}
+ commentCount={type === 'Post' ? this.props.commentCount : 0}
+ />
+ );
+ }
+
+ let dotMenuId = null;
+ if (this.props.idCount > -1) {
+ dotMenuId = Utils.createSafeId(idPrefix + this.props.idCount);
+ }
+
+ if (this.props.idPrefix === Constants.RHS_ROOT) {
+ dotMenuId = idPrefix;
+ }
+
+ return (
+ <div
+ id={dotMenuId}
+ className='dropdown'
+ ref='dotMenu'
+ >
+ <div
+ id={this.props.idPrefix + '_dropdown' + this.props.post.id}
+ >
+ <a
+ ref='dropdownToggle'
+ href='#'
+ className='dropdown-toggle post__dropdown theme'
+ type='button'
+ data-toggle='dropdown'
+ aria-expanded='false'
+ />
+ <div className='dropdown-menu__content'>
+ <ul
+ ref='dropdown'
+ className='dropdown-menu'
+ role='menu'
+ >
+ {dotMenuReply}
+ {dotMenuFlag}
+ {dotMenuPermalink}
+ {dotMenuPin}
+ {dotMenuDelete}
+ {dotMenuEdit}
+ </ul>
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/webapp/components/dot_menu/dot_menu_edit.jsx b/webapp/components/dot_menu/dot_menu_edit.jsx
new file mode 100644
index 000000000..2ee0947e4
--- /dev/null
+++ b/webapp/components/dot_menu/dot_menu_edit.jsx
@@ -0,0 +1,58 @@
+// 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 PropTypes from 'prop-types';
+
+import * as Utils from 'utils/utils.jsx';
+import Constants from 'utils/constants.jsx';
+
+export default function DotMenuEdit(props) {
+ let editId = null;
+ if (props.idCount > -1) {
+ editId = Utils.createSafeId(props.idPrefix + props.idCount);
+ }
+
+ if (props.idPrefix.indexOf(Constants.RHS_ROOT) === 0) {
+ editId = props.idPrefix;
+ }
+
+ return (
+ <li
+ id={Utils.createSafeId(editId)}
+ key={props.idPrefix}
+ role='presentation'
+ >
+ <a
+ href='#'
+ role='menuitem'
+ data-toggle='modal'
+ data-target='#edit_post'
+ data-refocusid={props.idPrefix.indexOf(Constants.CENTER) === 0 ? '#post_textbox' : '#reply_textbox'}
+ data-title={props.idPrefix.indexOf(Constants.CENTER) === 0 ? props.type : Utils.localizeMessage('rhs_comment.comment', 'Comment')}
+ data-message={props.post.message}
+ data-postid={props.post.id}
+ data-channelid={props.post.channel_id}
+ data-comments={props.commentCount}
+ >
+ <FormattedMessage
+ id='post_info.edit'
+ defaultMessage='Edit'
+ />
+ </a>
+ </li>
+ );
+}
+
+DotMenuEdit.propTypes = {
+ idPrefix: PropTypes.string.isRequired,
+ idCount: PropTypes.number,
+ post: PropTypes.object,
+ type: PropTypes.string,
+ commentCount: PropTypes.number
+};
+
+DotMenuEdit.defaultProps = {
+ idCount: -1
+};
diff --git a/webapp/components/dot_menu/dot_menu_flag.jsx b/webapp/components/dot_menu/dot_menu_flag.jsx
new file mode 100644
index 000000000..105363211
--- /dev/null
+++ b/webapp/components/dot_menu/dot_menu_flag.jsx
@@ -0,0 +1,70 @@
+// 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 PropTypes from 'prop-types';
+
+import {flagPost, unflagPost} from 'actions/post_actions.jsx';
+import * as Utils from 'utils/utils.jsx';
+import Constants from 'utils/constants.jsx';
+
+function formatMessage(isFlagged) {
+ return (
+ <FormattedMessage
+ id={isFlagged ? 'rhs_root.mobile.unflag' : 'rhs_root.mobile.flag'}
+ defaultMessage={isFlagged ? 'Unflag' : 'Flag'}
+ />
+ );
+}
+
+export default function DotMenuFlag(props) {
+ function onFlagPost(e) {
+ e.preventDefault();
+ flagPost(props.postId);
+ }
+
+ function onUnflagPost(e) {
+ e.preventDefault();
+ unflagPost(props.postId);
+ }
+
+ const flagFunc = props.isFlagged ? onUnflagPost : onFlagPost;
+
+ let flagId = null;
+ if (props.idCount > -1) {
+ flagId = Utils.createSafeId(props.idPrefix + props.idCount);
+ }
+
+ if (props.idPrefix.indexOf(Constants.RHS_ROOT) === 0) {
+ flagId = props.idPrefix;
+ }
+
+ return (
+ <li
+ key={props.idPrefix}
+ role='presentation'
+ >
+ <a
+ id={flagId}
+ href='#'
+ onClick={flagFunc}
+ >
+ {formatMessage(props.isFlagged)}
+ </a>
+ </li>
+ );
+}
+
+DotMenuFlag.propTypes = {
+ idCount: PropTypes.number,
+ idPrefix: PropTypes.string.isRequired,
+ postId: PropTypes.string.isRequired,
+ isFlagged: PropTypes.bool.isRequired
+};
+
+DotMenuFlag.defaultProps = {
+ idCount: -1,
+ postId: '',
+ isFlagged: false
+};
diff --git a/webapp/components/dot_menu/dot_menu_item.jsx b/webapp/components/dot_menu/dot_menu_item.jsx
new file mode 100644
index 000000000..ceda0a1a4
--- /dev/null
+++ b/webapp/components/dot_menu/dot_menu_item.jsx
@@ -0,0 +1,107 @@
+// 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 PropTypes from 'prop-types';
+
+import {unpinPost, pinPost} from 'actions/post_actions.jsx';
+import {showGetPostLinkModal, showDeletePostModal} from 'actions/global_actions.jsx';
+import * as Utils from 'utils/utils.jsx';
+import Constants from 'utils/constants.jsx';
+
+export default function DotMenuItem(props) {
+ function handlePermalink(e) {
+ e.preventDefault();
+ showGetPostLinkModal(props.post);
+ }
+
+ function handleUnpinPost(e) {
+ e.preventDefault();
+ unpinPost(props.post.channel_id, props.post.id);
+ }
+
+ function handlePinPost(e) {
+ e.preventDefault();
+ pinPost(props.post.channel_id, props.post.id);
+ }
+
+ function handleDeletePost(e) {
+ e.preventDefault();
+ showDeletePostModal(props.post, props.commentCount);
+ }
+
+ const attrib = {};
+ attrib.idPrefix = props.idPrefix;
+ attrib.class = '';
+
+ switch (props.idPrefix.substring((props.idPrefix.indexOf('DotMenu') + 7))) {
+ case 'Reply':
+ attrib.class = 'link__reply theme';
+ attrib.onClick = props.handleOnClick;
+ attrib.formattedMessageId = 'post_info.reply';
+ attrib.formattedDefaultMessage = 'Reply';
+ break;
+ case 'Permalink':
+ attrib.onClick = handlePermalink;
+ attrib.formattedMessageId = 'post_info.permalink';
+ attrib.formattedDefaultMessage = 'Permalink';
+ attrib.post = props.post;
+ break;
+ case 'Pin':
+ attrib.onClick = props.post.is_pinned ? handleUnpinPost : handlePinPost;
+ attrib.formattedMessageId = props.post.is_pinned ? 'post_info.unpin' : 'post_info.pin';
+ attrib.formattedDefaultMessage = props.post.is_pinned ? 'Un-pin from channel' : 'Pin from channel';
+ attrib.post = props.post;
+ break;
+ case 'Delete':
+ attrib.onClick = handleDeletePost;
+ attrib.formattedMessageId = 'post_info.del';
+ attrib.formattedDefaultMessage = 'Delete';
+ attrib.commentCount = props.commentCount;
+ break;
+ default:
+ }
+
+ let itemId = null;
+ if (props.idCount > -1) {
+ itemId = Utils.createSafeId(props.idPrefix + props.idCount);
+ }
+
+ if (attrib.idPrefix.indexOf(Constants.RHS_ROOT) === 0) {
+ itemId = attrib.idPrefix;
+ }
+
+ return (
+ <li
+ id={Utils.createSafeId(itemId)}
+ key={attrib.idPrefix}
+ role='presentation'
+ >
+ <a
+ href='#'
+ role='menuitem'
+ onClick={attrib.onClick}
+ >
+ <FormattedMessage
+ id={attrib.formattedMessageId}
+ defaultMessage={attrib.formattedDefaultMessage}
+ />
+ </a>
+ </li>
+ );
+}
+
+DotMenuItem.propTypes = {
+ idPrefix: PropTypes.string.isRequired,
+ idCount: PropTypes.number,
+ post: PropTypes.object,
+ handleOnClick: PropTypes.func,
+ type: PropTypes.string,
+ commentCount: PropTypes.number
+};
+
+DotMenuItem.defaultProps = {
+ idPrefix: '',
+ idCount: -1
+};
diff --git a/webapp/components/post_view/components/post_info.jsx b/webapp/components/post_view/components/post_info.jsx
index 45c716ad0..20c37ee72 100644
--- a/webapp/components/post_view/components/post_info.jsx
+++ b/webapp/components/post_view/components/post_info.jsx
@@ -1,10 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import $ from 'jquery';
-
import PostTime from './post_time.jsx';
import PostFlagIcon from 'components/common/post_flag_icon.jsx';
+import DotMenu from 'components/dot_menu/dot_menu.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
import * as PostActions from 'actions/post_actions.jsx';
@@ -13,7 +12,6 @@ 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 EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay.jsx';
import ChannelStore from 'stores/channel_store.jsx';
@@ -26,257 +24,15 @@ export default class PostInfo extends React.Component {
constructor(props) {
super(props);
- this.handleDropdownOpened = this.handleDropdownOpened.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);
- this.pinPost = this.pinPost.bind(this);
- this.unpinPost = this.unpinPost.bind(this);
this.reactEmojiClick = this.reactEmojiClick.bind(this);
- this.canEdit = false;
- this.canDelete = false;
- this.editDisableAction = new DelayedAction(this.handleEditDisable);
-
this.state = {
showEmojiPicker: false,
reactionPickerOffset: 21
};
}
- handleDropdownOpened() {
- this.props.handleDropdownOpened(true);
-
- const position = $('#post-list').height() - $(this.refs.dropdownToggle).offset().top;
- const dropdown = $(this.refs.dropdown);
-
- if (position < dropdown.height()) {
- dropdown.addClass('bottom');
- }
- }
-
- handleEditDisable() {
- this.canEdit = false;
- }
-
- componentDidMount() {
- $('#post_dropdown' + this.props.post.id).on('shown.bs.dropdown', this.handleDropdownOpened);
- $('#post_dropdown' + this.props.post.id).on('hidden.bs.dropdown', () => this.props.handleDropdownOpened(false));
- }
-
- createDropdown(isSystemMessage) {
- const post = this.props.post;
-
- var type = 'Post';
- if (post.root_id && post.root_id.length > 0) {
- type = 'Comment';
- }
-
- var dropdownContents = [];
- var dataComments = 0;
- if (type === 'Post') {
- dataComments = this.props.commentCount;
- }
-
- if (!isSystemMessage) {
- dropdownContents.push(
- <li
- key='replyLink'
- role='presentation'
- >
- <a
- className='link__reply theme'
- href='#'
- onClick={this.props.handleCommentClick}
- >
- <FormattedMessage
- id='post_info.reply'
- defaultMessage='Reply'
- />
- </a>
- </li>
- );
- }
-
- 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>
- );
- }
- }
-
- if (!isSystemMessage) {
- dropdownContents.push(
- <li
- key='copyLink'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.handlePermalink}
- >
- <FormattedMessage
- id='post_info.permalink'
- defaultMessage='Permalink'
- />
- </a>
- </li>
- );
-
- if (this.props.post.is_pinned) {
- dropdownContents.push(
- <li
- key='unpinLink'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.unpinPost}
- >
- <FormattedMessage
- id='post_info.unpin'
- defaultMessage='Un-pin from channel'
- />
- </a>
- </li>
- );
- } else {
- dropdownContents.push(
- <li
- key='pinLink'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.pinPost}
- >
- <FormattedMessage
- id='post_info.pin'
- defaultMessage='Pin to channel'
- />
- </a>
- </li>
- );
- }
- }
-
- if (this.canDelete) {
- dropdownContents.push(
- <li
- key='deletePost'
- role='presentation'
- >
- <a
- href='#'
- role='menuitem'
- onClick={(e) => {
- e.preventDefault();
- GlobalActions.showDeletePostModal(post, dataComments);
- }}
- >
- <FormattedMessage
- id='post_info.del'
- defaultMessage='Delete'
- />
- </a>
- </li>
- );
- }
-
- if (this.canEdit) {
- dropdownContents.push(
- <li
- key='editPost'
- role='presentation'
- className={this.canEdit ? 'dropdown-submenu' : 'dropdown-submenu hide'}
- >
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-refocusid='#post_textbox'
- data-title={type}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- data-comments={dataComments}
- >
- <FormattedMessage
- id='post_info.edit'
- defaultMessage='Edit'
- />
- </a>
- </li>
- );
- }
-
- if (dropdownContents.length === 0) {
- return '';
- }
-
- return (
- <div
- id={'post_dropdown' + this.props.post.id}
- >
- <a
- ref='dropdownToggle'
- href='#'
- className='dropdown-toggle post__dropdown theme'
- type='button'
- data-toggle='dropdown'
- aria-expanded='false'
- />
- <div className='dropdown-menu__content'>
- <ul
- ref='dropdown'
- className='dropdown-menu'
- role='menu'
- >
- {dropdownContents}
- </ul>
- </div>
- </div>
- );
- }
-
- handlePermalink(e) {
- e.preventDefault();
- GlobalActions.showGetPostLinkModal(this.props.post);
- }
-
toggleEmojiPicker = () => {
const showEmojiPicker = !this.state.showEmojiPicker;
@@ -306,26 +62,6 @@ export default class PostInfo extends React.Component {
);
}
- pinPost(e) {
- e.preventDefault();
- PostActions.pinPost(this.props.post.channel_id, this.props.post.id);
- }
-
- unpinPost(e) {
- e.preventDefault();
- PostActions.unpinPost(this.props.post.channel_id, this.props.post.id);
- }
-
- flagPost(e) {
- e.preventDefault();
- PostActions.flagPost(this.props.post.id);
- }
-
- unflagPost(e) {
- e.preventDefault();
- PostActions.unflagPost(this.props.post.id);
- }
-
reactEmojiClick(emoji) {
const pickerOffset = 21;
this.setState({showEmojiPicker: false, reactionPickerOffset: pickerOffset});
@@ -345,9 +81,6 @@ export default class PostInfo extends React.Component {
idCount = this.props.lastPostCount;
}
- this.canDelete = PostUtils.canDeletePost(post);
- this.canEdit = PostUtils.canEditPost(post, this.editDisableAction);
-
const isEphemeral = Utils.isPostEphemeral(post);
const isPending = post.state === Constants.POST_FAILED || post.state === Constants.POST_LOADING;
const isSystemMessage = PostUtils.isSystemMessage(post);
@@ -395,20 +128,25 @@ export default class PostInfo extends React.Component {
</div>
);
} else if (!isPending) {
- const dropdown = this.createDropdown(isSystemMessage);
+ const dotMenu = (
+ <DotMenu
+ idPrefix={Constants.CENTER}
+ idCount={idCount}
+ post={this.props.post}
+ commentCount={this.props.commentCount}
+ isFlagged={this.props.isFlagged}
+ handleCommentClick={this.props.handleCommentClick}
+ handleDropdownOpened={this.props.handleDropdownOpened}
+ />
+ );
- if (dropdown) {
+ if (dotMenu) {
options = (
<div
ref='dotMenu'
className='col col__reply'
>
- <div
- className='dropdown'
- ref='dotMenu'
- >
- {dropdown}
- </div>
+ {dotMenu}
{react}
{comments}
</div>
diff --git a/webapp/components/rhs_comment.jsx b/webapp/components/rhs_comment.jsx
index 20734c4f4..73ae70598 100644
--- a/webapp/components/rhs_comment.jsx
+++ b/webapp/components/rhs_comment.jsx
@@ -7,8 +7,9 @@ import PendingPostOptions from 'components/post_view/components/pending_post_opt
import PostMessageContainer from 'components/post_view/components/post_message_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 DotMenu from 'components/dot_menu/dot_menu.jsx';
+import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
import {flagPost, unflagPost, pinPost, unpinPost, addReaction} from 'actions/post_actions.jsx';
@@ -17,19 +18,13 @@ import TeamStore from 'stores/team_store.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 {FormattedMessage} from 'react-intl';
-
-import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay.jsx';
import loadingGif from 'images/load.gif';
-import PropTypes from 'prop-types';
-
import React from 'react';
+import PropTypes from 'prop-types';
+import {FormattedMessage} from 'react-intl';
import {Link} from 'react-router/es6';
export default class RhsComment extends React.Component {
@@ -45,10 +40,6 @@ export default class RhsComment extends React.Component {
this.reactEmojiClick = this.reactEmojiClick.bind(this);
this.handleDropdownOpened = this.handleDropdownOpened.bind(this);
- this.canEdit = false;
- this.canDelete = false;
- this.editDisableAction = new DelayedAction(this.handleEditDisable);
-
this.state = {
currentTeamDisplayName: TeamStore.getCurrent().name,
width: '',
@@ -75,10 +66,6 @@ export default class RhsComment extends React.Component {
GlobalActions.showGetPostLinkModal(this.props.post);
}
- handleEditDisable() {
- this.canEdit = false;
- }
-
removePost() {
GlobalActions.emitRemovePost(this.props.post);
}
@@ -160,173 +147,6 @@ export default class RhsComment extends React.Component {
unpinPost(this.props.post.channel_id, this.props.post.id);
}
- createDropdown(isSystemMessage) {
- const post = this.props.post;
-
- if (post.state === Constants.POST_FAILED || post.state === Constants.POST_LOADING) {
- return '';
- }
-
- this.canDelete = PostUtils.canDeletePost(post);
- this.canEdit = PostUtils.canEditPost(post, this.editDisableAction);
-
- 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>
- );
- }
- }
-
- if (!isSystemMessage) {
- dropdownContents.push(
- <li
- key='rhs-root-permalink'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.handlePermalink}
- >
- <FormattedMessage
- id='rhs_comment.permalink'
- defaultMessage='Permalink'
- />
- </a>
- </li>
- );
-
- if (post.is_pinned) {
- dropdownContents.push(
- <li
- key='rhs-comment-unpin'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.unpinPost}
- >
- <FormattedMessage
- id='rhs_root.unpin'
- defaultMessage='Un-pin from channel'
- />
- </a>
- </li>
- );
- } else {
- dropdownContents.push(
- <li
- key='rhs-comment-pin'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.pinPost}
- >
- <FormattedMessage
- id='rhs_root.pin'
- defaultMessage='Pin to channel'
- />
- </a>
- </li>
- );
- }
- }
-
- if (this.canDelete) {
- dropdownContents.push(
- <li
- role='presentation'
- key='delete-button'
- >
- <a
- href='#'
- role='menuitem'
- onClick={(e) => {
- e.preventDefault();
- GlobalActions.showDeletePostModal(post, 0);
- }}
- >
- <FormattedMessage
- id='rhs_comment.del'
- defaultMessage='Delete'
- />
- </a>
- </li>
- );
- }
-
- if (this.canEdit) {
- dropdownContents.push(
- <li
- role='presentation'
- key='edit-button'
- className={this.canEdit ? '' : 'hide'}
- >
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-refocusid='#reply_textbox'
- data-title={Utils.localizeMessage('rhs_comment.comment', 'Comment')}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- >
- <FormattedMessage
- id='rhs_comment.edit'
- defaultMessage='Edit'
- />
- </a>
- </li>
- );
- }
-
- if (dropdownContents.length === 0) {
- return '';
- }
-
- return (
- <RhsDropdown
- dropdownContents={dropdownContents}
- handleDropdownOpened={this.handleDropdownOpened}
- />
- );
- }
-
timeTag(post, timeOptions) {
return (
<time
@@ -575,12 +395,22 @@ export default class RhsComment extends React.Component {
</div>
);
} else if (!isSystemMessage) {
+ const dotMenu = (
+ <DotMenu
+ idPrefix={Constants.RHS}
+ idCount={idCount}
+ post={this.props.post}
+ isFlagged={this.props.isFlagged}
+ handleDropdownOpened={this.handleDropdownOpened}
+ />
+ );
+
options = (
<div
ref='dotMenu'
className='col col__reply'
>
- {this.createDropdown(isSystemMessage)}
+ {dotMenu}
{react}
</div>
);
diff --git a/webapp/components/rhs_dropdown.jsx b/webapp/components/rhs_dropdown.jsx
deleted file mode 100644
index 9323e1cec..000000000
--- a/webapp/components/rhs_dropdown.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import PropTypes from 'prop-types';
-
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import React, {Component} from 'react';
-import {Dropdown} from 'react-bootstrap';
-
-import RhsDropdownButton from 'components/rhs_dropdown_button.jsx';
-import RhsDropdownMenu from 'components/rhs_dropdown_menu.jsx';
-
-import * as Agent from 'utils/user_agent.jsx';
-
-export default class RhsDropdown extends Component {
- static propTypes = {
- dropdownContents: PropTypes.array.isRequired,
- handleDropdownOpened: PropTypes.func
- }
-
- constructor(props) {
- super(props);
-
- this.state = {
- showDropdown: false
- };
- }
-
- toggleDropdown = () => {
- const showDropdown = !this.state.showDropdown;
- if (this.props.handleDropdownOpened) {
- this.props.handleDropdownOpened(showDropdown);
- }
- if (Agent.isMobile() || Agent.isMobileApp()) {
- const scroll = document.querySelector('.scrollbar--view');
- if (showDropdown) {
- scroll.style.overflow = 'hidden';
- } else {
- scroll.style.overflow = 'scroll';
- }
- }
-
- this.setState({showDropdown});
- }
-
- render() {
- return (
- <Dropdown
- id='rhs_dropdown'
- open={this.state.showDropdown}
- onToggle={this.toggleDropdown}
- >
- <RhsDropdownButton
- bsRole='toggle'
- onClick={this.toggleDropdown}
- />
- <RhsDropdownMenu>
- {this.props.dropdownContents}
- </RhsDropdownMenu>
- </Dropdown>
- );
- }
-}
diff --git a/webapp/components/rhs_dropdown_button.jsx b/webapp/components/rhs_dropdown_button.jsx
deleted file mode 100644
index ac7563994..000000000
--- a/webapp/components/rhs_dropdown_button.jsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import PropTypes from 'prop-types';
-
-// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import React, {PureComponent} from 'react';
-
-export default class RhsDropdownButton extends PureComponent {
- static propTypes = {
- onClick: PropTypes.func.isRequired
- }
-
- render() {
- return (
- <a
- href='#'
- className='post__dropdown dropdown-toggle'
- onClick={this.props.onClick}
- />
- );
- }
-}
diff --git a/webapp/components/rhs_dropdown_menu.jsx b/webapp/components/rhs_dropdown_menu.jsx
deleted file mode 100644
index 5f5107691..000000000
--- a/webapp/components/rhs_dropdown_menu.jsx
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {Dropdown} from 'react-bootstrap';
-import React from 'react';
-
-export default class RhsDropdownMenu extends Dropdown.Menu {
- constructor(props) { //eslint-disable-line no-useless-constructor
- super(props);
- }
-
- render() {
- return (
- <div
- className='dropdown-menu__content'
- onClick={this.props.onClose}
- >
- {super.render()}
- </div>
- );
- }
-}
diff --git a/webapp/components/rhs_root_post.jsx b/webapp/components/rhs_root_post.jsx
index e77fb7992..c617477af 100644
--- a/webapp/components/rhs_root_post.jsx
+++ b/webapp/components/rhs_root_post.jsx
@@ -7,8 +7,8 @@ import PostMessageContainer from 'components/post_view/components/post_message_c
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 DotMenu from 'components/dot_menu/dot_menu.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx';
@@ -44,8 +44,6 @@ export default class RhsRootPost extends React.Component {
this.reactEmojiClick = this.reactEmojiClick.bind(this);
this.handleDropdownOpened = this.handleDropdownOpened.bind(this);
- this.canEdit = false;
- this.canDelete = false;
this.editDisableAction = new DelayedAction(this.handleEditDisable);
this.state = {
@@ -75,10 +73,6 @@ export default class RhsRootPost extends React.Component {
GlobalActions.showGetPostLinkModal(this.props.post);
}
- handleEditDisable() {
- this.canEdit = false;
- }
-
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.status !== this.props.status) {
return true;
@@ -225,18 +219,10 @@ export default class RhsRootPost extends React.Component {
var timestamp = user ? user.last_picture_update : 0;
var channel = ChannelStore.get(post.channel_id);
- this.canDelete = PostUtils.canDeletePost(post);
- this.canEdit = PostUtils.canEditPost(post, this.editDisableAction);
-
const isEphemeral = Utils.isPostEphemeral(post);
const isPending = post.state === Constants.POST_FAILED || post.state === Constants.POST_LOADING;
const isSystemMessage = PostUtils.isSystemMessage(post);
- var type = 'Post';
- if (post.root_id.length > 0) {
- type = 'Comment';
- }
-
var channelName;
if (channel) {
if (channel.type === 'D') {
@@ -274,158 +260,6 @@ 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>
- );
- }
- }
-
- if (!isSystemMessage) {
- dropdownContents.push(
- <li
- key='rhs-root-permalink'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.handlePermalink}
- >
- <FormattedMessage
- id='rhs_root.permalink'
- defaultMessage='Permalink'
- />
- </a>
- </li>
- );
-
- if (post.is_pinned) {
- dropdownContents.push(
- <li
- key='rhs-root-unpin'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.unpinPost}
- >
- <FormattedMessage
- id='rhs_root.unpin'
- defaultMessage='Un-pin from channel'
- />
- </a>
- </li>
- );
- } else {
- dropdownContents.push(
- <li
- key='rhs-root-pin'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.pinPost}
- >
- <FormattedMessage
- id='rhs_root.pin'
- defaultMessage='Pin to channel'
- />
- </a>
- </li>
- );
- }
- }
-
- if (this.canDelete) {
- dropdownContents.push(
- <li
- key='rhs-root-delete'
- role='presentation'
- >
- <a
- href='#'
- role='menuitem'
- onClick={() => GlobalActions.showDeletePostModal(post, this.props.commentCount)}
- >
- <FormattedMessage
- id='rhs_root.del'
- defaultMessage='Delete'
- />
- </a>
- </li>
- );
- }
-
- if (this.canEdit) {
- dropdownContents.push(
- <li
- key='rhs-root-edit'
- role='presentation'
- className={this.canEdit ? '' : 'hide'}
- >
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-refocusid='#reply_textbox'
- data-title={type}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- >
- <FormattedMessage
- id='rhs_root.edit'
- defaultMessage='Edit'
- />
- </a>
- </li>
- );
- }
-
- var rootOptions = '';
- if (dropdownContents.length > 0) {
- rootOptions = (
- <RhsDropdown
- dropdownContents={dropdownContents}
- handleDropdownOpened={this.handleDropdownOpened}
- />
- );
- }
-
let fileAttachment = null;
if (post.file_ids && post.file_ids.length > 0) {
fileAttachment = (
@@ -559,6 +393,15 @@ export default class RhsRootPost extends React.Component {
hour12: !this.props.useMilitaryTime
};
+ const dotMenu = (
+ <DotMenu
+ idPrefix={Constants.RHS_ROOT}
+ post={this.props.post}
+ isFlagged={this.props.isFlagged}
+ handleDropdownOpened={this.handleDropdownOpened}
+ />
+ );
+
return (
<div
id='thread--root'
@@ -584,7 +427,7 @@ export default class RhsRootPost extends React.Component {
ref='dotMenu'
className='col col__reply'
>
- {rootOptions}
+ {dotMenu}
{react}
</div>
</div>
diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx
index dc0856888..7a6dac90e 100644
--- a/webapp/utils/constants.jsx
+++ b/webapp/utils/constants.jsx
@@ -956,7 +956,10 @@ export const Constants = {
ANIMATION_TIMEOUT: 1000,
SEARCH_TIMEOUT_MILLISECONDS: 100,
DIAGNOSTICS_SEGMENT_KEY: 'fwb7VPbFeQ7SKp3wHm1RzFUuXZudqVok',
- TEST_ID_COUNT: 10
+ TEST_ID_COUNT: 10,
+ CENTER: 'center',
+ RHS: 'rhs',
+ RHS_ROOT: 'rhsroot'
};
export default Constants;