From 52334a3ad4915b8bfcf179c1d9824ad85ff348a8 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 18 Aug 2023 08:37:24 +0200 Subject: [PATCH 01/14] add new functional component --- src/components/ImageView/index-old.native.js | 255 ++++++++++++++ src/components/ImageView/index.native.js | 344 +++++++++---------- 2 files changed, 420 insertions(+), 179 deletions(-) create mode 100644 src/components/ImageView/index-old.native.js diff --git a/src/components/ImageView/index-old.native.js b/src/components/ImageView/index-old.native.js new file mode 100644 index 000000000000..e1dcaffde9f8 --- /dev/null +++ b/src/components/ImageView/index-old.native.js @@ -0,0 +1,255 @@ +import React, {PureComponent} from 'react'; +import PropTypes from 'prop-types'; +import {View, PanResponder} from 'react-native'; +import ImageZoom from 'react-native-image-pan-zoom'; +import _ from 'underscore'; +import styles from '../../styles/styles'; +import variables from '../../styles/variables'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; +import FullscreenLoadingIndicator from '../FullscreenLoadingIndicator'; +import Image from '../Image'; + +/** + * On the native layer, we use a image library to handle zoom functionality + */ +const propTypes = { + /** Whether source url requires authentication */ + isAuthTokenRequired: PropTypes.bool, + + /** URL to full-sized image */ + url: PropTypes.string.isRequired, + + /** Handles scale changed event in image zoom component. Used on native only */ + onScaleChanged: PropTypes.func.isRequired, + + /** Function for handle on press */ + onPress: PropTypes.func, + + ...windowDimensionsPropTypes, +}; + +const defaultProps = { + isAuthTokenRequired: false, + onPress: () => {}, +}; + +class ImageView extends PureComponent { + constructor(props) { + super(props); + + this.state = { + isLoading: true, + imageWidth: 0, + imageHeight: 0, + interactionPromise: undefined, + }; + + // Use the default double click interval from the ImageZoom library + // https://github.com/ascoders/react-native-image-zoom/blob/master/src/image-zoom/image-zoom.type.ts#L79 + this.doubleClickInterval = 175; + this.imageZoomScale = 1; + this.lastClickTime = 0; + this.amountOfTouches = 0; + + // PanResponder used to capture how many touches are active on the attachment image + this.panResponder = PanResponder.create({ + onStartShouldSetPanResponder: this.updatePanResponderTouches.bind(this), + }); + + this.configureImageZoom = this.configureImageZoom.bind(this); + this.imageLoadingStart = this.imageLoadingStart.bind(this); + } + + componentDidMount() { + console.log('I RENDER') + } + + componentDidUpdate(prevProps) { + if (this.props.url === prevProps.url) { + return; + } + + this.imageLoadingStart(); + + if (this.interactionPromise) { + this.state.interactionPromise.cancel(); + } + } + + componentWillUnmount() { + if (!this.state.interactionPromise) { + return; + } + this.state.interactionPromise.cancel(); + } + + /** + * Updates the amount of active touches on the PanResponder on our ImageZoom overlay View + * + * @param {Event} e + * @param {GestureState} gestureState + * @returns {Boolean} + */ + updatePanResponderTouches(e, gestureState) { + if (_.isNumber(gestureState.numberActiveTouches)) { + this.amountOfTouches = gestureState.numberActiveTouches; + } + + // We don't need to set the panResponder since all we care about is checking the gestureState, so return false + return false; + } + + /** + * The `ImageZoom` component requires image dimensions which + * are calculated here from the natural image dimensions produced by + * the `onLoad` event + * + * @param {Object} nativeEvent + */ + configureImageZoom({nativeEvent}) { + console.log('CONFIGURE IMAGE ZOOM') + let imageWidth = nativeEvent.width; + let imageHeight = nativeEvent.height; + const containerWidth = Math.round(this.props.windowWidth); + const containerHeight = Math.round(this.state.containerHeight ? this.state.containerHeight : this.props.windowHeight); + + const aspectRatio = Math.min(containerHeight / imageHeight, containerWidth / imageWidth); + + imageHeight *= aspectRatio; + imageWidth *= aspectRatio; + + // Resize the image to max dimensions possible on the Native platforms to prevent crashes on Android. To keep the same behavior, apply to IOS as well. + const maxDimensionsScale = 11; + imageWidth = Math.min(imageWidth, containerWidth * maxDimensionsScale); + imageHeight = Math.min(imageHeight, containerHeight * maxDimensionsScale); + this.setState({imageHeight, imageWidth, isLoading: false}); + } + + /** + * When the url changes and the image must load again, + * this resets the zoom to ensure the next image loads with the correct dimensions. + */ + resetImageZoom() { + if (this.imageZoomScale !== 1) { + this.imageZoomScale = 1; + } + + if (this.zoom) { + this.zoom.centerOn({ + x: 0, + y: 0, + scale: 1, + duration: 0, + }); + } + } + + imageLoadingStart() { + if (this.state.isLoading) { + return; + } + console.log('IMAGE LOADING START') + this.resetImageZoom(); + this.setState({imageHeight: 0, imageWidth: 0, isLoading: true}); + } + + render() { + // Default windowHeight accounts for the modal header height + const windowHeight = this.props.windowHeight - variables.contentHeaderHeight; + const hasImageDimensions = this.state.imageWidth !== 0 && this.state.imageHeight !== 0; + const shouldShowLoadingIndicator = this.state.isLoading || !hasImageDimensions; + + // Zoom view should be loaded only after measuring actual image dimensions, otherwise it causes blurred images on Android + return ( + { + const layout = event.nativeEvent.layout; + this.setState({ + containerHeight: layout.height, + }); + }} + > + {Boolean(this.state.containerHeight) && ( + (this.zoom = el)} + onClick={() => this.props.onPress()} + cropWidth={this.props.windowWidth} + cropHeight={windowHeight} + imageWidth={this.state.imageWidth} + imageHeight={this.state.imageHeight} + onStartShouldSetPanResponder={() => { + const isDoubleClick = new Date().getTime() - this.lastClickTime <= this.doubleClickInterval; + this.lastClickTime = new Date().getTime(); + + // Let ImageZoom handle the event if the tap is more than one touchPoint or if we are zoomed in + if (this.amountOfTouches === 2 || this.imageZoomScale !== 1) { + return true; + } + + // When we have a double click and the zoom scale is 1 then programmatically zoom the image + // but let the tap fall through to the parent so we can register a swipe down to dismiss + if (isDoubleClick) { + this.zoom.centerOn({ + x: 0, + y: 0, + scale: 2, + duration: 100, + }); + + // onMove will be called after the zoom animation. + // So it's possible to zoom and swipe and stuck in between the images. + // Sending scale just when we actually trigger the animation makes this nearly impossible. + // you should be really fast to catch in between state updates. + // And this lucky case will be fixed by migration to UI thread only code + // with gesture handler and reanimated. + this.props.onScaleChanged(2); + } + + // We must be either swiping down or double tapping since we are at zoom scale 1 + return false; + }} + onMove={({scale}) => { + this.props.onScaleChanged(scale); + this.imageZoomScale = scale; + }} + > + + {/** + Create an invisible view on top of the image so we can capture and set the amount of touches before + the ImageZoom's PanResponder does. Children will be triggered first, so this needs to be inside the + ImageZoom to work + */} + + + )} + {shouldShowLoadingIndicator && } + + ); + } +} + +ImageView.propTypes = propTypes; +ImageView.defaultProps = defaultProps; + +export default withWindowDimensions(ImageView); diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index bcfcaf78d5c6..653d98e22cc3 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -1,13 +1,13 @@ -import React, {PureComponent} from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import {View, PanResponder} from 'react-native'; import ImageZoom from 'react-native-image-pan-zoom'; import _ from 'underscore'; import styles from '../../styles/styles'; import variables from '../../styles/variables'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; import FullscreenLoadingIndicator from '../FullscreenLoadingIndicator'; import Image from '../Image'; +import useWindowDimensions from '../../hooks/useWindowDimensions'; /** * On the native layer, we use a image library to handle zoom functionality @@ -25,59 +25,30 @@ const propTypes = { /** Function for handle on press */ onPress: PropTypes.func, - ...windowDimensionsPropTypes, + /** Additional styles to add to the component */ + style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), }; const defaultProps = { isAuthTokenRequired: false, onPress: () => {}, + style: {}, }; -class ImageView extends PureComponent { - constructor(props) { - super(props); - - this.state = { - isLoading: true, - imageWidth: 0, - imageHeight: 0, - interactionPromise: undefined, - }; - - // Use the default double click interval from the ImageZoom library - // https://github.com/ascoders/react-native-image-zoom/blob/master/src/image-zoom/image-zoom.type.ts#L79 - this.doubleClickInterval = 175; - this.imageZoomScale = 1; - this.lastClickTime = 0; - this.amountOfTouches = 0; - - // PanResponder used to capture how many touches are active on the attachment image - this.panResponder = PanResponder.create({ - onStartShouldSetPanResponder: this.updatePanResponderTouches.bind(this), - }); - - this.configureImageZoom = this.configureImageZoom.bind(this); - this.imageLoadingStart = this.imageLoadingStart.bind(this); - } - - componentDidUpdate(prevProps) { - if (this.props.url === prevProps.url) { - return; - } +const DOUBLE_CLICK_INTERVAL = 175; - this.imageLoadingStart(); +function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { + const {windowWidth, windowHeight} = useWindowDimensions(); - if (this.interactionPromise) { - this.state.interactionPromise.cancel(); - } - } + const [isLoading, setIsLoading] = React.useState(true); + const [imageWidth, setImageWidth] = React.useState(0); + const [imageHeight, setImageHeight] = React.useState(0); + const [containerHeight, setContainerHeight] = React.useState(); - componentWillUnmount() { - if (!this.state.interactionPromise) { - return; - } - this.state.interactionPromise.cancel(); - } + const imageZoomScale = React.useRef(1) + const lastClickTime = React.useRef(0) + const numberOfTouches = React.useRef(0) + const zoom = React.useRef(); /** * Updates the amount of active touches on the PanResponder on our ImageZoom overlay View @@ -86,164 +57,179 @@ class ImageView extends PureComponent { * @param {GestureState} gestureState * @returns {Boolean} */ - updatePanResponderTouches(e, gestureState) { + const updatePanResponderTouches = (e, gestureState) => { if (_.isNumber(gestureState.numberActiveTouches)) { - this.amountOfTouches = gestureState.numberActiveTouches; + numberOfTouches.current = gestureState.numberActiveTouches } // We don't need to set the panResponder since all we care about is checking the gestureState, so return false return false; - } - - /** - * The `ImageZoom` component requires image dimensions which - * are calculated here from the natural image dimensions produced by - * the `onLoad` event - * - * @param {Object} nativeEvent - */ - configureImageZoom({nativeEvent}) { - let imageWidth = nativeEvent.width; - let imageHeight = nativeEvent.height; - const containerWidth = Math.round(this.props.windowWidth); - const containerHeight = Math.round(this.state.containerHeight ? this.state.containerHeight : this.props.windowHeight); - - const aspectRatio = Math.min(containerHeight / imageHeight, containerWidth / imageWidth); + }; - imageHeight *= aspectRatio; - imageWidth *= aspectRatio; - - // Resize the image to max dimensions possible on the Native platforms to prevent crashes on Android. To keep the same behavior, apply to IOS as well. - const maxDimensionsScale = 11; - imageWidth = Math.min(imageWidth, containerWidth * maxDimensionsScale); - imageHeight = Math.min(imageHeight, containerHeight * maxDimensionsScale); - this.setState({imageHeight, imageWidth, isLoading: false}); - } + // PanResponder used to capture how many touches are active on the attachment image + const panResponder = React.useRef( + PanResponder.create({ + onStartShouldSetPanResponder: updatePanResponderTouches, + }), + ).current; /** * When the url changes and the image must load again, * this resets the zoom to ensure the next image loads with the correct dimensions. */ - resetImageZoom() { - if (this.imageZoomScale !== 1) { - this.imageZoomScale = 1; + const resetImageZoom = React.useCallback(() => { + if (imageZoomScale.current !== 1) { + imageZoomScale.current = 1; } - if (this.zoom) { - this.zoom.centerOn({ + if (zoom.current) { + zoom.current.centerOn({ x: 0, y: 0, scale: 1, duration: 0, }); } - } + }, []); - imageLoadingStart() { - if (this.state.isLoading) { + const imageLoadingStart = React.useCallback(() => { + if (isLoading) { return; } - this.resetImageZoom(); - this.setState({imageHeight: 0, imageWidth: 0, isLoading: true}); - } - - render() { - // Default windowHeight accounts for the modal header height - const windowHeight = this.props.windowHeight - variables.contentHeaderHeight; - const hasImageDimensions = this.state.imageWidth !== 0 && this.state.imageHeight !== 0; - const shouldShowLoadingIndicator = this.state.isLoading || !hasImageDimensions; - - // Zoom view should be loaded only after measuring actual image dimensions, otherwise it causes blurred images on Android - return ( - { - const layout = event.nativeEvent.layout; - this.setState({ - containerHeight: layout.height, - }); - }} - > - {Boolean(this.state.containerHeight) && ( - (this.zoom = el)} - onClick={() => this.props.onPress()} - cropWidth={this.props.windowWidth} - cropHeight={windowHeight} - imageWidth={this.state.imageWidth} - imageHeight={this.state.imageHeight} - onStartShouldSetPanResponder={() => { - const isDoubleClick = new Date().getTime() - this.lastClickTime <= this.doubleClickInterval; - this.lastClickTime = new Date().getTime(); - - // Let ImageZoom handle the event if the tap is more than one touchPoint or if we are zoomed in - if (this.amountOfTouches === 2 || this.imageZoomScale !== 1) { - return true; - } - - // When we have a double click and the zoom scale is 1 then programmatically zoom the image - // but let the tap fall through to the parent so we can register a swipe down to dismiss - if (isDoubleClick) { - this.zoom.centerOn({ - x: 0, - y: 0, - scale: 2, - duration: 100, - }); - - // onMove will be called after the zoom animation. - // So it's possible to zoom and swipe and stuck in between the images. - // Sending scale just when we actually trigger the animation makes this nearly impossible. - // you should be really fast to catch in between state updates. - // And this lucky case will be fixed by migration to UI thread only code - // with gesture handler and reanimated. - this.props.onScaleChanged(2); - } - - // We must be either swiping down or double tapping since we are at zoom scale 1 - return false; - }} - onMove={({scale}) => { - this.props.onScaleChanged(scale); - this.imageZoomScale = scale; - }} - > - - {/** - Create an invisible view on top of the image so we can capture and set the amount of touches before - the ImageZoom's PanResponder does. Children will be triggered first, so this needs to be inside the - ImageZoom to work - */} - - - )} - {shouldShowLoadingIndicator && } - - ); - } + resetImageZoom(); + + setImageHeight(0); + setImageWidth(0); + setIsLoading(true); + }, [isLoading, resetImageZoom]); + + // React.useEffect(() => { + // imageLoadingStart(); + // }, [imageLoadingStart, url]); + + /** + * The `ImageZoom` component requires image dimensions which + * are calculated here from the natural image dimensions produced by + * the `onLoad` event + * + * @param {Object} nativeEvent + */ + const configureImageZoom = ({nativeEvent}) => { + let imgWidth = nativeEvent.width; + let imgHeight = nativeEvent.height; + const roundedContainerWidth = Math.round(windowWidth); + const roundedContainerHeight = Math.round(containerHeight || windowHeight); + + const aspectRatio = Math.min(roundedContainerHeight / imgHeight, roundedContainerWidth / imgWidth); + + imgHeight *= aspectRatio; + imgWidth *= aspectRatio; + + // Resize the image to max dimensions possible on the Native platforms to prevent crashes on Android. To keep the same behavior, apply to IOS as well. + const maxDimensionsScale = 11; + imgWidth = Math.min(imgWidth, roundedContainerWidth * maxDimensionsScale); + imgHeight = Math.min(imgHeight, roundedContainerHeight * maxDimensionsScale); + + setImageHeight(imgHeight); + setImageWidth(imgWidth); + setIsLoading(false); + }; + + // Default windowHeight accounts for the modal header height + const calculatedWindowHeight = windowHeight - variables.contentHeaderHeight; + const hasImageDimensions = imageWidth !== 0 && imageHeight !== 0; + const shouldShowLoadingIndicator = isLoading || !hasImageDimensions; + + // Zoom view should be loaded only after measuring actual image dimensions, otherwise it causes blurred images on Android + return ( + { + const layout = event.nativeEvent.layout; + setContainerHeight(layout.height); + }} + > + {Boolean(containerHeight) && ( + (this.zoom = el)} + ref={zoom} + onClick={onPress} + cropWidth={windowWidth} + cropHeight={calculatedWindowHeight} + imageWidth={imageWidth} + imageHeight={imageHeight} + onStartShouldSetPanResponder={() => { + const isDoubleClick = new Date().getTime() - lastClickTime.current <= DOUBLE_CLICK_INTERVAL; + lastClickTime.current = new Date().getTime() + + // Let ImageZoom handle the event if the tap is more than one touchPoint or if we are zoomed in + if (numberOfTouches.current === 2 || imageZoomScale.current !== 1) { + return true; + } + + // When we have a double click and the zoom scale is 1 then programmatically zoom the image + // but let the tap fall through to the parent so we can register a swipe down to dismiss + if (isDoubleClick) { + zoom.current.centerOn({ + x: 0, + y: 0, + scale: 2, + duration: 100, + }); + + // onMove will be called after the zoom animation. + // So it's possible to zoom and swipe and stuck in between the images. + // Sending scale just when we actually trigger the animation makes this nearly impossible. + // you should be really fast to catch in between state updates. + // And this lucky case will be fixed by migration to UI thread only code + // with gesture handler and reanimated. + onScaleChanged(2); + } + + // We must be either swiping down or double tapping since we are at zoom scale 1 + return false; + }} + onMove={({scale}) => { + onScaleChanged(scale); + imageZoomScale.current = scale + }} + > + + {/** + Create an invisible view on top of the image so we can capture and set the amount of touches before + the ImageZoom's PanResponder does. Children will be triggered first, so this needs to be inside the + ImageZoom to work + */} + + + )} + {shouldShowLoadingIndicator && } + + ); } ImageView.propTypes = propTypes; ImageView.defaultProps = defaultProps; +ImageView.displayName = 'ImageView'; -export default withWindowDimensions(ImageView); +export default React.memo(ImageView); From 3428aa3b455ade7fd9cb5c91891a53a1478bd75d Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 18 Aug 2023 14:17:16 +0200 Subject: [PATCH 02/14] refactor --- src/components/ImageView/index.native.js | 76 ++++++++++++------------ 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 653d98e22cc3..2033afc4120d 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -92,7 +92,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { } }, []); - const imageLoadingStart = React.useCallback(() => { + const imageLoadingStart = () => { if (isLoading) { return; } @@ -101,11 +101,12 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { setImageHeight(0); setImageWidth(0); setIsLoading(true); - }, [isLoading, resetImageZoom]); + }; - // React.useEffect(() => { - // imageLoadingStart(); - // }, [imageLoadingStart, url]); + React.useEffect(() => { + imageLoadingStart(); + // eslint-disable-next-line react-hooks/exhaustive-deps -- adding imageLoadingStart to deps array causes the component to be stuck on loading + }, [url]); /** * The `ImageZoom` component requires image dimensions which @@ -135,6 +136,38 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { setIsLoading(false); }; + const onStartShouldSetPanResponder = () => { + const isDoubleClick = new Date().getTime() - lastClickTime.current <= DOUBLE_CLICK_INTERVAL; + lastClickTime.current = new Date().getTime() + + // Let ImageZoom handle the event if the tap is more than one touchPoint or if we are zoomed in + if (numberOfTouches.current === 2 || imageZoomScale.current !== 1) { + return true; + } + + // When we have a double click and the zoom scale is 1 then programmatically zoom the image + // but let the tap fall through to the parent so we can register a swipe down to dismiss + if (isDoubleClick) { + zoom.current.centerOn({ + x: 0, + y: 0, + scale: 2, + duration: 100, + }); + + // onMove will be called after the zoom animation. + // So it's possible to zoom and swipe and stuck in between the images. + // Sending scale just when we actually trigger the animation makes this nearly impossible. + // you should be really fast to catch in between state updates. + // And this lucky case will be fixed by migration to UI thread only code + // with gesture handler and reanimated. + onScaleChanged(2); + } + + // We must be either swiping down or double tapping since we are at zoom scale 1 + return false; + } + // Default windowHeight accounts for the modal header height const calculatedWindowHeight = windowHeight - variables.contentHeaderHeight; const hasImageDimensions = imageWidth !== 0 && imageHeight !== 0; @@ -151,44 +184,13 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { > {Boolean(containerHeight) && ( (this.zoom = el)} ref={zoom} onClick={onPress} cropWidth={windowWidth} cropHeight={calculatedWindowHeight} imageWidth={imageWidth} imageHeight={imageHeight} - onStartShouldSetPanResponder={() => { - const isDoubleClick = new Date().getTime() - lastClickTime.current <= DOUBLE_CLICK_INTERVAL; - lastClickTime.current = new Date().getTime() - - // Let ImageZoom handle the event if the tap is more than one touchPoint or if we are zoomed in - if (numberOfTouches.current === 2 || imageZoomScale.current !== 1) { - return true; - } - - // When we have a double click and the zoom scale is 1 then programmatically zoom the image - // but let the tap fall through to the parent so we can register a swipe down to dismiss - if (isDoubleClick) { - zoom.current.centerOn({ - x: 0, - y: 0, - scale: 2, - duration: 100, - }); - - // onMove will be called after the zoom animation. - // So it's possible to zoom and swipe and stuck in between the images. - // Sending scale just when we actually trigger the animation makes this nearly impossible. - // you should be really fast to catch in between state updates. - // And this lucky case will be fixed by migration to UI thread only code - // with gesture handler and reanimated. - onScaleChanged(2); - } - - // We must be either swiping down or double tapping since we are at zoom scale 1 - return false; - }} + onStartShouldSetPanResponder={onStartShouldSetPanResponder} onMove={({scale}) => { onScaleChanged(scale); imageZoomScale.current = scale From ad92c4b58b86be4c1df12f1aa1bb81d333f80c26 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 21 Aug 2023 14:47:11 +0200 Subject: [PATCH 03/14] remove old component --- src/components/ImageView/index-old.native.js | 255 ------------------- 1 file changed, 255 deletions(-) delete mode 100644 src/components/ImageView/index-old.native.js diff --git a/src/components/ImageView/index-old.native.js b/src/components/ImageView/index-old.native.js deleted file mode 100644 index e1dcaffde9f8..000000000000 --- a/src/components/ImageView/index-old.native.js +++ /dev/null @@ -1,255 +0,0 @@ -import React, {PureComponent} from 'react'; -import PropTypes from 'prop-types'; -import {View, PanResponder} from 'react-native'; -import ImageZoom from 'react-native-image-pan-zoom'; -import _ from 'underscore'; -import styles from '../../styles/styles'; -import variables from '../../styles/variables'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; -import FullscreenLoadingIndicator from '../FullscreenLoadingIndicator'; -import Image from '../Image'; - -/** - * On the native layer, we use a image library to handle zoom functionality - */ -const propTypes = { - /** Whether source url requires authentication */ - isAuthTokenRequired: PropTypes.bool, - - /** URL to full-sized image */ - url: PropTypes.string.isRequired, - - /** Handles scale changed event in image zoom component. Used on native only */ - onScaleChanged: PropTypes.func.isRequired, - - /** Function for handle on press */ - onPress: PropTypes.func, - - ...windowDimensionsPropTypes, -}; - -const defaultProps = { - isAuthTokenRequired: false, - onPress: () => {}, -}; - -class ImageView extends PureComponent { - constructor(props) { - super(props); - - this.state = { - isLoading: true, - imageWidth: 0, - imageHeight: 0, - interactionPromise: undefined, - }; - - // Use the default double click interval from the ImageZoom library - // https://github.com/ascoders/react-native-image-zoom/blob/master/src/image-zoom/image-zoom.type.ts#L79 - this.doubleClickInterval = 175; - this.imageZoomScale = 1; - this.lastClickTime = 0; - this.amountOfTouches = 0; - - // PanResponder used to capture how many touches are active on the attachment image - this.panResponder = PanResponder.create({ - onStartShouldSetPanResponder: this.updatePanResponderTouches.bind(this), - }); - - this.configureImageZoom = this.configureImageZoom.bind(this); - this.imageLoadingStart = this.imageLoadingStart.bind(this); - } - - componentDidMount() { - console.log('I RENDER') - } - - componentDidUpdate(prevProps) { - if (this.props.url === prevProps.url) { - return; - } - - this.imageLoadingStart(); - - if (this.interactionPromise) { - this.state.interactionPromise.cancel(); - } - } - - componentWillUnmount() { - if (!this.state.interactionPromise) { - return; - } - this.state.interactionPromise.cancel(); - } - - /** - * Updates the amount of active touches on the PanResponder on our ImageZoom overlay View - * - * @param {Event} e - * @param {GestureState} gestureState - * @returns {Boolean} - */ - updatePanResponderTouches(e, gestureState) { - if (_.isNumber(gestureState.numberActiveTouches)) { - this.amountOfTouches = gestureState.numberActiveTouches; - } - - // We don't need to set the panResponder since all we care about is checking the gestureState, so return false - return false; - } - - /** - * The `ImageZoom` component requires image dimensions which - * are calculated here from the natural image dimensions produced by - * the `onLoad` event - * - * @param {Object} nativeEvent - */ - configureImageZoom({nativeEvent}) { - console.log('CONFIGURE IMAGE ZOOM') - let imageWidth = nativeEvent.width; - let imageHeight = nativeEvent.height; - const containerWidth = Math.round(this.props.windowWidth); - const containerHeight = Math.round(this.state.containerHeight ? this.state.containerHeight : this.props.windowHeight); - - const aspectRatio = Math.min(containerHeight / imageHeight, containerWidth / imageWidth); - - imageHeight *= aspectRatio; - imageWidth *= aspectRatio; - - // Resize the image to max dimensions possible on the Native platforms to prevent crashes on Android. To keep the same behavior, apply to IOS as well. - const maxDimensionsScale = 11; - imageWidth = Math.min(imageWidth, containerWidth * maxDimensionsScale); - imageHeight = Math.min(imageHeight, containerHeight * maxDimensionsScale); - this.setState({imageHeight, imageWidth, isLoading: false}); - } - - /** - * When the url changes and the image must load again, - * this resets the zoom to ensure the next image loads with the correct dimensions. - */ - resetImageZoom() { - if (this.imageZoomScale !== 1) { - this.imageZoomScale = 1; - } - - if (this.zoom) { - this.zoom.centerOn({ - x: 0, - y: 0, - scale: 1, - duration: 0, - }); - } - } - - imageLoadingStart() { - if (this.state.isLoading) { - return; - } - console.log('IMAGE LOADING START') - this.resetImageZoom(); - this.setState({imageHeight: 0, imageWidth: 0, isLoading: true}); - } - - render() { - // Default windowHeight accounts for the modal header height - const windowHeight = this.props.windowHeight - variables.contentHeaderHeight; - const hasImageDimensions = this.state.imageWidth !== 0 && this.state.imageHeight !== 0; - const shouldShowLoadingIndicator = this.state.isLoading || !hasImageDimensions; - - // Zoom view should be loaded only after measuring actual image dimensions, otherwise it causes blurred images on Android - return ( - { - const layout = event.nativeEvent.layout; - this.setState({ - containerHeight: layout.height, - }); - }} - > - {Boolean(this.state.containerHeight) && ( - (this.zoom = el)} - onClick={() => this.props.onPress()} - cropWidth={this.props.windowWidth} - cropHeight={windowHeight} - imageWidth={this.state.imageWidth} - imageHeight={this.state.imageHeight} - onStartShouldSetPanResponder={() => { - const isDoubleClick = new Date().getTime() - this.lastClickTime <= this.doubleClickInterval; - this.lastClickTime = new Date().getTime(); - - // Let ImageZoom handle the event if the tap is more than one touchPoint or if we are zoomed in - if (this.amountOfTouches === 2 || this.imageZoomScale !== 1) { - return true; - } - - // When we have a double click and the zoom scale is 1 then programmatically zoom the image - // but let the tap fall through to the parent so we can register a swipe down to dismiss - if (isDoubleClick) { - this.zoom.centerOn({ - x: 0, - y: 0, - scale: 2, - duration: 100, - }); - - // onMove will be called after the zoom animation. - // So it's possible to zoom and swipe and stuck in between the images. - // Sending scale just when we actually trigger the animation makes this nearly impossible. - // you should be really fast to catch in between state updates. - // And this lucky case will be fixed by migration to UI thread only code - // with gesture handler and reanimated. - this.props.onScaleChanged(2); - } - - // We must be either swiping down or double tapping since we are at zoom scale 1 - return false; - }} - onMove={({scale}) => { - this.props.onScaleChanged(scale); - this.imageZoomScale = scale; - }} - > - - {/** - Create an invisible view on top of the image so we can capture and set the amount of touches before - the ImageZoom's PanResponder does. Children will be triggered first, so this needs to be inside the - ImageZoom to work - */} - - - )} - {shouldShowLoadingIndicator && } - - ); - } -} - -ImageView.propTypes = propTypes; -ImageView.defaultProps = defaultProps; - -export default withWindowDimensions(ImageView); From c9746786f9b239291f3c8f3fd6f583d5d1c21b36 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 21 Aug 2023 14:53:06 +0200 Subject: [PATCH 04/14] run prettier --- src/components/ImageView/index.native.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 2033afc4120d..5315b1e525c9 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -45,9 +45,9 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { const [imageHeight, setImageHeight] = React.useState(0); const [containerHeight, setContainerHeight] = React.useState(); - const imageZoomScale = React.useRef(1) - const lastClickTime = React.useRef(0) - const numberOfTouches = React.useRef(0) + const imageZoomScale = React.useRef(1); + const lastClickTime = React.useRef(0); + const numberOfTouches = React.useRef(0); const zoom = React.useRef(); /** @@ -59,7 +59,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { */ const updatePanResponderTouches = (e, gestureState) => { if (_.isNumber(gestureState.numberActiveTouches)) { - numberOfTouches.current = gestureState.numberActiveTouches + numberOfTouches.current = gestureState.numberActiveTouches; } // We don't need to set the panResponder since all we care about is checking the gestureState, so return false @@ -138,7 +138,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { const onStartShouldSetPanResponder = () => { const isDoubleClick = new Date().getTime() - lastClickTime.current <= DOUBLE_CLICK_INTERVAL; - lastClickTime.current = new Date().getTime() + lastClickTime.current = new Date().getTime(); // Let ImageZoom handle the event if the tap is more than one touchPoint or if we are zoomed in if (numberOfTouches.current === 2 || imageZoomScale.current !== 1) { @@ -166,7 +166,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { // We must be either swiping down or double tapping since we are at zoom scale 1 return false; - } + }; // Default windowHeight accounts for the modal header height const calculatedWindowHeight = windowHeight - variables.contentHeaderHeight; @@ -193,7 +193,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { onStartShouldSetPanResponder={onStartShouldSetPanResponder} onMove={({scale}) => { onScaleChanged(scale); - imageZoomScale.current = scale + imageZoomScale.current = scale; }} > Date: Mon, 21 Aug 2023 15:18:20 +0200 Subject: [PATCH 05/14] modify react hook imports --- src/components/ImageView/index.native.js | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 5315b1e525c9..82e24701c093 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useState, useCallback, useRef, memo, useEffect} from 'react'; import PropTypes from 'prop-types'; import {View, PanResponder} from 'react-native'; import ImageZoom from 'react-native-image-pan-zoom'; @@ -40,15 +40,15 @@ const DOUBLE_CLICK_INTERVAL = 175; function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { const {windowWidth, windowHeight} = useWindowDimensions(); - const [isLoading, setIsLoading] = React.useState(true); - const [imageWidth, setImageWidth] = React.useState(0); - const [imageHeight, setImageHeight] = React.useState(0); - const [containerHeight, setContainerHeight] = React.useState(); + const [isLoading, setIsLoading] = useState(true); + const [imageWidth, setImageWidth] = useState(0); + const [imageHeight, setImageHeight] = useState(0); + const [containerHeight, setContainerHeight] = useState(); - const imageZoomScale = React.useRef(1); - const lastClickTime = React.useRef(0); - const numberOfTouches = React.useRef(0); - const zoom = React.useRef(); + const imageZoomScale = useRef(1); + const lastClickTime = useRef(0); + const numberOfTouches = useRef(0); + const zoom = useRef(); /** * Updates the amount of active touches on the PanResponder on our ImageZoom overlay View @@ -67,7 +67,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { }; // PanResponder used to capture how many touches are active on the attachment image - const panResponder = React.useRef( + const panResponder = useRef( PanResponder.create({ onStartShouldSetPanResponder: updatePanResponderTouches, }), @@ -77,7 +77,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { * When the url changes and the image must load again, * this resets the zoom to ensure the next image loads with the correct dimensions. */ - const resetImageZoom = React.useCallback(() => { + const resetImageZoom = useCallback(() => { if (imageZoomScale.current !== 1) { imageZoomScale.current = 1; } @@ -103,7 +103,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { setIsLoading(true); }; - React.useEffect(() => { + useEffect(() => { imageLoadingStart(); // eslint-disable-next-line react-hooks/exhaustive-deps -- adding imageLoadingStart to deps array causes the component to be stuck on loading }, [url]); @@ -234,4 +234,4 @@ ImageView.propTypes = propTypes; ImageView.defaultProps = defaultProps; ImageView.displayName = 'ImageView'; -export default React.memo(ImageView); +export default memo(ImageView); From ea5859cda835cec87a1ddc473c4182091a847713 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 21 Aug 2023 15:22:24 +0200 Subject: [PATCH 06/14] minor changes based on code review feedback --- src/components/ImageView/index.native.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 82e24701c093..307c6646f55b 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -35,6 +35,8 @@ const defaultProps = { style: {}, }; +// Use the default double click interval from the ImageZoom library +// https://github.com/ascoders/react-native-image-zoom/blob/master/src/image-zoom/image-zoom.type.ts#L79 const DOUBLE_CLICK_INTERVAL = 175; function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { @@ -43,12 +45,12 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { const [isLoading, setIsLoading] = useState(true); const [imageWidth, setImageWidth] = useState(0); const [imageHeight, setImageHeight] = useState(0); - const [containerHeight, setContainerHeight] = useState(); + const [containerHeight, setContainerHeight] = useState(null); const imageZoomScale = useRef(1); const lastClickTime = useRef(0); const numberOfTouches = useRef(0); - const zoom = useRef(); + const zoom = useRef(null); /** * Updates the amount of active touches on the PanResponder on our ImageZoom overlay View From 36a6b2b565412ba2bc2f0419de021ede82e2449b Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 21 Aug 2023 15:30:08 +0200 Subject: [PATCH 07/14] clean up the code --- src/components/ImageView/index.native.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 307c6646f55b..31a0f9fa1f7b 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -98,8 +98,8 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { if (isLoading) { return; } + resetImageZoom(); - setImageHeight(0); setImageWidth(0); setIsLoading(true); @@ -118,27 +118,27 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { * @param {Object} nativeEvent */ const configureImageZoom = ({nativeEvent}) => { - let imgWidth = nativeEvent.width; - let imgHeight = nativeEvent.height; + let imageZoomWidth = nativeEvent.width; + let imageZoomHeight = nativeEvent.height; const roundedContainerWidth = Math.round(windowWidth); const roundedContainerHeight = Math.round(containerHeight || windowHeight); - const aspectRatio = Math.min(roundedContainerHeight / imgHeight, roundedContainerWidth / imgWidth); + const aspectRatio = Math.min(roundedContainerHeight / imageZoomHeight, roundedContainerWidth / imageZoomWidth); - imgHeight *= aspectRatio; - imgWidth *= aspectRatio; + imageZoomHeight *= aspectRatio; + imageZoomWidth *= aspectRatio; // Resize the image to max dimensions possible on the Native platforms to prevent crashes on Android. To keep the same behavior, apply to IOS as well. const maxDimensionsScale = 11; - imgWidth = Math.min(imgWidth, roundedContainerWidth * maxDimensionsScale); - imgHeight = Math.min(imgHeight, roundedContainerHeight * maxDimensionsScale); + imageZoomWidth = Math.min(imageZoomWidth, roundedContainerWidth * maxDimensionsScale); + imageZoomHeight = Math.min(imageZoomHeight, roundedContainerHeight * maxDimensionsScale); - setImageHeight(imgHeight); - setImageWidth(imgWidth); + setImageHeight(imageZoomHeight); + setImageWidth(imageZoomWidth); setIsLoading(false); }; - const onStartShouldSetPanResponder = () => { + const configurePanResponder = () => { const isDoubleClick = new Date().getTime() - lastClickTime.current <= DOUBLE_CLICK_INTERVAL; lastClickTime.current = new Date().getTime(); @@ -192,7 +192,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { cropHeight={calculatedWindowHeight} imageWidth={imageWidth} imageHeight={imageHeight} - onStartShouldSetPanResponder={onStartShouldSetPanResponder} + onStartShouldSetPanResponder={configurePanResponder} onMove={({scale}) => { onScaleChanged(scale); imageZoomScale.current = scale; From 2e9ff94e8dd45e5d9af5ed08fce96e49cfa1e5a3 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 21 Aug 2023 15:43:11 +0200 Subject: [PATCH 08/14] assign current timestamp to a variable --- src/components/ImageView/index.native.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 31a0f9fa1f7b..b52f3c9f4642 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -139,8 +139,9 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { }; const configurePanResponder = () => { - const isDoubleClick = new Date().getTime() - lastClickTime.current <= DOUBLE_CLICK_INTERVAL; - lastClickTime.current = new Date().getTime(); + const currentTimestamp = new Date().getTime() + const isDoubleClick = currentTimestamp - lastClickTime.current <= DOUBLE_CLICK_INTERVAL; + lastClickTime.current = currentTimestamp; // Let ImageZoom handle the event if the tap is more than one touchPoint or if we are zoomed in if (numberOfTouches.current === 2 || imageZoomScale.current !== 1) { From 743b0d1752cf15b1b493f9e72d79c881a38052a6 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 21 Aug 2023 15:57:47 +0200 Subject: [PATCH 09/14] merge two states into one --- src/components/ImageView/index.native.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index b52f3c9f4642..de881fdde6bd 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -43,8 +43,10 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { const {windowWidth, windowHeight} = useWindowDimensions(); const [isLoading, setIsLoading] = useState(true); - const [imageWidth, setImageWidth] = useState(0); - const [imageHeight, setImageHeight] = useState(0); + const [imageDimensions, setImageDimensions] = useState({ + width: 0, + height: 0 + }) const [containerHeight, setContainerHeight] = useState(null); const imageZoomScale = useRef(1); @@ -100,8 +102,10 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { } resetImageZoom(); - setImageHeight(0); - setImageWidth(0); + setImageDimensions({ + width: 0, + height: 0 + }) setIsLoading(true); }; @@ -133,8 +137,10 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { imageZoomWidth = Math.min(imageZoomWidth, roundedContainerWidth * maxDimensionsScale); imageZoomHeight = Math.min(imageZoomHeight, roundedContainerHeight * maxDimensionsScale); - setImageHeight(imageZoomHeight); - setImageWidth(imageZoomWidth); + setImageDimensions({ + height: imageZoomHeight, + width: imageZoomWidth + }) setIsLoading(false); }; @@ -173,7 +179,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { // Default windowHeight accounts for the modal header height const calculatedWindowHeight = windowHeight - variables.contentHeaderHeight; - const hasImageDimensions = imageWidth !== 0 && imageHeight !== 0; + const hasImageDimensions = imageDimensions.width !== 0 && imageDimensions.height !== 0; const shouldShowLoadingIndicator = isLoading || !hasImageDimensions; // Zoom view should be loaded only after measuring actual image dimensions, otherwise it causes blurred images on Android @@ -191,8 +197,8 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { onClick={onPress} cropWidth={windowWidth} cropHeight={calculatedWindowHeight} - imageWidth={imageWidth} - imageHeight={imageHeight} + imageWidth={imageDimensions.width} + imageHeight={imageDimensions.height} onStartShouldSetPanResponder={configurePanResponder} onMove={({scale}) => { onScaleChanged(scale); From 7d2eeddc8188cfaf47211e79486d4c7d522e3fe6 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 21 Aug 2023 16:01:30 +0200 Subject: [PATCH 10/14] remove unnecessary memo --- src/components/ImageView/index.native.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index de881fdde6bd..8ddd87bbab59 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -1,4 +1,4 @@ -import React, {useState, useCallback, useRef, memo, useEffect} from 'react'; +import React, {useState, useCallback, useRef, useEffect} from 'react'; import PropTypes from 'prop-types'; import {View, PanResponder} from 'react-native'; import ImageZoom from 'react-native-image-pan-zoom'; @@ -243,4 +243,4 @@ ImageView.propTypes = propTypes; ImageView.defaultProps = defaultProps; ImageView.displayName = 'ImageView'; -export default memo(ImageView); +export default ImageView; From 240cece6a8720faa8c5bcd19656aa7dbc1d95f9c Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 21 Aug 2023 16:07:46 +0200 Subject: [PATCH 11/14] remove unnecessary useCallback --- src/components/ImageView/index.native.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 8ddd87bbab59..d90463324b41 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -1,4 +1,4 @@ -import React, {useState, useCallback, useRef, useEffect} from 'react'; +import React, {useState, useRef, useEffect} from 'react'; import PropTypes from 'prop-types'; import {View, PanResponder} from 'react-native'; import ImageZoom from 'react-native-image-pan-zoom'; @@ -81,7 +81,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { * When the url changes and the image must load again, * this resets the zoom to ensure the next image loads with the correct dimensions. */ - const resetImageZoom = useCallback(() => { + const resetImageZoom = () => { if (imageZoomScale.current !== 1) { imageZoomScale.current = 1; } @@ -94,7 +94,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { duration: 0, }); } - }, []); + } const imageLoadingStart = () => { if (isLoading) { From fe88006d233c01cbe361cd127737ad5080c5790d Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 21 Aug 2023 16:44:41 +0200 Subject: [PATCH 12/14] lint code --- src/components/ImageView/index.native.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index d90463324b41..6697a9dadfff 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -45,8 +45,8 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { const [isLoading, setIsLoading] = useState(true); const [imageDimensions, setImageDimensions] = useState({ width: 0, - height: 0 - }) + height: 0, + }); const [containerHeight, setContainerHeight] = useState(null); const imageZoomScale = useRef(1); @@ -94,18 +94,18 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { duration: 0, }); } - } + }; const imageLoadingStart = () => { if (isLoading) { return; } - + resetImageZoom(); setImageDimensions({ width: 0, - height: 0 - }) + height: 0, + }); setIsLoading(true); }; @@ -139,13 +139,13 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { setImageDimensions({ height: imageZoomHeight, - width: imageZoomWidth - }) + width: imageZoomWidth, + }); setIsLoading(false); }; const configurePanResponder = () => { - const currentTimestamp = new Date().getTime() + const currentTimestamp = new Date().getTime(); const isDoubleClick = currentTimestamp - lastClickTime.current <= DOUBLE_CLICK_INTERVAL; lastClickTime.current = currentTimestamp; From bf073ef35f36f870ebb5475a001b91c2a99a7d83 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 22 Aug 2023 09:11:59 +0200 Subject: [PATCH 13/14] add more detailed explaination to esline disable --- src/components/ImageView/index.native.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 6697a9dadfff..6ddf53a9e400 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -111,7 +111,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { useEffect(() => { imageLoadingStart(); - // eslint-disable-next-line react-hooks/exhaustive-deps -- adding imageLoadingStart to deps array causes the component to be stuck on loading + // eslint-disable-next-line react-hooks/exhaustive-deps -- adding imageLoadingStart to deps array causes the component to be stuck on loading, wrapping imageLoadingStart with useCallback doesn't help. This issue occurs on both platforms }, [url]); /** From edc4821567cdbfd531a73cf57257c7a6112a040c Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 22 Aug 2023 16:28:32 +0200 Subject: [PATCH 14/14] modify effect commit --- src/components/ImageView/index.native.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 6ddf53a9e400..b365f507a4fc 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -111,7 +111,7 @@ function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) { useEffect(() => { imageLoadingStart(); - // eslint-disable-next-line react-hooks/exhaustive-deps -- adding imageLoadingStart to deps array causes the component to be stuck on loading, wrapping imageLoadingStart with useCallback doesn't help. This issue occurs on both platforms + // eslint-disable-next-line react-hooks/exhaustive-deps -- this effect only needs to run when the url changes }, [url]); /**