Commit 58aac948 authored by Martin Kraft's avatar Martin Kraft
Browse files

Merge remote-tracking branch 'origin/master' into advanced-permissions-phase-2

parents fcd2be45 3f362427
......@@ -58,7 +58,7 @@ export function executeCommand(message, args, success, error) {
cmdLength = msg.length;
}
const cmd = msg.substring(0, cmdLength).toLowerCase();
msg = cmd + msg.substring(cmdLength, msg.length);
msg = cmd + ' ' + msg.substring(cmdLength, msg.length).trim();
switch (cmd) {
case '/search':
......
......@@ -278,3 +278,16 @@ export const openMenu = () => (dispatch) => dispatch({
export const closeMenu = () => (dispatch) => dispatch({
type: ActionTypes.CLOSE_RHS_MENU,
});
export function setRhsExpanded(expanded) {
return {
type: ActionTypes.SET_RHS_EXPANDED,
expanded,
};
}
export function toggleRhsExpanded() {
return {
type: ActionTypes.TOGGLE_RHS_EXPANDED,
};
}
......@@ -121,7 +121,7 @@ export default class FileAttachmentList extends React.Component {
}
return (
<div>
<React.Fragment>
<div className='post-image__columns clearfix'>
{postFiles}
</div>
......@@ -131,7 +131,7 @@ export default class FileAttachmentList extends React.Component {
startIndex={this.state.startImgIndex}
fileInfos={sortedFileInfos}
/>
</div>
</React.Fragment>
);
}
}
......
......@@ -98,11 +98,11 @@ export default class LoggedIn extends React.Component {
$('body').on('mouseenter mouseleave', '.post', function mouseOver(ev) {
if (ev.type === 'mouseenter') {
$(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--after');
$(this).parent('div').next('.date-separator, .new-separator').addClass('hovered--before');
$(this).prev('.date-separator, .new-separator').addClass('hovered--after');
$(this).next('.date-separator, .new-separator').addClass('hovered--before');
} else {
$(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--after');
$(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--before');
$(this).prev('.date-separator, .new-separator').removeClass('hovered--after');
$(this).next('.date-separator, .new-separator').removeClass('hovered--before');
}
});
......@@ -118,11 +118,11 @@ export default class LoggedIn extends React.Component {
$('body').on('mouseenter mouseleave', '.post.post--comment.same--root', function mouseOver(ev) {
if (ev.type === 'mouseenter') {
$(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--comment');
$(this).parent('div').next('.date-separator, .new-separator').addClass('hovered--comment');
$(this).prev('.date-separator, .new-separator').addClass('hovered--comment');
$(this).next('.date-separator, .new-separator').addClass('hovered--comment');
} else {
$(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--comment');
$(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--comment');
$(this).prev('.date-separator, .new-separator').removeClass('hovered--comment');
$(this).next('.date-separator, .new-separator').removeClass('hovered--comment');
}
});
......
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {Posts} from 'mattermost-redux/constants';
import PostBodyAdditionalContent from 'components/post_view/post_body_additional_content';
import PostMessageContainer from 'components/post_view/post_message_view';
import PostMessageView from 'components/post_view/post_message_view';
export default function MessageWithAdditionalContent({post, previewCollapsed, previewEnabled, isEmbedVisible, pluginPostTypes}) {
const hasPlugin = post.type && pluginPostTypes.hasOwnProperty(post.type);
let msg;
const messageWrapper = (
<PostMessageContainer
<PostMessageView
post={post}
isRHS={true}
hasMention={true}
......@@ -31,4 +32,4 @@ export default function MessageWithAdditionalContent({post, previewCollapsed, pr
);
}
return msg;
}
\ No newline at end of file
}
......@@ -296,41 +296,38 @@ export default class Post extends React.PureComponent {
return (
<div
ref={this.getRef}
id={'post_' + post.id}
className={this.getClassName(post, isSystemMessage, fromWebhook, fromAutoResponder)}
onMouseOver={this.setHover}
onMouseLeave={this.unsetHover}
>
<div
id={'post_' + post.id}
className={this.getClassName(post, isSystemMessage, fromWebhook, fromAutoResponder)}
>
<div className={'post__content ' + centerClass}>
{profilePicContainer}
<div>
<PostHeader
post={post}
handleCommentClick={this.handleCommentClick}
handleDropdownOpened={this.handleDropdownOpened}
user={this.props.user}
currentUser={this.props.currentUser}
compactDisplay={this.props.compactDisplay}
status={this.props.status}
isBusy={this.props.isBusy}
lastPostCount={this.props.lastPostCount}
isFirstReply={this.props.isFirstReply}
replyCount={this.props.replyCount}
showTimeWithoutHover={!hideProfilePicture}
getPostList={this.props.getPostList}
hover={this.state.hover}
/>
<PostBody
post={post}
handleCommentClick={this.handleCommentClick}
compactDisplay={this.props.compactDisplay}
lastPostCount={this.props.lastPostCount}
isCommentMention={this.props.isCommentMention}
isFirstReply={this.props.isFirstReply}
/>
</div>
<div className={'post__content ' + centerClass}>
{profilePicContainer}
<div>
<PostHeader
post={post}
handleCommentClick={this.handleCommentClick}
handleDropdownOpened={this.handleDropdownOpened}
user={this.props.user}
currentUser={this.props.currentUser}
compactDisplay={this.props.compactDisplay}
status={this.props.status}
isBusy={this.props.isBusy}
lastPostCount={this.props.lastPostCount}
isFirstReply={this.props.isFirstReply}
replyCount={this.props.replyCount}
showTimeWithoutHover={!hideProfilePicture}
getPostList={this.props.getPostList}
hover={this.state.hover}
/>
<PostBody
post={post}
handleCommentClick={this.handleCommentClick}
compactDisplay={this.props.compactDisplay}
lastPostCount={this.props.lastPostCount}
isCommentMention={this.props.isCommentMention}
isFirstReply={this.props.isFirstReply}
/>
</div>
</div>
</div>
......
......@@ -234,11 +234,7 @@ export default class PostBody extends React.PureComponent {
}
const messageWrapper = (
<div
key={`${post.id}_message`}
id={`${post.id}_message`}
className={postClass}
>
<React.Fragment>
{failedOptions}
{sending}
<PostMessageView
......@@ -247,7 +243,7 @@ export default class PostBody extends React.PureComponent {
compactDisplay={this.props.compactDisplay}
hasMention={true}
/>
</div>
</React.Fragment>
);
const hasPlugin = post.type && this.props.pluginPostTypes.hasOwnProperty(post.type);
......@@ -281,7 +277,10 @@ export default class PostBody extends React.PureComponent {
return (
<div>
{comment}
<div className={`post__body ${mentionHighlightClass} ${ephemeralPostClass}`}>
<div
id={`${post.id}_message`}
className={`post__body ${mentionHighlightClass} ${ephemeralPostClass} ${postClass}`}
>
{messageWithAdditionalContent}
{fileAttachmentHolder}
<ReactionListContainer
......
......@@ -5,11 +5,15 @@ import {connect} from 'react-redux';
import {Preferences} from 'mattermost-redux/constants';
import {getTheme, getBool} from 'mattermost-redux/selectors/entities/preferences';
import {getIsRhsExpanded, getIsRhsOpen} from 'selectors/rhs';
import PostMessageView from './post_message_view.jsx';
function mapStateToProps(state) {
return {
enableFormatting: getBool(state, Preferences.CATEGORY_ADVANCED_SETTINGS, 'formatting', true),
isRHSExpanded: getIsRhsExpanded(state),
isRHSOpen: getIsRhsOpen(state),
pluginPostTypes: state.plugins.postTypes,
theme: getTheme(state),
};
......
......@@ -10,6 +10,9 @@ import PostMarkdown from 'components/post_markdown';
import * as PostUtils from 'utils/post_utils.jsx';
import * as Utils from 'utils/utils.jsx';
// This must match the max-height defined in CSS for the collapsed content div
const MAX_POST_HEIGHT = 600;
export default class PostMessageView extends React.PureComponent {
static propTypes = {
......@@ -43,6 +46,16 @@ export default class PostMessageView extends React.PureComponent {
*/
isRHS: PropTypes.bool,
/**
* Whether or not the RHS is visible
*/
isRHSOpen: PropTypes.bool,
/**
* Whether or not the RHS is expanded
*/
isRHSExpanded: PropTypes.bool,
/*
* Logged in user's theme
*/
......@@ -60,6 +73,68 @@ export default class PostMessageView extends React.PureComponent {
pluginPostTypes: {},
};
constructor(props) {
super(props);
this.state = {
collapse: true,
hasOverflow: false,
};
}
componentDidMount() {
this.checkOverflow();
window.addEventListener('resize', this.handleResize);
}
componentWillUpdate(nextProps) {
if (this.props.post.id !== nextProps.post.id) {
this.setState({
collapse: true,
});
}
}
componentDidUpdate(prevProps) {
if (this.props.post !== prevProps.post ||
this.props.isRHSOpen !== prevProps.isRHSOpen ||
this.props.isRHSExpanded !== prevProps.isRHSExpanded) {
this.checkOverflow();
}
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
}
handleResize = () => {
this.checkOverflow();
};
checkOverflow = () => {
const content = this.refs.content;
let hasOverflow = false;
if (content && content.scrollHeight > MAX_POST_HEIGHT) {
hasOverflow = true;
}
if (hasOverflow !== this.state.hasOverflow) {
this.setState({
hasOverflow,
});
}
};
toggleCollapse = () => {
this.setState((state) => {
return {
collapse: !state.collapse,
};
});
};
renderDeletedPost() {
return (
<p>
......@@ -133,21 +208,63 @@ export default class PostMessageView extends React.PureComponent {
message = message.concat(visibleMessage);
}
let className = 'post-message';
if (this.state.collapse) {
className += ' post-message--collapsed';
} else {
className += ' post-message--expanded';
}
let overflow = null;
if (this.state.hasOverflow) {
let icon = 'fa fa-angle-up';
let text = 'Show Less';
if (this.state.collapse) {
icon = 'fa fa-angle-down';
text = 'Show More';
}
overflow = (
<div className='post-collapse'>
<div className='post-collapse__gradient'/>
<div className='post-collapse__show-more'>
<div className='post-collapse__show-more-line'/>
<button
className='post-collapse__show-more-button'
onClick={this.toggleCollapse}
>
<span className={icon}/>
{text}
</button>
<div className='post-collapse__show-more-line'/>
</div>
</div>
);
className += ' post-message--overflow';
}
return (
<div>
<span
id={postId}
className='post-message__text'
onClick={Utils.handleFormattedTextClick}
<div className={className}>
<div
className='post-message__text-container'
ref='content'
>
<PostMarkdown
message={message}
isRHS={isRHS}
options={options}
post={post}
/>
</span>
{this.renderEditedIndicator()}
<div
id={postId}
className='post-message__text'
onClick={Utils.handleFormattedTextClick}
>
<PostMarkdown
message={message}
isRHS={isRHS}
options={options}
post={post}
/>
</div>
{this.renderEditedIndicator()}
</div>
{overflow}
</div>
);
}
......
......@@ -10,6 +10,7 @@ import {
showFlaggedPosts,
showPinnedPosts,
closeRightHandSide,
toggleRhsExpanded,
} from 'actions/views/rhs';
import RhsHeaderPost from './rhs_header_post.jsx';
......@@ -22,6 +23,7 @@ function mapDispatchToProps(dispatch) {
showFlaggedPosts,
showPinnedPosts,
closeRightHandSide,
toggleRhsExpanded,
}, dispatch),
};
}
......
......@@ -14,34 +14,16 @@ export default class RhsHeaderPost extends React.Component {
previousRhsState: PropTypes.oneOf(
Object.values(RHSStates)
),
toggleSize: PropTypes.func,
shrink: PropTypes.func,
actions: PropTypes.shape({
showMentions: PropTypes.func,
showSearchResults: PropTypes.func,
showFlaggedPosts: PropTypes.func,
showPinnedPosts: PropTypes.func,
closeRightHandSide: PropTypes.func,
toggleRhsExpanded: PropTypes.func,
}),
};
constructor(props) {
super(props);
this.state = {};
}
handleClose = (e) => {
e.preventDefault();
this.props.actions.closeRightHandSide();
this.props.shrink();
}
toggleSize = (e) => {
e.preventDefault();
this.props.toggleSize();
}
handleBack = (e) => {
e.preventDefault();
......@@ -178,7 +160,7 @@ export default class RhsHeaderPost extends React.Component {
type='button'
className='sidebar--right__expand'
aria-label='Expand'
onClick={this.toggleSize}
onClick={this.props.actions.toggleRhsExpanded}
>
<OverlayTrigger
trigger={['hover', 'focus']}
......@@ -201,7 +183,7 @@ export default class RhsHeaderPost extends React.Component {
type='button'
className='sidebar--right__close'
aria-label='Close'
onClick={this.handleClose}
onClick={this.props.actions.closeRightHandSide}
>
<OverlayTrigger
......
......@@ -56,8 +56,6 @@ export default class RhsThread extends React.Component {
previousRhsState: PropTypes.string,
isWebrtc: PropTypes.bool,
currentUser: PropTypes.object.isRequired,
toggleSize: PropTypes.func,
shrink: PropTypes.func,
previewCollapsed: PropTypes.string.isRequired,
previewEnabled: PropTypes.bool.isRequired,
postsEmbedVisibleObj: PropTypes.object,
......@@ -358,24 +356,23 @@ export default class RhsThread extends React.Component {
const keyPrefix = comPost.id ? comPost.id : comPost.pending_post_id;
const reverseCount = postsLength - i - 1;
commentsLists.push(
<div key={keyPrefix + 'commentKey'}>
<RhsComment
ref={comPost.id}
post={comPost}
teamId={this.props.channel.team_id}
lastPostCount={(reverseCount >= 0 && reverseCount < Constants.TEST_ID_COUNT) ? reverseCount : -1}
user={p}
currentUser={this.props.currentUser}
compactDisplay={this.state.compactDisplay}
isFlagged={isFlagged}
status={status}
isBusy={this.state.isBusy}
removePost={this.props.actions.removePost}
previewCollapsed={this.props.previewCollapsed}
previewEnabled={this.props.previewEnabled}
isEmbedVisible={this.props.postsEmbedVisibleObj[comPost.id]}
/>
</div>
<RhsComment
key={keyPrefix + 'commentKey'}
ref={comPost.id}
post={comPost}
teamId={this.props.channel.team_id}
lastPostCount={(reverseCount >= 0 && reverseCount < Constants.TEST_ID_COUNT) ? reverseCount : -1}
user={p}
currentUser={this.props.currentUser}
compactDisplay={this.state.compactDisplay}
isFlagged={isFlagged}
status={status}
isBusy={this.state.isBusy}
removePost={this.props.actions.removePost}
previewCollapsed={this.props.previewCollapsed}
previewEnabled={this.props.previewEnabled}
isEmbedVisible={this.props.postsEmbedVisibleObj[comPost.id]}
/>
);
}
......@@ -425,8 +422,6 @@ export default class RhsThread extends React.Component {
<RhsHeaderPost
previousRhsState={this.props.previousRhsState}
isWebrtc={this.props.isWebrtc}
toggleSize={this.props.toggleSize}
shrink={this.props.shrink}
/>
<Scrollbars
autoHide={true}
......
......@@ -7,7 +7,6 @@ import {getSearchResults} from 'mattermost-redux/selectors/entities/posts';
import * as PreferenceSelectors from 'mattermost-redux/selectors/entities/preferences';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {selectPostFromRightHandSideSearch} from 'actions/views/rhs';
import {
getSearchResultsTerms,
getIsSearchingTerm,
......@@ -82,8 +81,4 @@ function makeMapStateToProps() {
};
}
const mapDispatchToProps = {
selectPost: selectPostFromRightHandSideSearch,
};
export default connect(makeMapStateToProps, mapDispatchToProps)(SearchResults);
export default connect(makeMapStateToProps)(SearchResults);
......@@ -49,13 +49,10 @@ export default class SearchResults extends React.PureComponent {
isSearchingFlaggedPost: PropTypes.bool,
isSearchingPinnedPost: PropTypes.bool,
compactDisplay: PropTypes.bool,
toggleSize: PropTypes.func,
shrink: PropTypes.func,
isMentionSearch: PropTypes.bool,
isFlaggedPosts: PropTypes.bool,
isPinnedPosts: PropTypes.bool,
channelDisplayName: PropTypes.string.isRequired,
selectPost: PropTypes.func,
dataRetentionEnableMessageDeletion: PropTypes.bool.isRequired,
dataRetentionMessageRetentionDays: PropTypes.string,
};
......@@ -360,11 +357,9 @@ export default class SearchResults extends React.PureComponent {
user={profile}
term={searchTerms}
isMentionSearch={this.props.isMentionSearch}
shrink={this.props.shrink}
isFlagged={isFlagged}
isBusy={this.state.isBusy}
status={status}
onSelect={this.props.selectPost}
/>
);
}, this);
......@@ -374,8 +369,6 @@ export default class SearchResults extends React.PureComponent {
<div className='sidebar-right__body'>
<SearchResultsHeader
isMentionSearch={this.props.isMentionSearch}
toggleSize={this.props.toggleSize}
shrink={this.props.shrink}
isFlaggedPosts={this.props.isFlaggedPosts}
isPinnedPosts={this.props.isPinnedPosts}
channelDisplayName={this.props.channelDisplayName}
......
......@@ -4,7 +4,10 @@
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';