summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDebanshu Kundu <debanshu.kundu@joshtechnologygroup.com>2017-03-02 23:17:27 +0530
committerenahum <nahumhbl@gmail.com>2017-03-02 14:47:27 -0300
commit47114a18e3d6dc2090beeb43d03f865d6436a99a (patch)
treeb7126d5cb3bd025eb62228af0cb0f7994964c6fa
parentd9a297d6291838e08a4acdbd603c35601c0e9878 (diff)
downloadchat-47114a18e3d6dc2090beeb43d03f865d6436a99a.tar.gz
chat-47114a18e3d6dc2090beeb43d03f865d6436a99a.tar.bz2
chat-47114a18e3d6dc2090beeb43d03f865d6436a99a.zip
PLT-5380 Moved link preview image to top right corner of preview area (#5212)
* PLT-5380 Moved link preview image to top right corner of preview area for smaller images, larger and wide images are still shown below the text. Also added logic to hide image area if image loading fails. * Updating link previews css
-rw-r--r--webapp/components/post_view/components/post_attachment_opengraph.jsx221
-rw-r--r--webapp/sass/layout/_post.scss2
-rw-r--r--webapp/sass/layout/_webhooks.scss69
-rw-r--r--webapp/sass/responsive/_mobile.scss45
-rw-r--r--webapp/sass/responsive/_tablet.scss13
-rw-r--r--webapp/tests/utils_get_nearest_point.test.jsx8
-rw-r--r--webapp/utils/commons.jsx18
7 files changed, 281 insertions, 95 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} &nbsp;
- {this.imageToggleAnchoreTag(imageUrl)}
+ <div>
+ {this.truncateText(data.description)} &nbsp;
+ {this.imageToggleAnchoreTag(imageUrl)}
+ </div>
+ {this.imageTag(imageUrl, true)}
</div>
- {this.imageTag(imageUrl)}
</div>
</div>
</div>
+ {this.imageTag(imageUrl, false)}
</div>
</div>
</div>
diff --git a/webapp/sass/layout/_post.scss b/webapp/sass/layout/_post.scss
index bca10cae2..ab391fa1d 100644
--- a/webapp/sass/layout/_post.scss
+++ b/webapp/sass/layout/_post.scss
@@ -1191,7 +1191,7 @@
cursor: pointer;
display: inline-block;
font: normal normal normal 14px/1 FontAwesome;
- margin: 0 0 10px;
+ margin: 0;
text-rendering: auto;
&.pull-left {
diff --git a/webapp/sass/layout/_webhooks.scss b/webapp/sass/layout/_webhooks.scss
index 904c50ccc..f3a8c6fd3 100644
--- a/webapp/sass/layout/_webhooks.scss
+++ b/webapp/sass/layout/_webhooks.scss
@@ -38,6 +38,9 @@
.post {
.attachment {
+ &.attachment--opengraph {
+ max-width: 800px;
+ }
.attachment__content {
border-radius: 4px;
border-style: solid;
@@ -68,11 +71,29 @@
&.attachment__container--danger {
border-left-color: #e40303;
}
+ &.attachment__container--opengraph {
+ display: table;
+ table-layout: fixed;
+ width: 100%;
+ margin: 0;
+ padding-bottom: 13px;
+ div {
+ margin: 0;
+ }
+ }
.sitename {
color: #A3A3A3;
}
}
+ .attachment__body__wrap {
+ &.attachment__body__wrap--opengraph {
+ display: table-cell;
+ width: 100%;
+ vertical-align: top;
+ }
+ }
+
.attachment__body {
float: left;
overflow-x: auto;
@@ -83,13 +104,11 @@
&.attachment__body--no_thumb {
width: 100%;
}
- .attachment__image {
- margin-bottom: 0;
- max-height: 150px;
- max-width: 150px;
- &.loading {
- height: 150px;
- }
+ &.attachment__body--opengraph {
+ float: none;
+ padding-right: 0;
+ width: 100%;
+ word-wrap: break-word;
}
}
@@ -97,10 +116,38 @@
display: inline-block;
}
+ .attachment__image__container--openraph {
+ display: table-cell;
+ vertical-align: top;
+ padding-top: 3px;
+ padding-left: 15px;
+ }
+
.attachment__image {
margin-bottom: 1em;
max-height: 300px;
max-width: 500px;
+
+ &.attachment__image--openraph {
+ margin-bottom: 0;
+ max-height: 80px;
+ max-width: 200px;
+
+ &.loading {
+ height: 80px;
+ }
+
+ &.large_image {
+ border-radius: 3px;
+ margin-top: 10px;
+ max-height: 200px;
+ max-width: 400px;
+
+ &.loading {
+ height: 150px;
+ }
+ }
+ }
}
.attachment__author-name {
@@ -121,6 +168,14 @@
overflow: hidden;
white-space: nowrap;
}
+
+ &.attachment__title--opengraph {
+ height: auto;
+ word-wrap: break-word;
+ &.is-url {
+ word-break: break-all
+ }
+ }
}
.attachment-link-more {
diff --git a/webapp/sass/responsive/_mobile.scss b/webapp/sass/responsive/_mobile.scss
index d1fc10428..3170fb0d4 100644
--- a/webapp/sass/responsive/_mobile.scss
+++ b/webapp/sass/responsive/_mobile.scss
@@ -1210,6 +1210,15 @@
}
}
}
+ .post {
+ .attachment {
+ .attachment__image {
+ &.attachment__image--openraph {
+ max-width: 200px;
+ }
+ }
+ }
+ }
}
@media screen and (max-width: 640px) {
@@ -1385,6 +1394,16 @@
text-align: left;
}
}
+
+ .post {
+ .attachment {
+ .attachment__image {
+ &.attachment__image--openraph {
+ max-width: 200px;
+ }
+ }
+ }
+ }
}
@media screen and (max-width: 550px) {
@@ -1415,6 +1434,15 @@
top: 60px;
width: calc(100% - 30px);
}
+ .post {
+ .attachment {
+ .attachment__image {
+ &.attachment__image--openraph {
+ max-width: 180px;
+ }
+ }
+ }
+ }
}
@media screen and (max-width: 480px) {
@@ -1521,6 +1549,16 @@
.integration__icon {
display: none;
}
+
+ .post {
+ .attachment {
+ .attachment__image {
+ &.attachment__image--openraph {
+ max-width: 120px;
+ }
+ }
+ }
+ }
}
@media screen and (max-height: 640px) {
@@ -1553,6 +1591,13 @@
}
}
}
+ .attachment {
+ .attachment__image {
+ &.attachment__image--openraph {
+ max-width: 80px;
+ }
+ }
+ }
}
.tutorial-steps__container {
diff --git a/webapp/sass/responsive/_tablet.scss b/webapp/sass/responsive/_tablet.scss
index 96a71694f..06a725a31 100644
--- a/webapp/sass/responsive/_tablet.scss
+++ b/webapp/sass/responsive/_tablet.scss
@@ -128,6 +128,19 @@
}
}
}
+ .post {
+ .attachment {
+ .attachment__image {
+ &.attachment__image--openraph {
+ max-height: 70px;
+ max-width: 300px;
+ &.loading {
+ height: 70px;
+ }
+ }
+ }
+ }
+ }
}
// Tablet and desktop
diff --git a/webapp/tests/utils_get_nearest_point.test.jsx b/webapp/tests/utils_get_nearest_point.test.jsx
index b0b0a2e0e..02ca29cc3 100644
--- a/webapp/tests/utils_get_nearest_point.test.jsx
+++ b/webapp/tests/utils_get_nearest_point.test.jsx
@@ -24,12 +24,10 @@ describe('CommonUtils.getNearestPoint', function() {
nearestPointLte: {x: 1, y: 1}
}
]) {
- const nearestPointData = CommonUtils.getNearestPoint(data.pivotPoint, data.points);
+ const nearestPoint = CommonUtils.getNearestPoint(data.pivotPoint, data.points);
- assert.equal(nearestPointData.nearestPoint.x, data.nearestPoint.x);
- assert.equal(nearestPointData.nearestPoint.y, data.nearestPoint.y);
- assert.equal(nearestPointData.nearestPointLte.x, data.nearestPointLte.x);
- assert.equal(nearestPointData.nearestPointLte.y, data.nearestPointLte.y);
+ assert.equal(nearestPoint.x, data.nearestPoint.x);
+ assert.equal(nearestPoint.y, data.nearestPoint.y);
}
});
});
diff --git a/webapp/utils/commons.jsx b/webapp/utils/commons.jsx
index 1888869dc..224653df7 100644
--- a/webapp/utils/commons.jsx
+++ b/webapp/utils/commons.jsx
@@ -8,7 +8,6 @@ export function getDistanceBW2Points(point1, point2, xAttr = 'x', yAttr = 'y') {
*/
export function getNearestPoint(pivotPoint, points, xAttr = 'x', yAttr = 'y') {
var nearestPoint = {};
- var nearestPointLte = {}; // Nearest point smaller than or equal to point
for (const point of points) {
if (typeof nearestPoint[xAttr] === 'undefined' || typeof nearestPoint[yAttr] === 'undefined') {
nearestPoint = point;
@@ -16,21 +15,6 @@ export function getNearestPoint(pivotPoint, points, xAttr = 'x', yAttr = 'y') {
// Check for bestImage
nearestPoint = point;
}
-
- if (typeof nearestPointLte[xAttr] === 'undefined' || typeof nearestPointLte[yAttr] === 'undefined') {
- if (point[xAttr] <= pivotPoint[xAttr] && point[yAttr] <= pivotPoint[yAttr]) {
- nearestPointLte = point;
- }
- } else if (
- // Check for bestImageLte
- getDistanceBW2Points(point, pivotPoint, xAttr, yAttr) < getDistanceBW2Points(nearestPointLte, pivotPoint, xAttr, yAttr) &&
- point[xAttr] <= pivotPoint[xAttr] && point[yAttr] <= pivotPoint[yAttr]
- ) {
- nearestPointLte = point;
- }
}
- return {
- nearestPoint,
- nearestPointLte
- };
+ return nearestPoint;
}