From 9b50fb855350ea1e747ee946ab3f955430abeb75 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Tue, 23 Feb 2016 16:28:16 -0500 Subject: Refactored embedded image/video code and prevented them from being displayed on deleted posts --- web/react/components/post_body.jsx | 137 +-------------------- .../components/post_body_additional_content.jsx | 123 ++++++++++++------ web/react/components/post_image.jsx | 62 ++++++++++ 3 files changed, 155 insertions(+), 167 deletions(-) create mode 100644 web/react/components/post_image.jsx diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index 506b38ce6..70cf86748 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -6,13 +6,9 @@ import UserStore from '../stores/user_store.jsx'; import * as Utils from '../utils/utils.jsx'; import * as Emoji from '../utils/emoticons.jsx'; import Constants from '../utils/constants.jsx'; -const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES; import * as TextFormatting from '../utils/text_formatting.jsx'; import twemoji from 'twemoji'; import PostBodyAdditionalContent from './post_body_additional_content.jsx'; -import YoutubeVideo from './youtube_video.jsx'; - -import providers from './providers.json'; import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl'; @@ -31,19 +27,7 @@ class PostBody extends React.Component { constructor(props) { super(props); - this.isImgLoading = false; - this.parseEmojis = this.parseEmojis.bind(this); - this.createEmbed = this.createEmbed.bind(this); - this.createImageEmbed = this.createImageEmbed.bind(this); - this.loadImg = this.loadImg.bind(this); - - const linkData = Utils.extractLinks(this.props.post.message); - - this.state = { - links: linkData, - post: this.props.post - }; } getAllChildNodes(nodeIn) { @@ -69,120 +53,10 @@ class PostBody extends React.Component { }); } - componentWillMount() { - if (this.props.post.filenames.length === 0 && this.state.links && this.state.links.length > 0) { - this.embed = this.createEmbed(this.state.links[0]); - } - } - componentDidMount() { this.parseEmojis(); } - componentDidUpdate() { - this.parseEmojis(); - } - - componentWillReceiveProps(nextProps) { - const linkData = Utils.extractLinks(nextProps.post.message); - if (this.props.post.filenames.length === 0 && this.state.links && this.state.links.length > 0) { - this.embed = this.createEmbed(linkData[0]); - } - this.setState({ - links: linkData - }); - } - - createEmbed(link) { - const post = this.state.post; - - if (!link) { - if (post.type === 'oEmbed') { - post.props.oEmbedLink = ''; - post.type = ''; - } - return null; - } - - const trimmedLink = link.trim(); - - if (Utils.isFeatureEnabled(PreReleaseFeatures.EMBED_PREVIEW)) { - const provider = this.getOembedProvider(trimmedLink); - if (provider != null) { - post.props.oEmbedLink = trimmedLink; - post.type = 'oEmbed'; - this.setState({post, provider}); - return ''; - } - } - - if (YoutubeVideo.isYoutubeLink(link)) { - return ( - - ); - } - - for (let i = 0; i < Constants.IMAGE_TYPES.length; i++) { - const imageType = Constants.IMAGE_TYPES[i]; - const suffix = link.substring(link.length - (imageType.length + 1)); - if (suffix === '.' + imageType || suffix === '=' + imageType) { - return this.createImageEmbed(link, this.state.imgLoaded); - } - } - - return null; - } - - getOembedProvider(link) { - for (let i = 0; i < providers.length; i++) { - for (let j = 0; j < providers[i].patterns.length; j++) { - if (link.match(providers[i].patterns[j])) { - return providers[i]; - } - } - } - return null; - } - - loadImg(src) { - if (this.isImgLoading) { - return; - } - - this.isImgLoading = true; - - const img = new Image(); - img.onload = ( - () => { - this.embed = this.createImageEmbed(src, true); - this.setState({imgLoaded: true}); - } - ); - img.src = src; - } - - createImageEmbed(link, isLoaded) { - if (!isLoaded) { - this.loadImg(link); - return ( - - ); - } - - return ( - - ); - } - render() { const {formatMessage} = this.props.intl; const post = this.props.post; @@ -295,6 +169,7 @@ class PostBody extends React.Component { } let message; + let additionalContent = null; if (this.props.post.state === Constants.POST_DELETED) { message = ( ); + + additionalContent = ( + + ); } return ( @@ -323,12 +202,8 @@ class PostBody extends React.Component { {loading} {message} - {fileAttachmentHolder} - {this.embed} + {additionalContent} ); diff --git a/web/react/components/post_body_additional_content.jsx b/web/react/components/post_body_additional_content.jsx index 4871eea4f..c0a52dc92 100644 --- a/web/react/components/post_body_additional_content.jsx +++ b/web/react/components/post_body_additional_content.jsx @@ -3,72 +3,123 @@ import PostAttachmentList from './post_attachment_list.jsx'; import PostAttachmentOEmbed from './post_attachment_oembed.jsx'; +import PostImage from './post_image.jsx'; +import YoutubeVideo from './youtube_video.jsx'; + +import Constants from '../utils/constants.jsx'; +import OEmbedProviders from './providers.json'; +import * as Utils from '../utils/utils.jsx'; export default class PostBodyAdditionalContent extends React.Component { constructor(props) { super(props); this.getSlackAttachment = this.getSlackAttachment.bind(this); - this.getOembedAttachment = this.getOembedAttachment.bind(this); - this.getComponent = this.getComponent.bind(this); + this.getOEmbedProvider = this.getOEmbedProvider.bind(this); + + this.state = { + link: Utils.extractLinks(props.post.message)[0] + }; } - componentWillMount() { - this.setState({type: this.props.post.type, shouldRender: Boolean(this.props.post.type)}); + componentWillReceiveProps(nextProps) { + if (this.props.post.message !== nextProps.post.message) { + this.setState({ + link: Utils.extractLinks(nextProps.post.message)[0] + }); + } + } + + shouldComponentUpdate(nextProps, nextState) { + if (nextState.link !== this.state.link) { + return true; + } + + if (nextProps.post.type !== this.props.post.type) { + return true; + } + + if (!Utils.areObjectsEqual(nextProps.post.props, this.props.post.props)) { + return true; + } + + return false; } getSlackAttachment() { - const attachments = this.props.post.props && this.props.post.props.attachments || []; + let attachments = []; + if (this.props.post.props && this.props.post.props.attachments) { + attachments = this.props.post.props.attachments; + } + return ( ); } - getOembedAttachment() { - const link = this.props.post.props && this.props.post.props.oEmbedLink || ''; - return ( - - ); + getOEmbedProvider(link) { + for (let i = 0; i < OEmbedProviders.length; i++) { + for (let j = 0; j < OEmbedProviders[i].patterns.length; j++) { + if (link.match(OEmbedProviders[i].patterns[j])) { + return OEmbedProviders[i]; + } + } + } + + return null; } - getComponent() { - switch (this.props.post.type) { - case 'slack_attachment': + render() { + if (this.props.post.type === 'slack_attachment') { return this.getSlackAttachment(); - case 'oEmbed': - return this.getOembedAttachment(); - default: - return ''; } - } - render() { - let content = []; + const link = this.state.link; + if (!link) { + return null; + } - if (this.props.post.type) { - const component = this.getComponent(); + if (Utils.isFeatureEnabled(Constants.PRE_RELEASE_FEATURES.EMBED_PREVIEW)) { + const provider = this.getOEmbedProvider(link); - if (component) { - content = component; + if (provider) { + return ( + + ); } } - return ( -
- {content} -
- ); + if (YoutubeVideo.isYoutubeLink(link)) { + return ( + + ); + } + + for (let i = 0; i < Constants.IMAGE_TYPES.length; i++) { + const imageType = Constants.IMAGE_TYPES[i]; + const suffix = link.substring(link.length - (imageType.length + 1)); + if (suffix === '.' + imageType || suffix === '=' + imageType) { + return ( + + ); + } + } + + return null; } } PostBodyAdditionalContent.propTypes = { - post: React.PropTypes.object.isRequired, - provider: React.PropTypes.object + post: React.PropTypes.object.isRequired }; diff --git a/web/react/components/post_image.jsx b/web/react/components/post_image.jsx new file mode 100644 index 000000000..b35f6d1de --- /dev/null +++ b/web/react/components/post_image.jsx @@ -0,0 +1,62 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +export default class PostImageEmbed extends React.Component { + constructor(props) { + super(props); + + this.state = { + loaded: false + }; + } + + componentWillMount() { + this.loadImg(this.props.link); + } + + componentWillReceiveProps(nextProps) { + if (nextProps.link !== this.props.link) { + this.setState({ + loaded: false + }); + } + } + + componentDidUpdate(prevProps) { + if (!this.state.loaded && prevProps.link !== this.props.link) { + this.loadImg(this.props.link); + } + } + + loadImg(src) { + const img = new Image(); + img.onload = () => { + this.setState({ + loaded: true + }); + }; + img.src = src; + } + + render() { + if (!this.state.loaded) { + return ( + + ); + } + + return ( + + ); + } +} + +PostImageEmbed.propTypes = { + link: React.PropTypes.string.isRequired +}; -- cgit v1.2.3-1-g7c22 From b2e333e83cf3a9693744fa2baab937bb3ac8be28 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Tue, 23 Feb 2016 16:35:15 -0500 Subject: Improved handling when an embedded image cannot be loaded --- web/react/components/post_image.jsx | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/web/react/components/post_image.jsx b/web/react/components/post_image.jsx index b35f6d1de..da4a25794 100644 --- a/web/react/components/post_image.jsx +++ b/web/react/components/post_image.jsx @@ -5,8 +5,12 @@ export default class PostImageEmbed extends React.Component { constructor(props) { super(props); + this.handleLoadComplete = this.handleLoadComplete.bind(this); + this.handleLoadError = this.handleLoadError.bind(this); + this.state = { - loaded: false + loaded: false, + errored: false }; } @@ -17,7 +21,8 @@ export default class PostImageEmbed extends React.Component { componentWillReceiveProps(nextProps) { if (nextProps.link !== this.props.link) { this.setState({ - loaded: false + loaded: false, + errored: false }); } } @@ -30,15 +35,29 @@ export default class PostImageEmbed extends React.Component { loadImg(src) { const img = new Image(); - img.onload = () => { - this.setState({ - loaded: true - }); - }; + img.onload = this.handleLoadComplete; + img.onerror = this.handleLoadError; img.src = src; } + handleLoadComplete() { + this.setState({ + loaded: true + }); + } + + handleLoadError() { + this.setState({ + errored: true, + loaded: true + }); + } + render() { + if (this.state.errored) { + return null; + } + if (!this.state.loaded) { return ( Date: Tue, 23 Feb 2016 16:45:43 -0500 Subject: Removed unnecessary state from PostBodyAdditionalContent --- .../components/post_body_additional_content.jsx | 26 +++------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/web/react/components/post_body_additional_content.jsx b/web/react/components/post_body_additional_content.jsx index c0a52dc92..a76c59fb3 100644 --- a/web/react/components/post_body_additional_content.jsx +++ b/web/react/components/post_body_additional_content.jsx @@ -16,30 +16,10 @@ export default class PostBodyAdditionalContent extends React.Component { this.getSlackAttachment = this.getSlackAttachment.bind(this); this.getOEmbedProvider = this.getOEmbedProvider.bind(this); - - this.state = { - link: Utils.extractLinks(props.post.message)[0] - }; } - componentWillReceiveProps(nextProps) { - if (this.props.post.message !== nextProps.post.message) { - this.setState({ - link: Utils.extractLinks(nextProps.post.message)[0] - }); - } - } - - shouldComponentUpdate(nextProps, nextState) { - if (nextState.link !== this.state.link) { - return true; - } - - if (nextProps.post.type !== this.props.post.type) { - return true; - } - - if (!Utils.areObjectsEqual(nextProps.post.props, this.props.post.props)) { + shouldComponentUpdate(nextProps) { + if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) { return true; } @@ -76,7 +56,7 @@ export default class PostBodyAdditionalContent extends React.Component { return this.getSlackAttachment(); } - const link = this.state.link; + const link = Utils.extractLinks(this.props.post.message)[0]; if (!link) { return null; } -- cgit v1.2.3-1-g7c22