Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: customize size behavior of image component #42469

Merged
merged 7 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/components/Image/ImageBehaviorContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -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<ImageBehaviorContextValue>({
shouldSetAspectRatioInStyle: true,
});

function ImageBehaviorContextProvider({children, ...value}: {children: React.ReactNode} & ImageBehaviorContextValue) {
return <ImageBehaviorContext.Provider value={value}>{children}</ImageBehaviorContext.Provider>;
}

export {ImageBehaviorContext, ImageBehaviorContextProvider};
13 changes: 10 additions & 3 deletions src/components/Image/index.tsx
Original file line number Diff line number Diff line change
@@ -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<string | number | null>(null);
const isObjectPositionTop = objectPosition === CONST.IMAGE_OBJECT_POSITION.TOP;

const {shouldSetAspectRatioInStyle} = useContext(ImageBehaviorContext);

const updateAspectRatio = useCallback(
(width: number, height: number) => {
if (!isObjectPositionTop) {
Expand All @@ -30,7 +33,6 @@ function Image({source: propsSource, isAuthTokenRequired = false, session, onLoa
const {width, height} = event.nativeEvent;

onLoad?.(event);

updateAspectRatio(width, height);
},
[onLoad, updateAspectRatio],
Expand Down Expand Up @@ -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;

dominictb marked this conversation as resolved.
Show resolved Hide resolved
return (
<BaseImage
// eslint-disable-next-line react/jsx-props-no-spreading
{...forwardedProps}
onLoad={handleLoad}
style={[style, aspectRatio ? {aspectRatio, height: 'auto'} : {}, isObjectPositionTop && !aspectRatio && {opacity: 0}]}
style={[style, shouldSetAspectRatioInStyle && aspectRatio ? {aspectRatio, height: 'auto'} : {}, shouldOpacityBeZero && {opacity: 0}]}
source={source}
/>
);
Expand Down
47 changes: 25 additions & 22 deletions src/components/ReportActionItem/ReportActionItemImages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -65,28 +66,30 @@ function ReportActionItemImages({images, size, total, isHovered = false}: Report
return (
<View style={styles.reportActionItemImagesContainer}>
<View style={[styles.reportActionItemImages, hoverStyle, heightStyle]}>
{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 (
<View
key={`${index}-${image}`}
style={[styles.reportActionItemImage, borderStyle, hoverStyle]}
>
<ReportActionItemImage
thumbnail={thumbnail}
fileExtension={fileExtension}
image={image}
isLocalFile={isLocalFile}
filename={filename}
transaction={transaction}
isThumbnail={isThumbnail}
isSingleImage={numberOfShownImages === 1}
/>
</View>
);
})}
<ImageBehaviorContextProvider shouldSetAspectRatioInStyle={false}>
{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 (
<View
key={`${index}-${image}`}
style={[styles.reportActionItemImage, borderStyle, hoverStyle]}
>
<ReportActionItemImage
thumbnail={thumbnail}
fileExtension={fileExtension}
image={image}
isLocalFile={isLocalFile}
filename={filename}
transaction={transaction}
isThumbnail={isThumbnail}
isSingleImage={numberOfShownImages === 1}
/>
</View>
);
})}
</ImageBehaviorContextProvider>
</View>
{remaining > 0 && (
<View style={[styles.reportActionItemImagesMoreContainer]}>
Expand Down
Loading