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

Image: support ImageSource with headers #2442

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

kidroca
Copy link

@kidroca kidroca commented Nov 25, 2022

Details

Extend ImageLoader functionality to be able to work with image sources containing headers

We preserve the existing strategy that works with image.src for cases where source
is just an uri with no headers

When source contain headers we make a fetch request and then render a local url for the
downloaded blob (URL.createObjectURL)

Fixed Issues

Test Strategy

  1. Verify existing Image functionality has no regressions

    • build web and examples: npm run dev -w react-native-web and npm run dev -w react-native-web-examples
    • open the examples page and go to Image: http://localhost:3000/image
    • see images are loading
    • take a screenshot and do the same from the master branch. You can switch back and forth and verify the image are loading the same way
  2. Verify Images with headers can be loaded

    • build web and examples: npm run dev -w react-native-web and npm run dev -w react-native-web-examples
    • open the examples page and go to Image: http://localhost:3000/image
    • modify sourceWithHeaders here packages/react-native-web-examples/pages/image/index.js and try to load images from a server that expects a GET request with a header
    • verify the image is loading on the examples page (near the bottom, labeled: "With Headers")
Example Project Screenshot

image

The code is also tested in a standalone project and it loads images with headers successfully

@codesandbox-ci
Copy link

codesandbox-ci bot commented Nov 25, 2022

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 616975e:

Sandbox Source
react-native-web-examples Configuration

Copy link
Author

@kidroca kidroca left a comment

Choose a reason for hiding this comment

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

Leaving some notes on the code changes

There's a breaking change that might be introduced or reverted - it's regarding the onLoad prop and the parameter structure used - I've opened a thread on the relevant source code chunk

packages/react-native-web/src/modules/ImageLoader/index.js Outdated Show resolved Hide resolved
packages/react-native-web/src/modules/ImageLoader/index.js Outdated Show resolved Hide resolved
packages/react-native-web/src/modules/ImageLoader/index.js Outdated Show resolved Hide resolved
packages/react-native-web/src/exports/Image/index.js Outdated Show resolved Hide resolved
packages/react-native-web/src/exports/Image/index.js Outdated Show resolved Hide resolved
packages/react-native-web/src/exports/Image/index.js Outdated Show resolved Hide resolved
packages/react-native-web/src/modules/ImageLoader/index.js Outdated Show resolved Hide resolved
packages/react-native-web/src/modules/ImageLoader/index.js Outdated Show resolved Hide resolved
@necolas
Copy link
Owner

necolas commented Nov 28, 2022

It would have been a good idea to ask about this before writing the PR, as there are numerous other plans around Image.

There's this PR for crossOrigin support #2207

This milestone https://github.com/necolas/react-native-web/milestone/18

And a parallel effort to support W3C props on Image facebook/react-native#34424

@kidroca
Copy link
Author

kidroca commented Nov 29, 2022

Hi @necolas
I thought this PR would be a nice place to get the conversation going

This PR is following the original idea to support headers via XHR, though I've opted to use fetch since the minimal browser requirements allow it

Overall I think the changes here are compatible with #2207

A big chunk of the changes comes from added tests
And the change for handling source objects in hook dependencies, which is also a problem for #2207 - https://github.com/necolas/react-native-web/pull/2207/files#diff-7cb74a3a32d73857be80350ecd1ea131d256bd5af11d2000e4fc2d03c2230584R222-R223

The rest of the changes are small cleanups and moving existing logic - I can revert some of these namely resolveAssetUri to avoid conflicts with the other PR

If there are any problems, I'm happy to discuss and modify the PR

@necolas
Copy link
Owner

necolas commented Nov 30, 2022

Please resolve the conflicts with master

@kidroca
Copy link
Author

kidroca commented Nov 30, 2022

There are no conflicts with master right now

There would be conflicts for the PR that happens to be merged last due to the changes to resolveAssetUri I made

Since the other PR just moves the function without changing the logic, it might be easier to do the same after this PR is merged
Alternatively I can apply the change after the other PR is merged

@necolas
Copy link
Owner

necolas commented Nov 30, 2022

Here's what GitHub shows me on this PR

image

@necolas
Copy link
Owner

necolas commented Nov 30, 2022

If you could rebase/squash your 20 commits into just a few, that would help too. Thanks

Copy link
Author

@kidroca kidroca left a comment

Choose a reason for hiding this comment

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

Squashed existing commits to allow rebasing

@NilsBaumgartner1994

This comment was marked as off-topic.

@kidroca kidroca force-pushed the kidroca/feat/image-source-headers branch from 8324905 to 2d82778 Compare January 17, 2023 11:08
Copy link
Author

@kidroca kidroca left a comment

Choose a reason for hiding this comment

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

PR Updated using an alternative strategy to load images with headers resulting in less changes overall

When a source with headers is used, we use a (kind of) a HOC component that fetches the source and then passes a local uri created with URL.createObjectURL to the original Image component

Comment on lines +128 to +133
nativeEvent.source = {
uri: image.src,
width: image.naturalWidth,
height: image.naturalHeight
};
Copy link
Author

Choose a reason for hiding this comment

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

Attach source to nativeEvent so onLoad matches the RN Image interface https://reactnative.dev/docs/image#onload

packages/react-native-web/src/exports/Image/index.js Outdated Show resolved Hide resolved
@Beamanator
Copy link

@necolas any chance you or a fellow maintainer can check out these changes? We're fine to wait since, as you mentioned before, you have many updates planned - we're just hoping to get a possible timeline when this could get reviewed.

