Skip to content

Commit

Permalink
Refactor Image for Web/Desktop with Source Headers
Browse files Browse the repository at this point in the history
- Introduced `BaseImage` component that branches between native and web implementations.
    - **Native**: Utilizes `FastImage` directly.
    - **Web**: Minor adjustments made to the `onLoad` event signature for compatibility.
- Eliminated `Image/index.native.js` as both native and web components now leverage a unified high-level implementation for image loading and rendering.
  • Loading branch information
kidroca committed Nov 29, 2023
1 parent 3624964 commit 2aa37d2
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 96 deletions.
28 changes: 28 additions & 0 deletions src/components/Image/BaseImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, {useCallback} from 'react';
import {Image as RNImage} from 'react-native';
import {defaultProps, imagePropTypes} from './imagePropTypes';

function BaseImage({onLoad, ...props}) {
const imageLoadedSuccessfully = useCallback(
({nativeEvent}) => {
// We override `onLoad`, so both web and native have the same signature
const {width, height} = nativeEvent.source;
onLoad({nativeEvent: {width, height}});
},
[onLoad],
);

return (
<RNImage
// Only subscribe to onLoad when a handler is provided to avoid unnecessary event registrations, optimizing performance.
onLoad={onLoad ? imageLoadedSuccessfully : undefined}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
);
}

BaseImage.propTypes = imagePropTypes;
BaseImage.defaultProps = defaultProps;

export default BaseImage;
3 changes: 3 additions & 0 deletions src/components/Image/BaseImage.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import RNFastImage from 'react-native-fast-image';

export default RNFastImage;
52 changes: 19 additions & 33 deletions src/components/Image/index.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,37 @@
import lodashGet from 'lodash/get';
import React, {useEffect, useMemo} from 'react';
import {Image as RNImage} from 'react-native';
import React, {useMemo} from 'react';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import BaseImage from './BaseImage';
import {defaultProps, imagePropTypes} from './imagePropTypes';
import RESIZE_MODES from './resizeModes';

function Image(props) {
const {source: propsSource, isAuthTokenRequired, onLoad, session} = props;
/**
* Check if the image source is a URL - if so the `encryptedAuthToken` is appended
* to the source.
*/
const {source: propsSource, isAuthTokenRequired, session, ...forwardedProps} = props;

// Update the source to include the auth token if required
const source = useMemo(() => {
if (isAuthTokenRequired) {
// There is currently a `react-native-web` bug preventing the authToken being passed
// in the headers of the image request so the authToken is added as a query param.
// On native the authToken IS passed in the image request headers
const authToken = lodashGet(session, 'encryptedAuthToken', null);
return {uri: `${propsSource.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`};
if (typeof lodashGet(propsSource, 'uri') === 'number') {
return propsSource.uri;
}
if (typeof propsSource !== 'number' && isAuthTokenRequired) {
const authToken = lodashGet(session, 'encryptedAuthToken');
return {
...propsSource,
headers: {
[CONST.CHAT_ATTACHMENT_TOKEN_KEY]: authToken,
},
};
}

return propsSource;
// The session prop is not required, as it causes the image to reload whenever the session changes. For more information, please refer to issue #26034.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [propsSource, isAuthTokenRequired]);

/**
* The natural image dimensions are retrieved using the updated source
* and as a result the `onLoad` event needs to be manually invoked to return these dimensions
*/
useEffect(() => {
// If an onLoad callback was specified then manually call it and pass
// the natural image dimensions to match the native API
if (onLoad == null) {
return;
}
RNImage.getSize(source.uri, (width, height) => {
onLoad({nativeEvent: {width, height}});
});
}, [onLoad, source]);

// Omit the props which the underlying RNImage won't use
const forwardedProps = _.omit(props, ['source', 'onLoad', 'session', 'isAuthTokenRequired']);

return (
<RNImage
<BaseImage
// eslint-disable-next-line react/jsx-props-no-spreading
{...forwardedProps}
source={source}
Expand Down
63 changes: 0 additions & 63 deletions src/components/Image/index.native.js

This file was deleted.

0 comments on commit 2aa37d2

Please sign in to comment.