diff options
Diffstat (limited to 'webapp/components')
-rw-r--r-- | webapp/components/dot_menu/dot_menu.jsx | 197 | ||||
-rw-r--r-- | webapp/components/dot_menu/dot_menu_edit.jsx | 58 | ||||
-rw-r--r-- | webapp/components/dot_menu/dot_menu_flag.jsx | 70 | ||||
-rw-r--r-- | webapp/components/dot_menu/dot_menu_item.jsx | 107 | ||||
-rw-r--r-- | webapp/components/post_view/components/post_info.jsx | 290 | ||||
-rw-r--r-- | webapp/components/rhs_comment.jsx | 200 | ||||
-rw-r--r-- | webapp/components/rhs_dropdown.jsx | 62 | ||||
-rw-r--r-- | webapp/components/rhs_dropdown_button.jsx | 22 | ||||
-rw-r--r-- | webapp/components/rhs_dropdown_menu.jsx | 22 | ||||
-rw-r--r-- | webapp/components/rhs_root_post.jsx | 179 |
10 files changed, 472 insertions, 735 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> |