From feb57aed753c1f464331bafe2b27fda03c217dfe Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 11 Jan 2024 21:12:49 +0700 Subject: [PATCH 01/18] fix: 34120 --- src/components/DistanceEReceipt.js | 1 + src/components/Image/index.js | 10 +++++-- src/components/Image/index.native.js | 26 ++++++++++++++++++- src/components/ImageWithSizeCalculation.tsx | 5 +++- .../MoneyRequestConfirmationList.js | 1 + ...oraryForRefactorRequestConfirmationList.js | 1 + .../ReportActionItem/ReportActionItemImage.js | 12 ++++++--- src/components/ThumbnailImage.tsx | 4 ++- .../Navigation/AppNavigator/AuthScreens.tsx | 2 +- src/pages/ErrorPage/NotFoundPage.js | 1 + 10 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 0241eea44063..5ffece092770 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -72,6 +72,7 @@ function DistanceEReceipt({transaction}) { style={[styles.w100, styles.h100]} isAuthTokenRequired shouldDynamicallyResize={false} + objectPositionTop /> )} diff --git a/src/components/Image/index.js b/src/components/Image/index.js index ef1a69e19c12..58b7ec3383a4 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -1,5 +1,5 @@ import lodashGet from 'lodash/get'; -import React, {useEffect, useMemo} from 'react'; +import React, {useEffect, useMemo, useState} from 'react'; import {Image as RNImage} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -9,6 +9,7 @@ import RESIZE_MODES from './resizeModes'; function Image(props) { const {source: propsSource, isAuthTokenRequired, onLoad, session} = props; + const [aspectRatio, setAspectRatio] = useState(); /** * Check if the image source is a URL - if so the `encryptedAuthToken` is appended * to the source. @@ -38,8 +39,12 @@ function Image(props) { } RNImage.getSize(source.uri, (width, height) => { onLoad({nativeEvent: {width, height}}); + + if (props.objectPositionTop) { + setAspectRatio(height ? width / height : 'auto'); + } }); - }, [onLoad, source]); + }, [onLoad, source, props.objectPositionTop]); // Omit the props which the underlying RNImage won't use const forwardedProps = _.omit(props, ['source', 'onLoad', 'session', 'isAuthTokenRequired']); @@ -49,6 +54,7 @@ function Image(props) { // eslint-disable-next-line react/jsx-props-no-spreading {...forwardedProps} source={source} + style={[forwardedProps.style, aspectRatio !== undefined && {aspectRatio, height: 'auto'}, props.objectPositionTop && !aspectRatio && {opacity: 0}]} /> ); } diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index f31cfb6936d9..edea86725ae7 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -1,11 +1,12 @@ import {Image as ImageComponent} from 'expo-image'; import lodashGet from 'lodash/get'; -import React from 'react'; +import React, {useEffect, useMemo, useState} from 'react'; import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {defaultProps, imagePropTypes} from './imagePropTypes'; import RESIZE_MODES from './resizeModes'; +import {Image as RNImage} from 'react-native'; const dimensionsCache = new Map(); @@ -16,6 +17,7 @@ function resolveDimensions(key) { function Image(props) { // eslint-disable-next-line react/destructuring-assignment const {source, isAuthTokenRequired, session, ...rest} = props; + const [aspectRatio, setAspectRatio] = useState(); let imageSource = source; if (source && source.uri && typeof source.uri === 'number') { @@ -33,6 +35,27 @@ function Image(props) { }; } + const newSource = useMemo(() => { + if (isAuthTokenRequired) { + const authToken = lodashGet(session, 'encryptedAuthToken', null); + return {uri: `${source.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`}; + } + return source; + }, [source, isAuthTokenRequired]); + + useEffect(() => { + if (props.onLoad == null) { + return; + } + RNImage.getSize(newSource.uri, (width, height) => { + props.onLoad({nativeEvent: {width, height}}); + + if (props.objectPositionTop) { + setAspectRatio(height ? width / height : 'auto'); + } + }); + }, [props.onLoad, newSource]); + return ( ); } diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index b13d863d97e1..7ebdd41379c1 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -29,6 +29,8 @@ type ImageWithSizeCalculationProps = { /** Whether the image requires an authToken */ isAuthTokenRequired: boolean; + + objectPositionTop: boolean; }; /** @@ -37,7 +39,7 @@ type ImageWithSizeCalculationProps = { * performing some calculation on a network image after fetching dimensions so * it can be appropriately resized. */ -function ImageWithSizeCalculation({url, style, onMeasure, isAuthTokenRequired}: ImageWithSizeCalculationProps) { +function ImageWithSizeCalculation({url, style, onMeasure, isAuthTokenRequired, objectPositionTop}: ImageWithSizeCalculationProps) { const styles = useThemeStyles(); const isLoadedRef = useRef(null); const [isImageCached, setIsImageCached] = useState(true); @@ -88,6 +90,7 @@ function ImageWithSizeCalculation({url, style, onMeasure, isAuthTokenRequired}: }} onError={onError} onLoad={imageLoadedSuccessfully} + objectPositionTop={objectPositionTop} /> {isLoading && !isImageCached && } diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 13dce9337673..aa33ef87a976 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -612,6 +612,7 @@ function MoneyRequestConfirmationList(props) { // but we don't need it to load the blob:// or file:// image when starting a money request / split bill // So if we have a thumbnail, it means we're retrieving the image from the server isAuthTokenRequired={!_.isEmpty(receiptThumbnail)} + objectPositionTop /> )} {props.shouldShowSmartScanFields && ( diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 36d424ea28f2..2888f7cf2cdf 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -646,6 +646,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ // but we don't need it to load the blob:// or file:// image when starting a money request / split bill // So if we have a thumbnail, it means we're retrieving the image from the server isAuthTokenRequired={!_.isEmpty(receiptThumbnail)} + objectPositionTop /> )} {shouldShowSmartScanFields && ( diff --git a/src/components/ReportActionItem/ReportActionItemImage.js b/src/components/ReportActionItem/ReportActionItemImage.js index 1495dcbd9111..d8f461aec138 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.js +++ b/src/components/ReportActionItem/ReportActionItemImage.js @@ -72,14 +72,18 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal, transactio style={[styles.w100, styles.h100]} isAuthTokenRequired shouldDynamicallyResize={false} + objectPositionTop /> ); } else { receiptImageComponent = ( - + + + ); } diff --git a/src/components/ThumbnailImage.tsx b/src/components/ThumbnailImage.tsx index 3c903ee9e78f..b0fecd07093d 100644 --- a/src/components/ThumbnailImage.tsx +++ b/src/components/ThumbnailImage.tsx @@ -26,6 +26,7 @@ type ThumbnailImageProps = { /** Should the image be resized on load or just fit container */ shouldDynamicallyResize?: boolean; + objectPositionTop?: boolean; }; type UpdateImageSizeParams = { @@ -71,7 +72,7 @@ function calculateThumbnailImageSize(width: number, height: number, windowHeight return {thumbnailWidth: Math.max(40, thumbnailScreenWidth), thumbnailHeight: Math.max(40, thumbnailScreenHeight)}; } -function ThumbnailImage({previewSourceURL, style, isAuthTokenRequired, imageWidth = 200, imageHeight = 200, shouldDynamicallyResize = true}: ThumbnailImageProps) { +function ThumbnailImage({previewSourceURL, style, isAuthTokenRequired, imageWidth, imageHeight, shouldDynamicallyResize = true, objectPositionTop = false}: ThumbnailImageProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {windowHeight} = useWindowDimensions(); @@ -103,6 +104,7 @@ function ThumbnailImage({previewSourceURL, style, isAuthTokenRequired, imageWidt url={previewSourceURL} onMeasure={updateImageSize} isAuthTokenRequired={isAuthTokenRequired} + objectPositionTop={objectPositionTop} /> diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 7286615e6ba6..caeb65fbaa3a 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -291,7 +291,7 @@ function AuthScreens({lastUpdateIDAppliedToClient, session, lastOpenedPublicRoom Navigation.navigate(ROUTES.HOME)}/>} /> ); From 0eeedc9bca10abc6554bd2eb5e412a35e9bbe835 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 11 Jan 2024 21:19:21 +0700 Subject: [PATCH 02/18] fix: redundant --- src/components/ThumbnailImage.tsx | 2 +- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 2 +- src/pages/ErrorPage/NotFoundPage.js | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/ThumbnailImage.tsx b/src/components/ThumbnailImage.tsx index b0fecd07093d..b04a67fbdc7d 100644 --- a/src/components/ThumbnailImage.tsx +++ b/src/components/ThumbnailImage.tsx @@ -72,7 +72,7 @@ function calculateThumbnailImageSize(width: number, height: number, windowHeight return {thumbnailWidth: Math.max(40, thumbnailScreenWidth), thumbnailHeight: Math.max(40, thumbnailScreenHeight)}; } -function ThumbnailImage({previewSourceURL, style, isAuthTokenRequired, imageWidth, imageHeight, shouldDynamicallyResize = true, objectPositionTop = false}: ThumbnailImageProps) { +function ThumbnailImage({previewSourceURL, style, isAuthTokenRequired, imageWidth = 200, imageHeight = 200, shouldDynamicallyResize = true, objectPositionTop = false}: ThumbnailImageProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {windowHeight} = useWindowDimensions(); diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index caeb65fbaa3a..7286615e6ba6 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -291,7 +291,7 @@ function AuthScreens({lastUpdateIDAppliedToClient, session, lastOpenedPublicRoom Navigation.navigate(ROUTES.HOME)}/>} + component={NotFoundPage} /> ); From 5e5adb85b0a05d0d57d4fc3cbc7521d59edc0b59 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 24 Jan 2024 16:17:03 +0700 Subject: [PATCH 03/18] fix lint --- src/components/Image/index.native.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index edea86725ae7..1c7b239f6acc 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -1,12 +1,12 @@ import {Image as ImageComponent} from 'expo-image'; import lodashGet from 'lodash/get'; import React, {useEffect, useMemo, useState} from 'react'; +import {Image as RNImage} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {defaultProps, imagePropTypes} from './imagePropTypes'; import RESIZE_MODES from './resizeModes'; -import {Image as RNImage} from 'react-native'; const dimensionsCache = new Map(); From 2e260e62b64f2c0d8601b006a59037b9fbd12fcc Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 24 Jan 2024 17:09:22 +0700 Subject: [PATCH 04/18] fix lint --- src/components/Image/index.native.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index 1c7b239f6acc..f221a3a15019 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -41,7 +41,7 @@ function Image(props) { return {uri: `${source.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`}; } return source; - }, [source, isAuthTokenRequired]); + }, [source, isAuthTokenRequired, session]); useEffect(() => { if (props.onLoad == null) { @@ -54,7 +54,7 @@ function Image(props) { setAspectRatio(height ? width / height : 'auto'); } }); - }, [props.onLoad, newSource]); + }, [props.onLoad, newSource, props]); return ( Date: Fri, 26 Jan 2024 11:35:09 +0700 Subject: [PATCH 05/18] fix crash bug --- src/components/Image/index.js | 4 ++-- src/components/Image/index.native.js | 26 ++++---------------------- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/src/components/Image/index.js b/src/components/Image/index.js index 58b7ec3383a4..f4962ad9b324 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -9,7 +9,7 @@ import RESIZE_MODES from './resizeModes'; function Image(props) { const {source: propsSource, isAuthTokenRequired, onLoad, session} = props; - const [aspectRatio, setAspectRatio] = useState(); + const [aspectRatio, setAspectRatio] = useState(null); /** * Check if the image source is a URL - if so the `encryptedAuthToken` is appended * to the source. @@ -54,7 +54,7 @@ function Image(props) { // eslint-disable-next-line react/jsx-props-no-spreading {...forwardedProps} source={source} - style={[forwardedProps.style, aspectRatio !== undefined && {aspectRatio, height: 'auto'}, props.objectPositionTop && !aspectRatio && {opacity: 0}]} + style={[forwardedProps.style, !!aspectRatio && {aspectRatio, height: 'auto'}, props.objectPositionTop && !aspectRatio && {opacity: 0}]} /> ); } diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index f221a3a15019..7da72c34a012 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -35,27 +35,6 @@ function Image(props) { }; } - const newSource = useMemo(() => { - if (isAuthTokenRequired) { - const authToken = lodashGet(session, 'encryptedAuthToken', null); - return {uri: `${source.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`}; - } - return source; - }, [source, isAuthTokenRequired, session]); - - useEffect(() => { - if (props.onLoad == null) { - return; - } - RNImage.getSize(newSource.uri, (width, height) => { - props.onLoad({nativeEvent: {width, height}}); - - if (props.objectPositionTop) { - setAspectRatio(height ? width / height : 'auto'); - } - }); - }, [props.onLoad, newSource, props]); - return ( ); } From 7c9c40ff933a7d2d47a39a5f391101863551aa00 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 26 Jan 2024 17:01:55 +0700 Subject: [PATCH 06/18] fix lint --- src/components/Image/index.native.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index 7da72c34a012..862807b909bb 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -1,7 +1,6 @@ import {Image as ImageComponent} from 'expo-image'; import lodashGet from 'lodash/get'; -import React, {useEffect, useMemo, useState} from 'react'; -import {Image as RNImage} from 'react-native'; +import React, { useState } from 'react'; import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; From a153e8a46181c676adfd995ce00759e7b4e6b5cb Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 26 Jan 2024 17:41:00 +0700 Subject: [PATCH 07/18] fix lint --- src/components/Image/index.native.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index 862807b909bb..2791ea40925d 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -1,6 +1,6 @@ import {Image as ImageComponent} from 'expo-image'; import lodashGet from 'lodash/get'; -import React, { useState } from 'react'; +import React, {useState} from 'react'; import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; From 21253021d96298966edad453518aec24aeb0c60f Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 30 Jan 2024 16:29:37 +0700 Subject: [PATCH 08/18] fix the case wide image --- src/components/Image/index.js | 4 ++++ src/components/Image/index.native.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/components/Image/index.js b/src/components/Image/index.js index f4962ad9b324..848e64f60350 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -41,6 +41,10 @@ function Image(props) { onLoad({nativeEvent: {width, height}}); if (props.objectPositionTop) { + if (width > height) { + setAspectRatio(1); + return; + } setAspectRatio(height ? width / height : 'auto'); } }); diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index 2791ea40925d..b6d5f5b2dcda 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -46,6 +46,10 @@ function Image(props) { props.onLoad({nativeEvent: {width, height}}); } if (props.objectPositionTop) { + if (width > height) { + setAspectRatio(1); + return; + } setAspectRatio(height ? width / height : 'auto'); } }} From 00ccc9bd4e75c8a480313f11ecf986725eb6a83e Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 30 Jan 2024 16:34:02 +0700 Subject: [PATCH 09/18] add comment prop --- src/components/Image/imagePropTypes.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/Image/imagePropTypes.js b/src/components/Image/imagePropTypes.js index 78bd48ba47ec..cc4c36e32387 100644 --- a/src/components/Image/imagePropTypes.js +++ b/src/components/Image/imagePropTypes.js @@ -34,6 +34,9 @@ const imagePropTypes = { /** Currently logged in user authToken */ authToken: PropTypes.string, }), + + /** Whether we should show the top of the image */ + objectPositionTop: PropTypes.string }; const defaultProps = { @@ -43,6 +46,7 @@ const defaultProps = { }, isAuthTokenRequired: false, resizeMode: RESIZE_MODES.cover, + objectPositionTop: false, onLoadStart: () => {}, onLoadEnd: () => {}, onLoad: () => {}, From c761e1c17e8795c514904e167f23e42c9fa7aa27 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 30 Jan 2024 18:08:45 +0700 Subject: [PATCH 10/18] merge main --- src/components/Image/imagePropTypes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Image/imagePropTypes.js b/src/components/Image/imagePropTypes.js index cc4c36e32387..5d28b1a9f3f4 100644 --- a/src/components/Image/imagePropTypes.js +++ b/src/components/Image/imagePropTypes.js @@ -36,7 +36,7 @@ const imagePropTypes = { }), /** Whether we should show the top of the image */ - objectPositionTop: PropTypes.string + objectPositionTop: PropTypes.string, }; const defaultProps = { From d09ee205845c7b47eb4b6cdc49c803dc8be43918 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 2 Feb 2024 10:41:02 +0700 Subject: [PATCH 11/18] centralize logic --- src/components/Image/BaseImage.native.tsx | 16 +++------------- src/components/Image/BaseImage.tsx | 16 +++------------- src/components/Image/index.js | 22 ++++++++++++++++++++-- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/components/Image/BaseImage.native.tsx b/src/components/Image/BaseImage.native.tsx index e912bdda9eba..c517efd04515 100644 --- a/src/components/Image/BaseImage.native.tsx +++ b/src/components/Image/BaseImage.native.tsx @@ -1,10 +1,9 @@ import {Image as ExpoImage} from 'expo-image'; import type {ImageProps as ExpoImageProps, ImageLoadEventData} from 'expo-image'; -import {useCallback, useState} from 'react'; +import {useCallback} from 'react'; import type {BaseImageProps} from './types'; -function BaseImage({onLoad, objectPositionTop = false, style, ...props}: ExpoImageProps & BaseImageProps) { - const [aspectRatio, setAspectRatio] = useState(null); +function BaseImage({onLoad, ...props}: ExpoImageProps & BaseImageProps) { const imageLoadedSuccessfully = useCallback( (event: ImageLoadEventData) => { if (!onLoad) { @@ -14,23 +13,14 @@ function BaseImage({onLoad, objectPositionTop = false, style, ...props}: ExpoIma // We override `onLoad`, so both web and native have the same signature const {width, height} = event.source; onLoad({nativeEvent: {width, height}}); - - if (objectPositionTop) { - if (width > height) { - setAspectRatio(1); - return; - } - setAspectRatio(height ? width / height : 'auto'); - } }, - [onLoad, objectPositionTop], + [onLoad], ); return ( diff --git a/src/components/Image/BaseImage.tsx b/src/components/Image/BaseImage.tsx index 9a3f4668647e..ebdd76840267 100644 --- a/src/components/Image/BaseImage.tsx +++ b/src/components/Image/BaseImage.tsx @@ -1,10 +1,9 @@ -import React, {useCallback, useState} from 'react'; +import React, {useCallback} from 'react'; import {Image as RNImage} from 'react-native'; import type {ImageLoadEventData, ImageProps as WebImageProps} from 'react-native'; import type {BaseImageProps} from './types'; -function BaseImage({onLoad, objectPositionTop = false, style, ...props}: WebImageProps & BaseImageProps) { - const [aspectRatio, setAspectRatio] = useState(null); +function BaseImage({onLoad, ...props}: WebImageProps & BaseImageProps) { const imageLoadedSuccessfully = useCallback( (event: {nativeEvent: ImageLoadEventData}) => { if (!onLoad) { @@ -14,23 +13,14 @@ function BaseImage({onLoad, objectPositionTop = false, style, ...props}: WebImag // We override `onLoad`, so both web and native have the same signature const {width, height} = event.nativeEvent.source; onLoad({nativeEvent: {width, height}}); - - if (objectPositionTop) { - if (width > height) { - setAspectRatio(1); - return; - } - setAspectRatio(height ? width / height : 'auto'); - } }, - [onLoad, objectPositionTop], + [onLoad], ); return ( diff --git a/src/components/Image/index.js b/src/components/Image/index.js index 8cee1cf95e14..2788e85895f6 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -1,5 +1,5 @@ import lodashGet from 'lodash/get'; -import React, {useMemo} from 'react'; +import React, {useCallback, useMemo} from 'react'; import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -7,7 +7,9 @@ import BaseImage from './BaseImage'; import {defaultProps, imagePropTypes} from './imagePropTypes'; import RESIZE_MODES from './resizeModes'; -function Image({source: propsSource, isAuthTokenRequired, session, ...forwardedProps}) { +function Image({source: propsSource, isAuthTokenRequired, session, onLoad, ...forwardedProps}) { + const [aspectRatio, setAspectRatio] = useState(null); + // Update the source to include the auth token if required const source = useMemo(() => { if (typeof lodashGet(propsSource, 'uri') === 'number') { @@ -28,11 +30,27 @@ function Image({source: propsSource, isAuthTokenRequired, session, ...forwardedP // eslint-disable-next-line react-hooks/exhaustive-deps }, [propsSource, isAuthTokenRequired]); + const imageLoadedSuccessfully = useCallback((event) => { + const {width, height} = event.nativeEvent; + + onLoad(event); + + if (objectPositionTop) { + if (width > height) { + setAspectRatio(1); + return; + } + setAspectRatio(height ? width / height : 'auto'); + } + }) + return ( ); } From 7cf8fd81cf99cd3a8cfa869f33be36c271081e65 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 2 Feb 2024 10:56:55 +0700 Subject: [PATCH 12/18] fix lint --- src/components/Image/imagePropTypes.js | 2 +- src/components/Image/index.js | 29 ++++++++++++++------------ src/components/Image/types.ts | 3 --- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/components/Image/imagePropTypes.js b/src/components/Image/imagePropTypes.js index 5d28b1a9f3f4..deb7d75da5ae 100644 --- a/src/components/Image/imagePropTypes.js +++ b/src/components/Image/imagePropTypes.js @@ -36,7 +36,7 @@ const imagePropTypes = { }), /** Whether we should show the top of the image */ - objectPositionTop: PropTypes.string, + objectPositionTop: PropTypes.bool, }; const defaultProps = { diff --git a/src/components/Image/index.js b/src/components/Image/index.js index 2788e85895f6..6851604be8b1 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -1,5 +1,5 @@ import lodashGet from 'lodash/get'; -import React, {useCallback, useMemo} from 'react'; +import React, {useCallback, useMemo, useState} from 'react'; import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -7,8 +7,8 @@ import BaseImage from './BaseImage'; import {defaultProps, imagePropTypes} from './imagePropTypes'; import RESIZE_MODES from './resizeModes'; -function Image({source: propsSource, isAuthTokenRequired, session, onLoad, ...forwardedProps}) { - const [aspectRatio, setAspectRatio] = useState(null); +function Image({source: propsSource, isAuthTokenRequired, session, onLoad, style, objectPositionTop, ...forwardedProps}) { + const [aspectRatio, setAspectRatio] = useState(null); // Update the source to include the auth token if required const source = useMemo(() => { @@ -30,19 +30,22 @@ function Image({source: propsSource, isAuthTokenRequired, session, onLoad, ...fo // eslint-disable-next-line react-hooks/exhaustive-deps }, [propsSource, isAuthTokenRequired]); - const imageLoadedSuccessfully = useCallback((event) => { - const {width, height} = event.nativeEvent; + const imageLoadedSuccessfully = useCallback( + (event) => { + const {width, height} = event.nativeEvent; - onLoad(event); + onLoad(event); - if (objectPositionTop) { - if (width > height) { - setAspectRatio(1); - return; + if (objectPositionTop) { + if (width > height) { + setAspectRatio(1); + return; + } + setAspectRatio(height ? width / height : 'auto'); } - setAspectRatio(height ? width / height : 'auto'); - } - }) + }, + [onLoad, objectPositionTop], + ); return ( void; - - /** Whether we should show the top of the image */ - objectPositionTop?: boolean; }; export type {BaseImageProps}; From 86190f61d6f9f090ea0714939a15685c57cdc246 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 6 Feb 2024 01:01:01 +0700 Subject: [PATCH 13/18] revert hard code --- .../ReportActionItem/ReportActionItemImage.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/ReportActionItemImage.tsx b/src/components/ReportActionItem/ReportActionItemImage.tsx index 7dc4cdf07031..ed46e00c7923 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.tsx +++ b/src/components/ReportActionItem/ReportActionItemImage.tsx @@ -73,13 +73,11 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal = false, tr ); } else { receiptImageComponent = ( - - - + ); } From 54d0bb9d340926b8997c8b1d584321554d49ecec Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 20 Mar 2024 12:08:37 +0700 Subject: [PATCH 14/18] fix lint --- src/components/Image/index.tsx | 36 +++++++++++++++++++-- src/components/Image/types.ts | 7 ++-- src/components/ImageWithSizeCalculation.tsx | 5 +-- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index f4e5bf4834e0..c8c78b54de06 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -1,11 +1,39 @@ -import React, {useMemo} from 'react'; +import React, {useCallback, useMemo, useState} from 'react'; import {withOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import BaseImage from './BaseImage'; -import type {ImageOnyxProps, ImageOwnProps, ImageProps} from './types'; +import type {ImageOnLoadEvent, ImageOnyxProps, ImageOwnProps, ImageProps} from './types'; -function Image({source: propsSource, isAuthTokenRequired = false, session, ...forwardedProps}: ImageProps) { +function Image({source: propsSource, isAuthTokenRequired = false, session, onLoad, objectPositionTop, style, ...forwardedProps}: ImageProps) { + const [aspectRatio, setAspectRatio] = useState(null); + + const updateAspectRatio = useCallback( + (width: number, height: number) => { + if (!objectPositionTop) { + return; + } + + if (width > height) { + setAspectRatio(1); + return; + } + + setAspectRatio(height ? width / height : 'auto'); + }, + [objectPositionTop], + ); + + const handleLoad = useCallback( + (event: ImageOnLoadEvent) => { + const {width, height} = event.nativeEvent; + + onLoad?.(event); + + updateAspectRatio(width, height); + }, + [onLoad, updateAspectRatio], + ); /** * Check if the image source is a URL - if so the `encryptedAuthToken` is appended * to the source. @@ -34,6 +62,8 @@ function Image({source: propsSource, isAuthTokenRequired = false, session, ...fo ); diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index 2a5fcbb19324..c2f73e74ee34 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -23,12 +23,12 @@ type BaseImageProps = { /** Event for when the image is fully loaded and returns the natural dimensions of the image */ onLoad?: (event: ImageOnLoadEvent) => void; -}; -type ImageOwnProps = BaseImageProps & { /** Styles for the Image */ style?: StyleProp; +}; +type ImageOwnProps = BaseImageProps & { /** Should an auth token be included in the image request */ isAuthTokenRequired?: boolean; @@ -46,6 +46,9 @@ type ImageOwnProps = BaseImageProps & { /** Progress events while the image is downloading */ onProgress?: () => void; + + /** Whether we should show the top of the image */ + objectPositionTop?: boolean; }; type ImageProps = ImageOnyxProps & ImageOwnProps; diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index 46e1308692ba..ffa9517299cc 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -33,7 +33,8 @@ type ImageWithSizeCalculationProps = { /** Whether the image requires an authToken */ isAuthTokenRequired: boolean; - objectPositionTop: boolean; + /** Whether we should show the top of the image */ + objectPositionTop?: boolean; }; /** @@ -42,7 +43,7 @@ type ImageWithSizeCalculationProps = { * performing some calculation on a network image after fetching dimensions so * it can be appropriately resized. */ -function ImageWithSizeCalculation({url, style, onMeasure, onLoadFailure, isAuthTokenRequired, objectPositionTop}: ImageWithSizeCalculationProps) { +function ImageWithSizeCalculation({url, style, onMeasure, onLoadFailure, isAuthTokenRequired, objectPositionTop = false}: ImageWithSizeCalculationProps) { const styles = useThemeStyles(); const isLoadedRef = useRef(null); const [isImageCached, setIsImageCached] = useState(true); From 5b1c8835707f9c7c8daf6ed6283a2e97f6d75707 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 21 Mar 2024 17:33:55 +0700 Subject: [PATCH 15/18] replace objectPositionTop with objectPosition --- src/CONST.ts | 5 +++++ src/components/DistanceEReceipt.tsx | 2 +- src/components/Image/index.tsx | 9 +++++---- src/components/Image/types.ts | 10 +++++++--- src/components/ImageWithSizeCalculation.tsx | 10 ++++++---- src/components/MoneyRequestConfirmationList.tsx | 2 +- ...MoneyTemporaryForRefactorRequestConfirmationList.js | 2 +- .../ReportActionItem/ReportActionItemImage.tsx | 4 ++-- src/components/ThumbnailImage.tsx | 10 +++++++--- 9 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index bb191ac5e028..3cd473f2f451 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1095,6 +1095,11 @@ const CONST = { JPEG: 'image/jpeg', }, + IMAGE_OBJECT_POSITION: { + TOP: 'top', + INITIAL: 'initial', + }, + FILE_TYPE_REGEX: { // Image MimeTypes allowed by iOS photos app. IMAGE: /\.(jpg|jpeg|png|webp|gif|tiff|bmp|heic|heif)$/, diff --git a/src/components/DistanceEReceipt.tsx b/src/components/DistanceEReceipt.tsx index 864dc831f74c..03ae1cdef5ad 100644 --- a/src/components/DistanceEReceipt.tsx +++ b/src/components/DistanceEReceipt.tsx @@ -63,7 +63,7 @@ function DistanceEReceipt({transaction}: DistanceEReceiptProps) { style={[styles.w100, styles.h100]} isAuthTokenRequired shouldDynamicallyResize={false} - objectPositionTop + objectPosition /> )} diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index c8c78b54de06..e6cecdc0d5ec 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -5,12 +5,13 @@ import ONYXKEYS from '@src/ONYXKEYS'; import BaseImage from './BaseImage'; import type {ImageOnLoadEvent, ImageOnyxProps, ImageOwnProps, ImageProps} from './types'; -function Image({source: propsSource, isAuthTokenRequired = false, session, onLoad, objectPositionTop, style, ...forwardedProps}: ImageProps) { +function Image({source: propsSource, isAuthTokenRequired = false, session, onLoad, objectPosition = CONST.IMAGE_OBJECT_POSITION.INITIAL, style, ...forwardedProps}: ImageProps) { const [aspectRatio, setAspectRatio] = useState(null); + const isObjectPositionTop = objectPosition === CONST.IMAGE_OBJECT_POSITION.TOP; const updateAspectRatio = useCallback( (width: number, height: number) => { - if (!objectPositionTop) { + if (!isObjectPositionTop) { return; } @@ -21,7 +22,7 @@ function Image({source: propsSource, isAuthTokenRequired = false, session, onLoa setAspectRatio(height ? width / height : 'auto'); }, - [objectPositionTop], + [isObjectPositionTop], ); const handleLoad = useCallback( @@ -63,7 +64,7 @@ function Image({source: propsSource, isAuthTokenRequired = false, session, onLoa // eslint-disable-next-line react/jsx-props-no-spreading {...forwardedProps} onLoad={handleLoad} - style={[style, aspectRatio ? {aspectRatio, height: 'auto'} : {}, objectPositionTop && !aspectRatio && {opacity: 0}]} + style={[style, aspectRatio ? {aspectRatio, height: 'auto'} : {}, isObjectPositionTop && !aspectRatio && {opacity: 0}]} source={source} /> ); diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index c2f73e74ee34..27964d8a6764 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -1,10 +1,14 @@ import type {ImageSource} from 'expo-image'; import type {ImageRequireSource, ImageResizeMode, ImageStyle, ImageURISource, StyleProp} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; import type {Session} from '@src/types/onyx'; type ExpoImageSource = ImageSource | number | ImageSource[]; +type ImageObjectPosition = ValueOf; + type ImageOnyxProps = { /** Session info for the currently logged in user. */ session: OnyxEntry; @@ -47,10 +51,10 @@ type ImageOwnProps = BaseImageProps & { /** Progress events while the image is downloading */ onProgress?: () => void; - /** Whether we should show the top of the image */ - objectPositionTop?: boolean; + /** The object position of image */ + objectPosition?: ImageObjectPosition; }; type ImageProps = ImageOnyxProps & ImageOwnProps; -export type {BaseImageProps, ImageOwnProps, ImageOnyxProps, ImageProps, ExpoImageSource, ImageOnLoadEvent}; +export type {BaseImageProps, ImageOwnProps, ImageOnyxProps, ImageProps, ExpoImageSource, ImageOnLoadEvent, ImageObjectPosition}; diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index ffa9517299cc..eac5c676370b 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -5,9 +5,11 @@ import {View} from 'react-native'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import Log from '@libs/Log'; +import CONST from '@src/CONST'; import FullscreenLoadingIndicator from './FullscreenLoadingIndicator'; import Image from './Image'; import RESIZE_MODES from './Image/resizeModes'; +import type {ImageObjectPosition} from './Image/types'; type OnMeasure = (args: {width: number; height: number}) => void; @@ -33,8 +35,8 @@ type ImageWithSizeCalculationProps = { /** Whether the image requires an authToken */ isAuthTokenRequired: boolean; - /** Whether we should show the top of the image */ - objectPositionTop?: boolean; + /** The object position of image */ + objectPosition?: ImageObjectPosition; }; /** @@ -43,7 +45,7 @@ type ImageWithSizeCalculationProps = { * performing some calculation on a network image after fetching dimensions so * it can be appropriately resized. */ -function ImageWithSizeCalculation({url, style, onMeasure, onLoadFailure, isAuthTokenRequired, objectPositionTop = false}: ImageWithSizeCalculationProps) { +function ImageWithSizeCalculation({url, style, onMeasure, onLoadFailure, isAuthTokenRequired, objectPosition = CONST.IMAGE_OBJECT_POSITION.INITIAL}: ImageWithSizeCalculationProps) { const styles = useThemeStyles(); const isLoadedRef = useRef(null); const [isImageCached, setIsImageCached] = useState(true); @@ -104,7 +106,7 @@ function ImageWithSizeCalculation({url, style, onMeasure, onLoadFailure, isAuthT }} onError={onError} onLoad={imageLoadedSuccessfully} - objectPositionTop={objectPositionTop} + objectPosition={objectPosition} /> {isLoading && !isImageCached && } diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index b2ea75cae8b0..75d156e58eaa 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -614,7 +614,7 @@ function MoneyRequestConfirmationList({ // but we don't need it to load the blob:// or file:// image when starting a money request / split bill // So if we have a thumbnail, it means we're retrieving the image from the server isAuthTokenRequired={!!receiptThumbnail} - objectPositionTop + objectPosition={CONST.IMAGE_OBJECT_POSITION.TOP} /> ) : ( // The empty receipt component should only show for IOU Requests of a paid policy ("Team" or "Corporate") diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 08957c96128a..c32241d92096 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -906,7 +906,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ // but we don't need it to load the blob:// or file:// image when starting a money request / split bill // So if we have a thumbnail, it means we're retrieving the image from the server isAuthTokenRequired={!_.isEmpty(receiptThumbnail)} - objectPositionTop + objectPosition={CONST.IMAGE_OBJECT_POSITION.TOP} /> ), [receiptFilename, receiptImage, styles, receiptThumbnail, isLocalFile, isAttachmentInvalid], diff --git a/src/components/ReportActionItem/ReportActionItemImage.tsx b/src/components/ReportActionItem/ReportActionItemImage.tsx index b39cabf4e74e..3c6f6e7e2421 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.tsx +++ b/src/components/ReportActionItem/ReportActionItemImage.tsx @@ -85,7 +85,7 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal = false, tr fallbackIcon={Expensicons.Receipt} fallbackIconSize={isSingleImage ? variables.iconSizeSuperLarge : variables.iconSizeExtraLarge} shouldDynamicallyResize={false} - objectPositionTop + objectPosition={CONST.IMAGE_OBJECT_POSITION.TOP} /> ); } else if (isLocalFile && filename && Str.isPDF(filename) && typeof attachmentModalSource === 'string') { @@ -100,7 +100,7 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal = false, tr ); } diff --git a/src/components/ThumbnailImage.tsx b/src/components/ThumbnailImage.tsx index d1bdfdf3ca9f..8b8da81c727a 100644 --- a/src/components/ThumbnailImage.tsx +++ b/src/components/ThumbnailImage.tsx @@ -6,9 +6,11 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useThumbnailDimensions from '@hooks/useThumbnailDimensions'; import variables from '@styles/variables'; +import CONST from '@src/CONST'; import type IconAsset from '@src/types/utils/IconAsset'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; +import type {ImageObjectPosition} from './Image/types'; import ImageWithSizeCalculation from './ImageWithSizeCalculation'; type ThumbnailImageProps = { @@ -35,7 +37,9 @@ type ThumbnailImageProps = { /** Should the image be resized on load or just fit container */ shouldDynamicallyResize?: boolean; - objectPositionTop?: boolean; + + /** The object position of image */ + objectPosition?: ImageObjectPosition; }; type UpdateImageSizeParams = { @@ -52,7 +56,7 @@ function ThumbnailImage({ shouldDynamicallyResize = true, fallbackIcon = Expensicons.Gallery, fallbackIconSize = variables.iconSizeSuperLarge, - objectPositionTop = false, + objectPosition = CONST.IMAGE_OBJECT_POSITION.INITIAL, }: ThumbnailImageProps) { const styles = useThemeStyles(); const theme = useTheme(); @@ -104,7 +108,7 @@ function ThumbnailImage({ onMeasure={updateImageSize} onLoadFailure={() => setFailedToLoad(true)} isAuthTokenRequired={isAuthTokenRequired} - objectPositionTop={objectPositionTop} + objectPosition={objectPosition} /> From 115ec923108600f1ad167783d5acb23417effe5c Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 21 Mar 2024 17:37:50 +0700 Subject: [PATCH 16/18] update objectPosition prop --- src/components/DistanceEReceipt.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/DistanceEReceipt.tsx b/src/components/DistanceEReceipt.tsx index 03ae1cdef5ad..3d2a6a3e57b6 100644 --- a/src/components/DistanceEReceipt.tsx +++ b/src/components/DistanceEReceipt.tsx @@ -18,6 +18,7 @@ import PendingMapView from './MapView/PendingMapView'; import ScrollView from './ScrollView'; import Text from './Text'; import ThumbnailImage from './ThumbnailImage'; +import CONST from '@src/CONST'; type DistanceEReceiptProps = { /** The transaction for the distance request */ @@ -63,7 +64,7 @@ function DistanceEReceipt({transaction}: DistanceEReceiptProps) { style={[styles.w100, styles.h100]} isAuthTokenRequired shouldDynamicallyResize={false} - objectPosition + objectPosition={CONST.IMAGE_OBJECT_POSITION.TOP} /> )} From 1562a88172728730d3ad58cad1abc0e1529f7a28 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 21 Mar 2024 18:05:42 +0700 Subject: [PATCH 17/18] fix order import --- src/components/DistanceEReceipt.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DistanceEReceipt.tsx b/src/components/DistanceEReceipt.tsx index 3d2a6a3e57b6..2d6e3e56210f 100644 --- a/src/components/DistanceEReceipt.tsx +++ b/src/components/DistanceEReceipt.tsx @@ -8,6 +8,7 @@ import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; +import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import type {Transaction} from '@src/types/onyx'; import type {WaypointCollection} from '@src/types/onyx/Transaction'; @@ -18,7 +19,6 @@ import PendingMapView from './MapView/PendingMapView'; import ScrollView from './ScrollView'; import Text from './Text'; import ThumbnailImage from './ThumbnailImage'; -import CONST from '@src/CONST'; type DistanceEReceiptProps = { /** The transaction for the distance request */ From 3e61229cc03c38e5e6c13b7328ee097ffb057a49 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 25 Mar 2024 15:59:38 +0700 Subject: [PATCH 18/18] fix offline case --- .../ReportActionItem/ReportActionItemImage.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItem/ReportActionItemImage.tsx b/src/components/ReportActionItem/ReportActionItemImage.tsx index 3c6f6e7e2421..67efc75f4c24 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.tsx +++ b/src/components/ReportActionItem/ReportActionItemImage.tsx @@ -6,7 +6,6 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import EReceiptThumbnail from '@components/EReceiptThumbnail'; import * as Expensicons from '@components/Icon/Expensicons'; -import Image from '@components/Image'; import PDFThumbnail from '@components/PDFThumbnail'; import PressableWithoutFocus from '@components/Pressable/PressableWithoutFocus'; import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; @@ -97,9 +96,13 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal = false, tr ); } else { receiptImageComponent = ( - );