Commit ea1d4685 authored by Saturnino Abril's avatar Saturnino Abril Committed by Joram Wilander

create DotMenu components and add dotmenu IDs to posts at center and RHS (#6642)

parent 5e0aa96b
// 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>
);
}
}
// 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
};
// 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
};
// 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
};
// 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