diff --git a/src/components/Image/ImageBehaviorContextProvider.tsx b/src/components/Image/ImageBehaviorContextProvider.tsx new file mode 100644 index 000000000000..cfff212c16f6 --- /dev/null +++ b/src/components/Image/ImageBehaviorContextProvider.tsx @@ -0,0 +1,18 @@ +import React, {createContext} from 'react'; + +type ImageBehaviorContextValue = { + /** + * Determine whether or not to set the aspect ratio of the container div based on the image's aspect ratio. + */ + shouldSetAspectRatioInStyle: boolean; +}; + +const ImageBehaviorContext = createContext({ + shouldSetAspectRatioInStyle: true, +}); + +function ImageBehaviorContextProvider({children, ...value}: {children: React.ReactNode} & ImageBehaviorContextValue) { + return {children}; +} + +export {ImageBehaviorContext, ImageBehaviorContextProvider}; diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index e6cecdc0d5ec..f3cbc332c995 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -1,14 +1,17 @@ -import React, {useCallback, useMemo, useState} from 'react'; +import React, {useCallback, useContext, 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 {ImageBehaviorContext} from './ImageBehaviorContextProvider'; import type {ImageOnLoadEvent, ImageOnyxProps, ImageOwnProps, ImageProps} from './types'; 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 {shouldSetAspectRatioInStyle} = useContext(ImageBehaviorContext); + const updateAspectRatio = useCallback( (width: number, height: number) => { if (!isObjectPositionTop) { @@ -30,7 +33,6 @@ function Image({source: propsSource, isAuthTokenRequired = false, session, onLoa const {width, height} = event.nativeEvent; onLoad?.(event); - updateAspectRatio(width, height); }, [onLoad, updateAspectRatio], @@ -59,12 +61,17 @@ function Image({source: propsSource, isAuthTokenRequired = false, session, onLoa // eslint-disable-next-line react-hooks/exhaustive-deps }, [propsSource, isAuthTokenRequired]); + /** + * If the image fails to load and the object position is top, we should hide the image by setting the opacity to 0. + */ + const shouldOpacityBeZero = isObjectPositionTop && !aspectRatio; + return ( ); diff --git a/src/components/ReportActionItem/ReportActionItemImages.tsx b/src/components/ReportActionItem/ReportActionItemImages.tsx index e2bcce9b9f1b..5e3a26d3a870 100644 --- a/src/components/ReportActionItem/ReportActionItemImages.tsx +++ b/src/components/ReportActionItem/ReportActionItemImages.tsx @@ -2,6 +2,7 @@ import React from 'react'; import {View} from 'react-native'; import {Polygon, Svg} from 'react-native-svg'; +import {ImageBehaviorContextProvider} from '@components/Image/ImageBehaviorContextProvider'; import Text from '@components/Text'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -65,28 +66,30 @@ function ReportActionItemImages({images, size, total, isHovered = false}: Report return ( - {shownImages.map(({thumbnail, isThumbnail, image, transaction, isLocalFile, fileExtension, filename}, index) => { - // Show a border to separate multiple images. Shown to the right for each except the last. - const shouldShowBorder = shownImages.length > 1 && index < shownImages.length - 1; - const borderStyle = shouldShowBorder ? styles.reportActionItemImageBorder : {}; - return ( - - - - ); - })} + + {shownImages.map(({thumbnail, isThumbnail, image, transaction, isLocalFile, fileExtension, filename}, index) => { + // Show a border to separate multiple images. Shown to the right for each except the last. + const shouldShowBorder = shownImages.length > 1 && index < shownImages.length - 1; + const borderStyle = shouldShowBorder ? styles.reportActionItemImageBorder : {}; + return ( + + + + ); + })} + {remaining > 0 && (