-
Notifications
You must be signed in to change notification settings - Fork 14
[add] Image source headers handling #11
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
Changes from 3 commits
059ab04
4e6daca
8f4d952
1e54c64
93df02a
7353183
bb25c15
1f393c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -8,7 +8,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||
* @flow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
import type { ImageProps } from './types'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
import type { ImageProps, SourceObject } from './types'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
import * as React from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
import createElement from '../createElement'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -146,6 +146,12 @@ function resolveAssetUri(source): ?string { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
return uri; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
function hasSourceDiff(a: SourceObject, b: SourceObject) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
a.uri !== b.uri || JSON.stringify(a.headers) !== JSON.stringify(b.headers) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
Beamanator marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
interface ImageStatics { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
getSize: ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
uri: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -158,10 +164,12 @@ interface ImageStatics { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
) => Promise<{| [uri: string]: 'disk/memory' |}>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
const Image: React.AbstractComponent< | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
type ImageComponent = React.AbstractComponent< | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
ImageProps, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
React.ElementRef<typeof View> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
> = React.forwardRef((props, ref) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
const BaseImage: ImageComponent = React.forwardRef((props, ref) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
accessibilityLabel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
blurRadius, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -332,24 +340,94 @@ const Image: React.AbstractComponent< | |||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
Image.displayName = 'Image'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* This component handles specifically loading an image source with header | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
kidroca marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
const ImageWithHeaders: ImageComponent = React.forwardRef((props, ref) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// $FlowIgnore | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
const nextSource: SourceObject = props.source; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
const prevSource = React.useRef<SourceObject>({}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
const cleanup = React.useRef<Function>(() => {}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
const [blobUri, setBlobUri] = React.useState(''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { onError, onLoadStart } = props; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
React.useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (!hasSourceDiff(nextSource, prevSource.current)) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
// When source changes we want to clean up any old/running requests | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
cleanup.current(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
prevSource.current = nextSource; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
let uri; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
const abortCtrl = new AbortController(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
const request = new Request(nextSource.uri, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
headers: nextSource.headers, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
signal: abortCtrl.signal | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
request.headers.append('accept', 'image/*'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (onLoadStart) onLoadStart(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
kidroca marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
fetch(request) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
.then((response) => response.blob()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
.then((blob) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
uri = URL.createObjectURL(blob); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
setBlobUri(uri); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
.catch((error) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (error.name !== 'AbortError' && onError) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
onError({ nativeEvent: error.message }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've tried to write some unit tests covering the new functionality, but because of this logic a few mocks need to be setup and make the PR bigger than it has to be I think it might be better to move this logic to It's easy to manually verify the I've made a similar comment on the main repo: https://github.com/necolas/react-native-web/pull/2442/files#r1072080652 What do you think, should we refactor / write some tests now or let the mainstream repo request those changes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm since this logic is the real meat & potatoes of the react-native-web/packages/react-native-web/src/exports/Image/index.js Lines 269 to 294 in bd1f7b8
If that's what you're thinking, I honestly do think that logic is cleaner, even without needing to write tests for Expensify's case so I'd say the refactor sounds like a nice idea There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, the idea is purely to ease mocking but I'll give your suggestion a try Last PR tried to solve everything inside the same component but that resulted in more logic What a component like BTW there's feedback on the mainstream PR: necolas#2442 (comment) that we should write tests and thumbs up for extracting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've tried to remove the But it results in a similar amount of changes and modifies some of the original logic (and seems harder to review)
We might merge There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for doing that refactor! Personally I prefer the new changes because it keeps all the image loading logic in I'd say let's move forward with this last refactor, as I think it's pretty straightforward compared to passing / not passing specific props to the BaseImage component required in this PR There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO the refactor is more of a proof that there are more "gymnastics" necessary in order to make this work by changing the original logic inside Image component Original logic is changed, the cleanup logic is different, IMO the mainstream maintainer already saw the update and is fine with just moving the There's also a big rework planned for the Image and the original loading logic would change, it would probably be best to not write stuff that depend on it (in the alt branch There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me update the current PR with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah this is one additional reason I really like this approach, even though there may be some additional changes in the upstream repo needed that we don't need at the moment in this fork 👍
That sounds perfect, thanks so much @kidroca 👍 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Capture a cleanup function for the current request | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// The reason for using a Ref is to avoid making this function a dependency | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Because the change of a dependency would otherwise would re-trigger a hook | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
kidroca marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
cleanup.current = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
abortCtrl.abort(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
setBlobUri(''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
URL.revokeObjectURL(uri); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
Beamanator marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, [nextSource, onLoadStart, onError]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Run the cleanup function on unmount | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
React.useEffect(() => cleanup.current, []); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
const propsToPass = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
...props, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Omit `onLoadStart` because we trigger it in the current scope | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not too sure what this comment means. Is there a different way to say this? I think it's something like - the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's more like: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that I look at it I see it's confusing - it's like Alex said - loading starts inside There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok cool I think we are all saying the same thing -
kidroca marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
onLoadStart: undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Until the current component resolves the request (using headers) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
kidroca marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
// we skip forwarding the source so the base component doesn't attempt | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// to load the original source | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
source: blobUri ? { ...nextSource, uri: blobUri } : undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
return <BaseImage ref={ref} {...propsToPass} />; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
// $FlowIgnore: This is the correct type, but casting makes it unhappy since the variables aren't defined yet | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
const ImageWithStatics = (Image: React.AbstractComponent< | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
ImageProps, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
React.ElementRef<typeof View> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
> & | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
ImageStatics); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
const Image: ImageComponent & ImageStatics = React.forwardRef((props, ref) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (props.source && props.source.headers) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return <ImageWithHeaders ref={ref} {...props} />; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
return <BaseImage ref={ref} {...props} />; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
Image.displayName = 'Image'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
ImageWithStatics.getSize = function (uri, success, failure) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
Image.getSize = function (uri, success, failure) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
ImageLoader.getSize(uri, success, failure); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
ImageWithStatics.prefetch = function (uri) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
Image.prefetch = function (uri) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ImageLoader.prefetch(uri); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
ImageWithStatics.queryCache = function (uris) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
Image.queryCache = function (uris) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ImageLoader.queryCache(uris); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -405,4 +483,4 @@ const resizeModeStyles = StyleSheet.create({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
export default ImageWithStatics; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
export default Image; |
Uh oh!
There was an error while loading. Please reload this page.