@necolas
Copy link
Owner

necolas commented Jan 18, 2023

It looks like the author is still working on the patch. It needs unit tests and the suggestion to make changes to make testing easier makes sense to me.

@necolas
Copy link
Owner

necolas commented Jan 30, 2023

What's next for this PR? The PR in the fork had no new unit tests and this one hasn't been updated for 2 weeks

@kidroca
Copy link
Author

kidroca commented Jan 31, 2023

I haven't synced my latest changes here, and I still need to write some unit tests
I'll post an update when I'm done

@kidroca kidroca force-pushed the kidroca/feat/image-source-headers branch 2 times, most recently from 8533f2d to 081216d Compare February 3, 2023 13:41
Copy link
Author

@kidroca kidroca left a comment

Choose a reason for hiding this comment

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

Ready for Review ✅

Comment on lines -109 to -108
jest.useFakeTimers();
ImageLoader.load = jest.fn().mockImplementation((_, onLoad, onError) => {
onLoad();
});
Copy link
Author

Choose a reason for hiding this comment

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

Fake timers no longer needed to pass this test

Copy link
Author

Choose a reason for hiding this comment

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

Actually the fake timers are breaking the (new) tests that use Promises

For some reason unless we remove all calls to jest.useFakeTimers() - return waitFor(() => ...) does not work

Comment on lines +150 to +178
function raiseOnErrorEvent(uri, { onError, onLoadEnd }) {
if (onError) {
onError({
nativeEvent: {
error: `Failed to load resource ${uri} (404)`
}
});
}
if (onLoadEnd) onLoadEnd();
}
Copy link
Author

Choose a reason for hiding this comment

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

This was extracted for reuse out of the original Image component hook
(We reuse this in case loading with headers fails)

Comment on lines +348 to +375
/**
* This component handles specifically loading an image source with headers
* default source is never loaded using headers
*/
const ImageWithHeaders: ImageComponent = React.forwardRef((props, ref) => {
// $FlowIgnore: This component would only be rendered when `source` matches `ImageSource`
const nextSource: ImageSource = props.source;
Copy link
Author

Choose a reason for hiding this comment

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

Perhaps there's a way to declare ImageWithHeaders as an Image component where the source prop is exactly type ImageSource, but I don't know exactly how

I guess I need to declare ImageWithHeaderProp type that spreads the default Image prop type but changes the source. Should I do that?

Comment on lines +105 to +107
export type ImageProps = {|
...$Exact<ViewProps>,
Copy link
Author

Choose a reason for hiding this comment

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

I had Flow errors after my changes
Following the error messages and suggested fixes led to these changes

Comment on lines -19 to +24
type ImageBackgroundProps = {
type ImageBackgroundProps = {|
...ImageProps,
imageRef?: any,
imageStyle?: $PropertyType<ImageProps, 'style'>,
style?: $PropertyType<ViewProps, 'style'>
};
|};
Copy link
Author

Choose a reason for hiding this comment

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

I had Flow errors after my changes
Following the error messages and suggested fixes led to these changes

@necolas necolas added this to the 0.20: Image milestone Mar 20, 2023
@necolas
Copy link
Owner

necolas commented Mar 29, 2023

Hi, please could you rebase this. Now that 0.19 is done, the 0.20 release will be focused on Image changes.

@kidroca kidroca force-pushed the kidroca/feat/image-source-headers branch from 081216d to 616975e Compare April 4, 2023 07:06
@kidroca
Copy link
Author

kidroca commented Apr 4, 2023

✅ Rebased

};

return <BaseImage ref={ref} {...propsToPass} />;
});

// $FlowIgnore: This is the correct type, but casting makes it unhappy since the variables aren't defined yet
Copy link
Owner

Choose a reason for hiding this comment

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

When this old $FlowIgnore is removed, there is a new type error:

Cannot assign React.forwardRef(...) to ImageWithStatics because property getSize is missing in AbstractComponent [1] but
exists in ImageStatics [2]. [incompatible-type]

Copy link
Author

Choose a reason for hiding this comment

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

I think I've seen the original error the comment references at some point
Could it be the error was fixed by a flow version update, but now we have this other error?

necolas pushed a commit that referenced this pull request Apr 10, 2023
Extend ImageLoader functionality to be able to work with image sources
containing headers

We preserve the existing strategy that works with image.src for cases
where source is just an uri with no headers

When sources contain headers we make a fetch request and then render a
local url for the downloaded blob (URL.createObjectURL)

Fix #1019
Close #2442
@necolas necolas added the has: pr Subject of a pull request label Apr 10, 2023
@necolas
Copy link
Owner

necolas commented Apr 10, 2023

This PR was merged into the 0.20-dev branch #2508

necolas pushed a commit that referenced this pull request Apr 10, 2023
Extend ImageLoader functionality to be able to work with image sources
containing headers

We preserve the existing strategy that works with image.src for cases
where source is just an uri with no headers

When sources contain headers we make a fetch request and then render a
local url for the downloaded blob (URL.createObjectURL)

Fix #1019
Fix #2268
Close #2442
necolas pushed a commit that referenced this pull request Apr 12, 2023
Extend ImageLoader functionality to be able to work with image sources
containing headers

We preserve the existing strategy that works with image.src for cases
where source is just an uri with no headers

When sources contain headers we make a fetch request and then render a
local url for the downloaded blob (URL.createObjectURL)

Fix #1019
Fix #2268
Close #2442
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has: pr Subject of a pull request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Image: support HTTP properties for 'source' objects
5 participants