Skip to content

Commit

Permalink
Merge pull request #20892 from Expensify/kadie-thumbnail-component-ch…
Browse files Browse the repository at this point in the history
…ange

Migrate ThumbnailImage.js to function component
  • Loading branch information
puneetlath authored Aug 9, 2023
2 parents 5fd39b9 + 2ea290c commit 58dc0c9
Showing 1 changed file with 54 additions and 66 deletions.
120 changes: 54 additions & 66 deletions src/components/ThumbnailImage.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import lodashClamp from 'lodash/clamp';
import React, {PureComponent} from 'react';
import {View, Dimensions} from 'react-native';
import React, {useCallback, useState} from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import ImageWithSizeCalculation from './ImageWithSizeCalculation';
import styles from '../styles/styles';
import * as StyleUtils from '../styles/StyleUtils';
import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions';
import * as DeviceCapabilities from '../libs/DeviceCapabilities';
import useWindowDimensions from '../hooks/useWindowDimensions';

const propTypes = {
/** Source URL for the preview image */
Expand All @@ -24,8 +23,6 @@ const propTypes = {

/** Height of the thumbnail image */
imageHeight: PropTypes.number,

...windowDimensionsPropTypes,
};

const defaultProps = {
Expand All @@ -34,77 +31,68 @@ const defaultProps = {
imageHeight: 200,
};

class ThumbnailImage extends PureComponent {
constructor(props) {
super(props);

this.updateImageSize = this.updateImageSize.bind(this);
const {thumbnailWidth, thumbnailHeight} = this.calculateThumbnailImageSize(props.imageWidth, props.imageHeight);
this.state = {
thumbnailWidth,
thumbnailHeight,
};
}
/**
* Compute the thumbnails width and height given original image dimensions.
*
* @param {Number} width - Width of the original image.
* @param {Number} height - Height of the original image.
* @param {Number} windowHeight - Height of the device/browser window.
* @returns {Object} - Object containing thumbnails width and height.
*/

/**
* Compute the thumbnails width and height given original image dimensions.
*
* @param {Number} width - Width of the original image.
* @param {Number} height - Height of the original image.
* @returns {Object} - Object containing thumbnails width and height.
*/
calculateThumbnailImageSize(width, height) {
if (!width || !height) {
return {};
}
function calculateThumbnailImageSize(width, height, windowHeight) {
// Width of the thumbnail works better as a constant than it does
// a percentage of the screen width since it is relative to each screen
// Note: Clamp minimum width 40px to support touch device
let thumbnailScreenWidth = lodashClamp(width, 40, 250);
const imageHeight = height / (width / thumbnailScreenWidth);
let thumbnailScreenHeight = lodashClamp(imageHeight, 40, windowHeight * 0.4);
const aspectRatio = height / width;

// Width of the thumbnail works better as a constant than it does
// a percentage of the screen width since it is relative to each screen
// Note: Clamp minimum width 40px to support touch device
let thumbnailScreenWidth = lodashClamp(width, 40, 250);
const imageHeight = height / (width / thumbnailScreenWidth);
// On mWeb, when soft keyboard opens, window height changes, making thumbnail height inconsistent. We use screen height instead.
const screenHeight = DeviceCapabilities.canUseTouchScreen() ? Dimensions.get('screen').height : this.props.windowHeight;
let thumbnailScreenHeight = lodashClamp(imageHeight, 40, screenHeight * 0.4);
const aspectRatio = height / width;

// If thumbnail height is greater than its width, then the image is portrait otherwise landscape.
// For portrait images, we need to adjust the width of the image to keep the aspect ratio and vice-versa.
if (thumbnailScreenHeight > thumbnailScreenWidth) {
thumbnailScreenWidth = Math.round(thumbnailScreenHeight * (1 / aspectRatio));
} else {
thumbnailScreenHeight = Math.round(thumbnailScreenWidth * aspectRatio);
}
return {thumbnailWidth: Math.max(40, thumbnailScreenWidth), thumbnailHeight: Math.max(40, thumbnailScreenHeight)};
// If thumbnail height is greater than its width, then the image is portrait otherwise landscape.
// For portrait images, we need to adjust the width of the image to keep the aspect ratio and vice-versa.
if (thumbnailScreenHeight > thumbnailScreenWidth) {
thumbnailScreenWidth = Math.round(thumbnailScreenHeight * (1 / aspectRatio));
} else {
thumbnailScreenHeight = Math.round(thumbnailScreenWidth * aspectRatio);
}
return {thumbnailWidth: Math.max(40, thumbnailScreenWidth), thumbnailHeight: Math.max(40, thumbnailScreenHeight)};
}

function ThumbnailImage(props) {
const {windowHeight} = useWindowDimensions();
const initialDimensions = calculateThumbnailImageSize(props.imageWidth, props.imageHeight, windowHeight);
const [imageWidth, setImageWidth] = useState(initialDimensions.thumbnailWidth);
const [imageHeight, setImageHeight] = useState(initialDimensions.thumbnailHeight);

/**
* Update the state with the computed thumbnail sizes.
*
* @param {Object} Params - width and height of the original image.
* @param {Number} Params.width
* @param {Number} Params.height
* @param {{ width: number, height: number }} Params - width and height of the original image.
*/
updateImageSize({width, height}) {
const {thumbnailWidth, thumbnailHeight} = this.calculateThumbnailImageSize(width, height);
this.setState({thumbnailWidth, thumbnailHeight});
}

render() {
return (
<View style={[this.props.style, styles.overflowHidden]}>
<View style={[StyleUtils.getWidthAndHeightStyle(this.state.thumbnailWidth, this.state.thumbnailHeight), styles.alignItemsCenter, styles.justifyContentCenter]}>
<ImageWithSizeCalculation
url={this.props.previewSourceURL}
onMeasure={this.updateImageSize}
isAuthTokenRequired={this.props.isAuthTokenRequired}
/>
</View>
const updateImageSize = useCallback(
({width, height}) => {
const {thumbnailWidth, thumbnailHeight} = calculateThumbnailImageSize(width, height, windowHeight);
setImageWidth(thumbnailWidth);
setImageHeight(thumbnailHeight);
},
[windowHeight],
);
return (
<View style={[props.style, styles.overflowHidden]}>
<View style={[StyleUtils.getWidthAndHeightStyle(imageWidth, imageHeight), styles.alignItemsCenter, styles.justifyContentCenter]}>
<ImageWithSizeCalculation
url={props.previewSourceURL}
onMeasure={updateImageSize}
isAuthTokenRequired={props.isAuthTokenRequired}
/>
</View>
);
}
</View>
);
}

ThumbnailImage.propTypes = propTypes;
ThumbnailImage.defaultProps = defaultProps;
export default withWindowDimensions(ThumbnailImage);
ThumbnailImage.displayName = 'ThumbnailImage';
export default React.memo(ThumbnailImage);

0 comments on commit 58dc0c9

Please sign in to comment.