Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
feb57ae
fix: 34120
dukenv0307 Jan 11, 2024
0eeedc9
fix: redundant
dukenv0307 Jan 11, 2024
8e3d0fc
Merge branch 'main' into fix/34120
dukenv0307 Jan 24, 2024
5e5adb8
fix lint
dukenv0307 Jan 24, 2024
2e260e6
fix lint
dukenv0307 Jan 24, 2024
7fce375
Merge branch 'main' into fix/34120
dukenv0307 Jan 26, 2024
6939350
fix crash bug
dukenv0307 Jan 26, 2024
7c9c40f
fix lint
dukenv0307 Jan 26, 2024
68ffee4
Merge branch 'main' into fix/34120
dukenv0307 Jan 26, 2024
a153e8a
fix lint
dukenv0307 Jan 26, 2024
0103a6e
Merge branch 'main' into fix/34120
dukenv0307 Jan 30, 2024
2125302
fix the case wide image
dukenv0307 Jan 30, 2024
00ccc9b
add comment prop
dukenv0307 Jan 30, 2024
bac3d4d
Merge branch 'main' into fix/34120
dukenv0307 Jan 30, 2024
c761e1c
merge main
dukenv0307 Jan 30, 2024
13acc33
resolve conflict
dukenv0307 Feb 1, 2024
d09ee20
centralize logic
dukenv0307 Feb 2, 2024
354c9bc
Merge branch 'main' into fix/34120
dukenv0307 Feb 2, 2024
7cf8fd8
fix lint
dukenv0307 Feb 2, 2024
9b30a3d
merge main and optimize logic
dukenv0307 Feb 5, 2024
86190f6
revert hard code
dukenv0307 Feb 5, 2024
fd26a31
resolve conflict
dukenv0307 Feb 19, 2024
ba5051d
merge main
dukenv0307 Feb 23, 2024
3d63a64
merge main
dukenv0307 Mar 20, 2024
54d0bb9
fix lint
dukenv0307 Mar 20, 2024
5b1c883
replace objectPositionTop with objectPosition
dukenv0307 Mar 21, 2024
115ec92
update objectPosition prop
dukenv0307 Mar 21, 2024
1562a88
fix order import
dukenv0307 Mar 21, 2024
fcdcb65
Merge branch 'main' into fix/34120
dukenv0307 Mar 22, 2024
4839282
Merge branch 'main' into fix/34120
dukenv0307 Mar 25, 2024
3e61229
fix offline case
dukenv0307 Mar 25, 2024
aa3247f
merge main
dukenv0307 Mar 26, 2024
e4532de
resolve conflict
dukenv0307 Mar 27, 2024
0b8f6d2
Merge branch 'main' into fix/34120
dukenv0307 Mar 29, 2024
681aae8
Merge branch 'main' into fix/34120
dukenv0307 Apr 1, 2024
2ab8b38
Merge branch 'main' into fix/34120
dukenv0307 Apr 2, 2024
67a43d7
Merge branch 'main' into fix/34120
dukenv0307 Apr 17, 2024
7a748f5
Merge branch 'main' into fix/34120
dukenv0307 Apr 22, 2024
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
5 changes: 5 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,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)$/,
Expand Down
37 changes: 34 additions & 3 deletions src/components/Image/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,40 @@
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, 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 updateAspectRatio = useCallback(
(width: number, height: number) => {
if (!isObjectPositionTop) {
return;
}

if (width > height) {
setAspectRatio(1);
return;
}

setAspectRatio(height ? width / height : 'auto');
},
[isObjectPositionTop],
);

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.
Expand Down Expand Up @@ -34,6 +63,8 @@ function Image({source: propsSource, isAuthTokenRequired = false, session, ...fo
<BaseImage
// eslint-disable-next-line react/jsx-props-no-spreading
{...forwardedProps}
onLoad={handleLoad}
style={[style, aspectRatio ? {aspectRatio, height: 'auto'} : {}, isObjectPositionTop && !aspectRatio && {opacity: 0}]}
source={source}
/>
);
Expand Down
13 changes: 10 additions & 3 deletions src/components/Image/types.ts
Original file line number Diff line number Diff line change
@@ -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<typeof CONST.IMAGE_OBJECT_POSITION>;

type ImageOnyxProps = {
/** Session info for the currently logged in user. */
session: OnyxEntry<Session>;
Expand All @@ -23,12 +27,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<ImageStyle>;
};

type ImageOwnProps = BaseImageProps & {
/** Should an auth token be included in the image request */
isAuthTokenRequired?: boolean;

Expand All @@ -46,8 +50,11 @@ type ImageOwnProps = BaseImageProps & {

/** Progress events while the image is downloading */
onProgress?: () => void;

/** 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};
8 changes: 7 additions & 1 deletion src/components/ImageWithSizeCalculation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -32,6 +34,9 @@ type ImageWithSizeCalculationProps = {

/** Whether the image requires an authToken */
isAuthTokenRequired: boolean;

/** The object position of image */
objectPosition?: ImageObjectPosition;
};

/**
Expand All @@ -40,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}: ImageWithSizeCalculationProps) {
function ImageWithSizeCalculation({url, style, onMeasure, onLoadFailure, isAuthTokenRequired, objectPosition = CONST.IMAGE_OBJECT_POSITION.INITIAL}: ImageWithSizeCalculationProps) {
const styles = useThemeStyles();
const isLoadedRef = useRef<boolean | null>(null);
const [isImageCached, setIsImageCached] = useState(true);
Expand Down Expand Up @@ -101,6 +106,7 @@ function ImageWithSizeCalculation({url, style, onMeasure, onLoadFailure, isAuthT
}}
onError={onError}
onLoad={imageLoadedSuccessfully}
objectPosition={objectPosition}
/>
{isLoading && !isImageCached && <FullscreenLoadingIndicator style={[styles.opacity1, styles.bgTransparent]} />}
</View>
Expand Down
4 changes: 3 additions & 1 deletion src/components/ReceiptImage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import type IconAsset from '@src/types/utils/IconAsset';
import EReceiptThumbnail from './EReceiptThumbnail';
import type {IconSize} from './EReceiptThumbnail';
Expand Down Expand Up @@ -115,10 +116,11 @@ function ReceiptImage({
<ThumbnailImage
previewSourceURL={source ?? ''}
style={[styles.w100, styles.h100]}
isAuthTokenRequired
isAuthTokenRequired={isAuthTokenRequired ?? false}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you quickly comment on the changes related to the auth token? (this line, but also some other lines in the whole PR diff)

Does adjusting the thumbnail positioning have some relation with auth tokens? I'm trying to ensure we got conflict resolution right.

Copy link
Copy Markdown
Contributor Author

@dukenv0307 dukenv0307 Mar 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cubuspl42 As I mentioned here #35041 (comment), we can use the thumbnail to render the receipt when it's a local file to fix the issue here #35041 (comment). So I want to control isAuthTokenRequired of ThumnailImage via isAuthTokenRequired prop.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is also the reason for the change in ReportActionItemImage

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I forgot. This is definitely a sign we should wrap this up.

shouldDynamicallyResize={false}
fallbackIcon={fallbackIcon}
fallbackIconSize={fallbackIconSize}
objectPosition={CONST.IMAGE_OBJECT_POSITION.TOP}
/>
);
}
Expand Down
3 changes: 3 additions & 0 deletions src/components/ReportActionItem/ReportActionItemImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,16 @@ function ReportActionItemImage({
source: thumbnailSource,
fallbackIcon: Expensicons.Receipt,
fallbackIconSize: isSingleImage ? variables.iconSizeSuperLarge : variables.iconSizeExtraLarge,
isAuthTokenRequired: true,
};
} else if (isLocalFile && filename && Str.isPDF(filename) && typeof attachmentModalSource === 'string') {
propsObj = {isPDFThumbnail: true, source: attachmentModalSource};
} else {
propsObj = {
isThumbnail,
...(isThumbnail && {iconSize: (isSingleImage ? 'medium' : 'small') as IconSize, fileExtension}),
shouldUseThumbnailImage: true,
isAuthTokenRequired: false,
source: thumbnail ?? image ?? '',
};
}
Expand Down
7 changes: 7 additions & 0 deletions src/components/ThumbnailImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -35,6 +37,9 @@ type ThumbnailImageProps = {

/** Should the image be resized on load or just fit container */
shouldDynamicallyResize?: boolean;

/** The object position of image */
objectPosition?: ImageObjectPosition;
};

type UpdateImageSizeParams = {
Expand All @@ -51,6 +56,7 @@ function ThumbnailImage({
shouldDynamicallyResize = true,
fallbackIcon = Expensicons.Gallery,
fallbackIconSize = variables.iconSizeSuperLarge,
objectPosition = CONST.IMAGE_OBJECT_POSITION.INITIAL,
}: ThumbnailImageProps) {
const styles = useThemeStyles();
const theme = useTheme();
Expand Down Expand Up @@ -102,6 +108,7 @@ function ThumbnailImage({
onMeasure={updateImageSize}
onLoadFailure={() => setFailedToLoad(true)}
isAuthTokenRequired={isAuthTokenRequired}
objectPosition={objectPosition}
/>
</View>
</View>
Expand Down