diff options
-rw-r--r-- | webapp/components/post_view/post_attachment_opengraph/index.js | 4 | ||||
-rw-r--r-- | webapp/components/post_view/post_attachment_opengraph/post_attachment_opengraph.jsx | 73 | ||||
-rw-r--r-- | webapp/components/post_view/post_body_additional_content.jsx | 1 | ||||
-rw-r--r-- | webapp/sass/layout/_webhooks.scss | 49 | ||||
-rw-r--r-- | webapp/sass/responsive/_mobile.scss | 10 | ||||
-rw-r--r-- | webapp/utils/constants.jsx | 3 | ||||
-rw-r--r-- | webapp/utils/utils.jsx | 2 |
7 files changed, 132 insertions, 10 deletions
diff --git a/webapp/components/post_view/post_attachment_opengraph/index.js b/webapp/components/post_view/post_attachment_opengraph/index.js index e0bec8f36..1f889f1d6 100644 --- a/webapp/components/post_view/post_attachment_opengraph/index.js +++ b/webapp/components/post_view/post_attachment_opengraph/index.js @@ -2,6 +2,7 @@ // See License.txt for license information. import {connect} from 'react-redux'; +import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; import {bindActionCreators} from 'redux'; import {getOpenGraphMetadata} from 'mattermost-redux/actions/posts'; import {getOpenGraphMetadataForUrl} from 'mattermost-redux/selectors/entities/posts'; @@ -11,7 +12,8 @@ import PostAttachmentOpenGraph from './post_attachment_opengraph.jsx'; function mapStateToProps(state, ownProps) { return { ...ownProps, - openGraphData: getOpenGraphMetadataForUrl(state, ownProps.link) + openGraphData: getOpenGraphMetadataForUrl(state, ownProps.link), + currentUser: getCurrentUser(state), }; } diff --git a/webapp/components/post_view/post_attachment_opengraph/post_attachment_opengraph.jsx b/webapp/components/post_view/post_attachment_opengraph/post_attachment_opengraph.jsx index 743d7a22a..04738fdcc 100644 --- a/webapp/components/post_view/post_attachment_opengraph/post_attachment_opengraph.jsx +++ b/webapp/components/post_view/post_attachment_opengraph/post_attachment_opengraph.jsx @@ -5,9 +5,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import {postListScrollChange} from 'actions/global_actions.jsx'; +import {updatePost} from 'actions/post_actions.jsx'; import * as Utils from 'utils/utils.jsx'; import * as CommonUtils from 'utils/commons.jsx'; +import {PostTypes} from 'utils/constants.jsx'; export default class PostAttachmentOpenGraph extends React.PureComponent { static propTypes = { @@ -18,6 +20,16 @@ export default class PostAttachmentOpenGraph extends React.PureComponent { link: PropTypes.string.isRequired, /** + * The current user viewing the post + */ + currentUser: PropTypes.object, + + /** + * The post where this link is included + */ + post: PropTypes.object, + + /** * The open graph data to render */ openGraphData: PropTypes.object, @@ -62,18 +74,28 @@ export default class PostAttachmentOpenGraph extends React.PureComponent { this.toggleImageVisibility = this.toggleImageVisibility.bind(this); this.onImageLoad = this.onImageLoad.bind(this); this.onImageError = this.onImageError.bind(this); + this.handleRemovePreview = this.handleRemovePreview.bind(this); } componentWillMount() { + const removePreview = this.isRemovePreview(this.props.post, this.props.currentUser); + this.setState({ imageLoaded: this.IMAGE_LOADED.LOADING, imageVisible: this.props.previewCollapsed.startsWith('false'), - hasLargeImage: false + hasLargeImage: false, + removePreview }); this.fetchData(this.props.link); } componentWillReceiveProps(nextProps) { + if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) { + const removePreview = this.isRemovePreview(nextProps.post, nextProps.currentUser); + this.setState({ + removePreview + }); + } if (nextProps.link !== this.props.link) { this.fetchData(nextProps.link); } @@ -94,12 +116,12 @@ export default class PostAttachmentOpenGraph extends React.PureComponent { } } - getBestImageUrl() { - if (Utils.isEmptyObject(this.props.openGraphData.images)) { + getBestImageUrl(data) { + if (Utils.isEmptyObject(data.images)) { return null; } - const bestImage = CommonUtils.getNearestPoint(this.imageDimentions, this.props.openGraphData.images, 'width', 'height'); + const bestImage = CommonUtils.getNearestPoint(this.imageDimentions, data.images, 'width', 'height'); return bestImage.secure_url || bestImage.url; } @@ -208,14 +230,50 @@ export default class PostAttachmentOpenGraph extends React.PureComponent { return text; } + handleRemovePreview() { + const props = Object.assign({}, this.props.post.props); + props[PostTypes.REMOVE_LINK_PREVIEW] = 'true'; + + const patchedPost = ({ + id: this.props.post.id, + props + }); + + updatePost(patchedPost, () => { + this.setState({removePreview: true}); + }); + } + + isRemovePreview(post, currentUser) { + if (post && post.props && currentUser.id === post.user_id) { + return post.props[PostTypes.REMOVE_LINK_PREVIEW] && post.props[PostTypes.REMOVE_LINK_PREVIEW] === 'true'; + } + + return false; + } + render() { - if (!this.props.openGraphData || Utils.isEmptyObject(this.props.openGraphData.description)) { + const data = this.props.openGraphData; + if (!data || Utils.isEmptyObject(data.description) || this.state.removePreview) { return null; } - const data = this.props.openGraphData; - const imageUrl = this.getBestImageUrl(); + let removePreviewButton; + if (this.props.currentUser.id === this.props.post.user_id) { + removePreviewButton = ( + <button + id='removePreviewButton' + type='button' + className='btn-close' + aria-label='Close' + onClick={this.handleRemovePreview} + > + <span aria-hidden='true'>{'×'}</span> + </button> + ); + } + const imageUrl = this.getBestImageUrl(data); if (imageUrl) { this.loadImage(imageUrl); } @@ -233,6 +291,7 @@ export default class PostAttachmentOpenGraph extends React.PureComponent { className={'attachment__body__wrap attachment__body__wrap--opengraph'} > <span className='sitename'>{this.truncateText(data.site_name)}</span> + {removePreviewButton} <h1 className={'attachment__title attachment__title--opengraph' + (data.title ? '' : ' is-url')} > diff --git a/webapp/components/post_view/post_body_additional_content.jsx b/webapp/components/post_view/post_body_additional_content.jsx index be9e37827..1d900018a 100644 --- a/webapp/components/post_view/post_body_additional_content.jsx +++ b/webapp/components/post_view/post_body_additional_content.jsx @@ -162,6 +162,7 @@ export default class PostBodyAdditionalContent extends React.PureComponent { <PostAttachmentOpenGraph link={link} previewCollapsed={this.props.previewCollapsed} + post={this.props.post} /> ); } diff --git a/webapp/sass/layout/_webhooks.scss b/webapp/sass/layout/_webhooks.scss index 6f6ab0c90..edb4cbd93 100644 --- a/webapp/sass/layout/_webhooks.scss +++ b/webapp/sass/layout/_webhooks.scss @@ -38,6 +38,17 @@ .post { .attachment { + margin-left: -20px; + position: relative; + + &:hover { + .attachment__body__wrap { + .btn-close { + visibility: visible; + } + } + } + &.attachment--opengraph { max-width: 800px; } @@ -46,7 +57,7 @@ border-radius: 4px; border-style: solid; border-width: 1px; - margin: 5px 0; + margin: 5px 0 5px 20px; padding: 2px 5px; } @@ -99,6 +110,42 @@ vertical-align: top; width: 100%; } + + .btn-close { + @include opacity(.4); + background: transparent; + border: none; + color: inherit; + font-size: 21px; + font-weight: 500; + height: 20px; + left: -7px; + line-height: 20px; + outline: none; + padding: 0; + position: absolute; + text-align: center; + text-decoration: none; + text-shadow: none; + visibility: hidden; + width: 20px; + z-index: 5; + + span { + font-family: 'Open Sans', sans-serif; + line-height: 10px; + } + + &:hover { + @include opacity(.9); + } + } + + &:hover { + .btn-close { + visibility: visible; + } + } } .attachment__body { diff --git a/webapp/sass/responsive/_mobile.scss b/webapp/sass/responsive/_mobile.scss index d39797efb..de453f93f 100644 --- a/webapp/sass/responsive/_mobile.scss +++ b/webapp/sass/responsive/_mobile.scss @@ -1349,6 +1349,16 @@ .post { .attachment { + .attachment__body__wrap { + .btn-close { + height: 30px; + left: -15px; + top: 7px; + visibility: visible; + width: 30px; + } + } + .attachment__image { &.attachment__image--openraph { max-width: 200px; diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx index a36a518d8..e9246fdaf 100644 --- a/webapp/utils/constants.jsx +++ b/webapp/utils/constants.jsx @@ -270,7 +270,8 @@ export const PostTypes = { DISPLAYNAME_CHANGE: 'system_displayname_change', PURPOSE_CHANGE: 'system_purpose_change', CHANNEL_DELETED: 'system_channel_deleted', - EPHEMERAL: 'system_ephemeral' + EPHEMERAL: 'system_ephemeral', + REMOVE_LINK_PREVIEW: 'remove_link_preview' }; export const StatTypes = keyMirror({ diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx index 94a2cf286..94f6ca91f 100644 --- a/webapp/utils/utils.jsx +++ b/webapp/utils/utils.jsx @@ -653,6 +653,8 @@ export function applyTheme(theme) { changeCss('.app__body .post.post--comment.other--root.current--user .post-comment, .app__body .more-modal__list .more-modal__row, .app__body .member-div:first-child, .app__body .member-div, .app__body .access-history__table .access__report, .app__body .activity-log__table', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.1)); changeCss('@media(max-width: 1800px){.app__body .inner-wrap.move--left .post.post--comment.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07)); changeCss('.app__body .post.post--hovered', 'background:' + changeOpacity(theme.centerChannelColor, 0.08)); + changeCss('.app__body .attachment__body__wrap.btn-close', 'background:' + changeOpacity(theme.centerChannelColor, 0.08)); + changeCss('.app__body .attachment__body__wrap.btn-close', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2)); changeCss('@media(min-width: 768px){.app__body .post:hover, .app__body .more-modal__list .more-modal__row:hover, .app__body .modal .settings-modal .settings-table .settings-content .section-min:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.08)); changeCss('.app__body .more-modal__row.more-modal__row--selected, .app__body .date-separator.hovered--before:after, .app__body .date-separator.hovered--after:before, .app__body .new-separator.hovered--after:before, .app__body .new-separator.hovered--before:after', 'background:' + changeOpacity(theme.centerChannelColor, 0.07)); changeCss('@media(min-width: 768px){.app__body .suggestion-list__content .command:hover, .app__body .mentions__name:hover, .app__body .dropdown-menu>li>a:focus, .app__body .dropdown-menu>li>a:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.15)); |