diff options
Diffstat (limited to 'webapp/components')
-rw-r--r-- | webapp/components/post_view/components/post_attachment_opengraph.jsx | 221 |
1 files changed, 156 insertions, 65 deletions
diff --git a/webapp/components/post_view/components/post_attachment_opengraph.jsx b/webapp/components/post_view/components/post_attachment_opengraph.jsx index b83150839..12437e672 100644 --- a/webapp/components/post_view/components/post_attachment_opengraph.jsx +++ b/webapp/components/post_view/components/post_attachment_opengraph.jsx @@ -11,29 +11,47 @@ import {requestOpenGraphMetadata} from 'actions/global_actions.jsx'; export default class PostAttachmentOpenGraph extends React.Component { constructor(props) { super(props); + this.largeImageMinWidth = 150; this.imageDimentions = { // Image dimentions in pixels. - height: 150, - width: 150 + height: 80, + width: 80 }; - this.maxDescriptionLength = 300; - this.descriptionEllipsis = '...'; + this.textMaxLenght = 300; + this.textEllipsis = '...'; + this.largeImageMinRatio = 16 / 9; + this.smallImageContainerLeftPadding = 15; + + this.imageRatio = null; + + this.smallImageContainer = null; + this.smallImageElement = null; + this.fetchData = this.fetchData.bind(this); this.onOpenGraphMetadataChange = this.onOpenGraphMetadataChange.bind(this); this.toggleImageVisibility = this.toggleImageVisibility.bind(this); this.onImageLoad = this.onImageLoad.bind(this); + this.onImageError = this.onImageError.bind(this); + this.truncateText = this.truncateText.bind(this); + this.setImageWidth = this.setImageWidth.bind(this); + } + + IMAGE_LOADED = { + LOADING: 'loading', + YES: 'yes', + ERROR: 'error' } componentWillMount() { this.setState({ data: {}, - imageLoaded: false, - imageVisible: this.props.previewCollapsed.startsWith('false') + imageLoaded: this.IMAGE_LOADED.LOADING, + imageVisible: this.props.previewCollapsed.startsWith('false'), + hasLargeImage: false }); this.fetchData(this.props.link); } componentWillReceiveProps(nextProps) { - this.setState({imageVisible: nextProps.previewCollapsed.startsWith('false')}); if (!Utils.areObjectsEqual(nextProps.link, this.props.link)) { this.fetchData(nextProps.link); } @@ -43,6 +61,9 @@ export default class PostAttachmentOpenGraph extends React.Component { if (nextState.imageVisible !== this.state.imageVisible) { return true; } + if (nextState.hasLargeImage !== this.state.hasLargeImage) { + return true; + } if (nextState.imageLoaded !== this.state.imageLoaded) { return true; } @@ -54,16 +75,20 @@ export default class PostAttachmentOpenGraph extends React.Component { componentDidMount() { OpenGraphStore.addUrlDataChangeListener(this.onOpenGraphMetadataChange); + this.setImageWidth(); + window.addEventListener('resize', this.setImageWidth); } componentDidUpdate() { if (this.props.childComponentDidUpdateFunction) { this.props.childComponentDidUpdateFunction(); } + this.setImageWidth(); } componentWillUnmount() { OpenGraphStore.removeUrlDataChangeListener(this.onOpenGraphMetadataChange); + window.removeEventListener('resize', this.setImageWidth); } onOpenGraphMetadataChange(url) { @@ -74,53 +99,54 @@ export default class PostAttachmentOpenGraph extends React.Component { fetchData(url) { const data = OpenGraphStore.getOgInfo(url); - this.setState({data, imageLoaded: false}); + this.setState({data, imageLoaded: this.IMAGE_LOADED.LOADING}); if (Utils.isEmptyObject(data)) { requestOpenGraphMetadata(url); } } getBestImageUrl() { - if (this.state.data.images == null) { + if (Utils.isEmptyObject(this.state.data.images)) { return null; } - const nearestPointData = CommonUtils.getNearestPoint(this.imageDimentions, this.state.data.images, 'width', 'height'); - - const bestImage = nearestPointData.nearestPoint; - const bestImageLte = nearestPointData.nearestPointLte; // Best image <= 150px height and width + const bestImage = CommonUtils.getNearestPoint(this.imageDimentions, this.state.data.images, 'width', 'height'); + return bestImage.secure_url || bestImage.url; + } - let finalBestImage; + toggleImageVisibility() { + this.setState({imageVisible: !this.state.imageVisible}); + } + onImageLoad(image) { + this.imageRatio = image.target.naturalWidth / image.target.naturalHeight; if ( - !Utils.isEmptyObject(bestImageLte) && - bestImageLte.height <= this.imageDimentions.height && - bestImageLte.width <= this.imageDimentions.width + image.target.naturalWidth >= this.largeImageMinWidth && + this.imageRatio >= this.largeImageMinRatio && + !this.state.hasLargeImage ) { - finalBestImage = bestImageLte; - } else { - finalBestImage = bestImage; + this.setState({ + hasLargeImage: true + }); } - - return finalBestImage.secure_url || finalBestImage.url; - } - - toggleImageVisibility() { - this.setState({imageVisible: !this.state.imageVisible}); + this.setState({ + imageLoaded: this.IMAGE_LOADED.YES + }); } - onImageLoad() { - this.setState({imageLoaded: true}); + onImageError() { + this.setState({imageLoaded: this.IMAGE_LOADED.ERROR}); } loadImage(src) { const img = new Image(); img.onload = this.onImageLoad; + img.onerror = this.onImageError; img.src = src; } imageToggleAnchoreTag(imageUrl) { - if (imageUrl) { + if (imageUrl && this.state.hasLargeImage) { return ( <a className={'post__embed-visibility'} @@ -133,16 +159,81 @@ export default class PostAttachmentOpenGraph extends React.Component { return null; } - imageTag(imageUrl) { - if (imageUrl && this.state.imageVisible) { - return ( - <img - className={this.state.imageLoaded ? 'attachment__image' : 'attachment__image loading'} - src={this.state.imageLoaded ? imageUrl : null} - /> + wrapInSmallImageContainer(imageElement) { + return ( + <div + className='attachment__image__container--openraph' + style={{ + width: (this.imageDimentions.height * this.imageRatio) + this.smallImageContainerLeftPadding + }} // Initially set the width accordinly to max image heigh, ie 80px. Later on it would be modified according to actul height of image. + ref={(div) => { + this.smallImageContainer = div; + }} + > + {imageElement} + </div> + ); + } + + imageTag(imageUrl, renderingForLargeImage = false) { + var element = null; + if ( + imageUrl && renderingForLargeImage === this.state.hasLargeImage && + (!renderingForLargeImage || (renderingForLargeImage && this.state.imageVisible)) + ) { + if (this.state.imageLoaded === this.IMAGE_LOADED.LOADING) { + if (renderingForLargeImage) { + element = <img className={'attachment__image attachment__image--openraph loading large_image'}/>; + } else { + element = this.wrapInSmallImageContainer( + <img className={'attachment__image attachment__image--openraph loading '}/> + ); + } + } else if (this.state.imageLoaded === this.IMAGE_LOADED.YES) { + if (renderingForLargeImage) { + element = ( + <img + className={'attachment__image attachment__image--openraph large_image'} + src={imageUrl} + /> + ); + } else { + element = this.wrapInSmallImageContainer( + <img + className={'attachment__image attachment__image--openraph'} + src={imageUrl} + ref={(img) => { + this.smallImageElement = img; + }} + /> + ); + } + } else if (this.state.imageLoaded === this.IMAGE_LOADED.ERROR) { + return null; + } + } + return element; + } + + setImageWidth() { + if ( + this.state.imageLoaded === this.IMAGE_LOADED.YES && + this.smallImageContainer && + this.smallImageElement + ) { + this.smallImageContainer.style.width = ( + (this.smallImageElement.offsetHeight * this.imageRatio) + + this.smallImageContainerLeftPadding + + 'px' ); } - return null; + } + + truncateText(text, maxLength = this.textMaxLenght, ellipsis = this.textEllipsis) { + if (text.length > maxLength) { + return text.substring(0, maxLength - ellipsis.length) + ellipsis; + } + return text; } render() { @@ -152,52 +243,52 @@ export default class PostAttachmentOpenGraph extends React.Component { const data = this.state.data; const imageUrl = this.getBestImageUrl(); - var description = data.description; - - if (description.length > this.maxDescriptionLength) { - description = description.substring(0, this.maxDescriptionLength - this.descriptionEllipsis.length) + this.descriptionEllipsis; - } - if (imageUrl && this.state.imageVisible) { + if (imageUrl) { this.loadImage(imageUrl); } return ( <div - className='attachment attachment--oembed' + className='attachment attachment--opengraph' ref='attachment' > <div className='attachment__content'> <div - className={'clearfix attachment__container'} + className={'clearfix attachment__container attachment__container--opengraph'} > - <span className='sitename'>{data.site_name}</span> - <h1 - className='attachment__title has-link' + <div + className={'attachment__body__wrap attachment__body__wrap--opengraph'} > - <a - className='attachment__title-link' - href={data.url || this.props.link} - target='_blank' - rel='noopener noreferrer' - title={data.title || data.url || this.props.link} - > - {data.title || data.url || this.props.link} - </a> - </h1> - <div > - <div - className={'attachment__body attachment__body--no_thumb'} + <span className='sitename'>{this.truncateText(data.site_name)}</span> + <h1 + className={'attachment__title attachment__title--opengraph' + (data.title ? '' : ' is-url')} > - <div> + <a + className='attachment__title-link attachment__title-link--opengraph' + href={data.url || this.props.link} + target='_blank' + rel='noopener noreferrer' + title={data.title || data.url || this.props.link} + > + {this.truncateText(data.title || data.url || this.props.link)} + </a> + </h1> + <div > + <div + className={'attachment__body attachment__body--opengraph'} + > <div> - {description} - {this.imageToggleAnchoreTag(imageUrl)} + <div> + {this.truncateText(data.description)} + {this.imageToggleAnchoreTag(imageUrl)} + </div> + {this.imageTag(imageUrl, true)} </div> - {this.imageTag(imageUrl)} </div> </div> </div> + {this.imageTag(imageUrl, false)} </div> </div> </div> |