From f3934bc7e1e8ef555e1c2e1fe0ece3dbd88ea687 Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Thu, 3 Aug 2017 14:29:31 +0800 Subject: [PLT-1249] Add close button 'x' to the right of a link preview (#7017) * add close button 'x' to the right of a link preview * Updating webhook UI * UI improvements for close button * Adding hover state * Making the close button visible on mobile * previews are permanently disabled/closed for that link * make post as required props * fix JS error of undefined * fix update issue both at center and RHS view --- .../post_view/post_attachment_opengraph/index.js | 4 +- .../post_attachment_opengraph.jsx | 73 +++++++++++++++++++--- .../post_view/post_body_additional_content.jsx | 1 + webapp/sass/layout/_webhooks.scss | 49 ++++++++++++++- webapp/sass/responsive/_mobile.scss | 10 +++ webapp/utils/constants.jsx | 3 +- 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 = { @@ -17,6 +19,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 */ @@ -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 = ( + + ); + } + 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'} > {this.truncateText(data.site_name)} + {removePreviewButton}

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 { ); } 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)); -- cgit v1.2.3-1-g7c22