// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import PropTypes from 'prop-types';
import React, {Component} from 'react';
import {FormattedMessage} from 'react-intl';
import {OverlayTrigger, Tooltip} from 'react-bootstrap';
import Permissions from 'mattermost-redux/constants/permissions';
import {showGetPostLinkModal} from 'actions/global_actions.jsx';
import {Locations, ModalIdentifiers, UNSET_POST_EDIT_TIME_LIMIT} from 'utils/constants.jsx';
import DeletePostModal from 'components/delete_post_modal';
import DelayedAction from 'utils/delayed_action.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
import * as Utils from 'utils/utils.jsx';
import ChannelPermissionGate from 'components/permissions_gates/channel_permission_gate';
import Pluggable from 'plugins/pluggable';
import Menu from 'components/widgets/menu/menu.jsx';
import MenuWrapper from 'components/widgets/menu/menu_wrapper.jsx';
import MenuItemAction from 'components/widgets/menu/menu_items/menu_item_action.jsx';
const MENU_BOTTOM_MARGIN = 80;
export default class DotMenu extends Component {
static propTypes = {
post: PropTypes.object.isRequired,
teamId: PropTypes.string,
location: PropTypes.oneOf([Locations.CENTER, Locations.RHS_ROOT, Locations.RHS_COMMENT, Locations.SEARCH]).isRequired,
commentCount: PropTypes.number,
isFlagged: PropTypes.bool,
handleCommentClick: PropTypes.func,
handleDropdownOpened: PropTypes.func,
handleAddReactionClick: PropTypes.func,
isReadOnly: PropTypes.bool,
pluginMenuItems: PropTypes.arrayOf(PropTypes.object),
isLicensed: PropTypes.bool.isRequired,
postEditTimeLimit: PropTypes.string.isRequired,
enableEmojiPicker: PropTypes.bool.isRequired,
actions: PropTypes.shape({
/**
* Function flag the post
*/
flagPost: PropTypes.func.isRequired,
/**
* Function to unflag the post
*/
unflagPost: PropTypes.func.isRequired,
/**
* Function to set the editing post
*/
setEditingPost: PropTypes.func.isRequired,
/**
* Function to pin the post
*/
pinPost: PropTypes.func.isRequired,
/**
* Function to unpin the post
*/
unpinPost: PropTypes.func.isRequired,
/**
* Function to open a modal
*/
openModal: PropTypes.func.isRequired,
}).isRequired,
}
static defaultProps = {
post: {},
commentCount: 0,
isFlagged: false,
isReadOnly: false,
pluginMenuItems: [],
location: Locations.CENTER,
}
constructor(props) {
super(props);
this.editDisableAction = new DelayedAction(this.handleEditDisable);
this.state = {
openUp: false,
};
this.buttonRef = React.createRef();
}
disableCanEditPostByTime() {
const {post, isLicensed, postEditTimeLimit} = this.props;
const canEdit = PostUtils.canEditPost(post);
if (canEdit && isLicensed) {
if (String(postEditTimeLimit) !== String(UNSET_POST_EDIT_TIME_LIMIT)) {
const milliseconds = 1000;
const timeLeft = (post.create_at + (postEditTimeLimit * milliseconds)) - Utils.getTimestamp();
if (timeLeft > 0) {
this.editDisableAction.fireAfter(timeLeft + milliseconds);
}
}
}
}
componentDidMount() {
this.disableCanEditPostByTime();
}
static getDerivedStateFromProps(props) {
return {
canDelete: PostUtils.canDeletePost(props.post) && !props.isReadOnly,
canEdit: PostUtils.canEditPost(props.post) && !props.isReadOnly,
};
}
componentWillUnmount() {
this.editDisableAction.cancel();
}
handleEditDisable = () => {
this.setState({canEdit: false});
}
handleFlagMenuItemActivated = () => {
if (this.props.isFlagged) {
this.props.actions.unflagPost(this.props.post.id);
} else {
this.props.actions.flagPost(this.props.post.id);
}
}
// listen to clicks/taps on add reaction menu item and pass to parent handler
handleAddReactionMenuItemActivated = (e) => {
e.preventDefault();
// to be safe, make sure the handler function has been defined
if (this.props.handleAddReactionClick) {
this.props.handleAddReactionClick();
}
}
handlePermalinkMenuItemActivated = (e) => {
e.preventDefault();
showGetPostLinkModal(this.props.post);
}
handlePinMenuItemActivated = () => {
if (this.props.post.is_pinned) {
this.props.actions.unpinPost(this.props.post.id);
} else {
this.props.actions.pinPost(this.props.post.id);
}
}
handleDeleteMenuItemActivated = (e) => {
e.preventDefault();
const deletePostModalData = {
ModalId: ModalIdentifiers.DELETE_POST,
dialogType: DeletePostModal,
dialogProps: {
post: this.props.post,
commentCount: this.props.commentCount,
isRHS: this.props.location === Locations.RHS_ROOT || this.props.location === Locations.RHS_COMMENT,
},
};
this.props.actions.openModal(deletePostModalData);
}
handleEditMenuItemActivated = () => {
this.props.actions.setEditingPost(
this.props.post.id,
this.props.commentCount,
this.props.location === Locations.CENTER ? 'post_textbox' : 'reply_textbox',
this.props.post.root_id ? Utils.localizeMessage('rhs_comment.comment', 'Comment') : Utils.localizeMessage('create_post.post', 'Post'),
this.props.location === Locations.RHS_ROOT || this.props.location === Locations.RHS_COMMENT,
);
}
tooltip = (
)
refCallback = (ref) => {
if (ref) {
const rect = ref.rect();
const y = rect.y || rect.top;
const height = rect.height;
const windowHeight = window.innerHeight;
if ((y + height) > (windowHeight - MENU_BOTTOM_MARGIN)) {
this.setState({openUp: true});
}
}
}
render() {
const isSystemMessage = PostUtils.isSystemMessage(this.props.post);
const isMobile = Utils.isMobile();
const pluginItems = this.props.pluginMenuItems.
filter((item) => {
return item.filter ? item.filter(this.props.post.id) : item;
}).
map((item) => {
return (
{
if (item.action) {
item.action(this.props.post.id);
}
}}
/>
);
});
if (!this.state.canDelete && !this.state.canEdit && pluginItems.length === 0 && isSystemMessage) {
return null;
}
return (
);
}
}