Skip to content

Commit

Permalink
Add react-native-web patch for image header support
Browse files Browse the repository at this point in the history
  • Loading branch information
kidroca committed Nov 29, 2023
1 parent 2aa37d2 commit 19b605e
Showing 1 changed file with 200 additions and 0 deletions.
200 changes: 200 additions & 0 deletions patches/react-native-web+0.19.9+005+image-header-support.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
diff --git a/node_modules/react-native-web/dist/exports/Image/index.js b/node_modules/react-native-web/dist/exports/Image/index.js
index 95355d5..19109fc 100644
--- a/node_modules/react-native-web/dist/exports/Image/index.js
+++ b/node_modules/react-native-web/dist/exports/Image/index.js
@@ -135,7 +135,22 @@ function resolveAssetUri(source) {
}
return uri;
}
-var Image = /*#__PURE__*/React.forwardRef((props, ref) => {
+function raiseOnErrorEvent(uri, _ref) {
+ var onError = _ref.onError,
+ onLoadEnd = _ref.onLoadEnd;
+ if (onError) {
+ onError({
+ nativeEvent: {
+ error: "Failed to load resource " + uri + " (404)"
+ }
+ });
+ }
+ if (onLoadEnd) onLoadEnd();
+}
+function hasSourceDiff(a, b) {
+ return a.uri !== b.uri || JSON.stringify(a.headers) !== JSON.stringify(b.headers);
+}
+var BaseImage = /*#__PURE__*/React.forwardRef((props, ref) => {
var ariaLabel = props['aria-label'],
blurRadius = props.blurRadius,
defaultSource = props.defaultSource,
@@ -236,16 +251,10 @@ var Image = /*#__PURE__*/React.forwardRef((props, ref) => {
}
}, function error() {
updateState(ERRORED);
- if (onError) {
- onError({
- nativeEvent: {
- error: "Failed to load resource " + uri + " (404)"
- }
- });
- }
- if (onLoadEnd) {
- onLoadEnd();
- }
+ raiseOnErrorEvent(uri, {
+ onError,
+ onLoadEnd
+ });
});
}
function abortPendingRequest() {
@@ -277,10 +286,78 @@ var Image = /*#__PURE__*/React.forwardRef((props, ref) => {
suppressHydrationWarning: true
}), hiddenImage, createTintColorSVG(tintColor, filterRef.current));
});
-Image.displayName = 'Image';
+BaseImage.displayName = 'Image';
+
+/**
+ * This component handles specifically loading an image source with headers
+ * default source is never loaded using headers
+ */
+var ImageWithHeaders = /*#__PURE__*/React.forwardRef((props, ref) => {
+ // $FlowIgnore: This component would only be rendered when `source` matches `ImageSource`
+ var nextSource = props.source;
+ var _React$useState3 = React.useState(''),
+ blobUri = _React$useState3[0],
+ setBlobUri = _React$useState3[1];
+ var request = React.useRef({
+ cancel: () => {},
+ source: {
+ uri: '',
+ headers: {}
+ },
+ promise: Promise.resolve('')
+ });
+ var onError = props.onError,
+ onLoadStart = props.onLoadStart,
+ onLoadEnd = props.onLoadEnd;
+ React.useEffect(() => {
+ if (!hasSourceDiff(nextSource, request.current.source)) {
+ return;
+ }
+
+ // When source changes we want to clean up any old/running requests
+ request.current.cancel();
+ if (onLoadStart) {
+ onLoadStart();
+ }
+
+ // Store a ref for the current load request so we know what's the last loaded source,
+ // and so we can cancel it if a different source is passed through props
+ request.current = ImageLoader.loadWithHeaders(nextSource);
+ request.current.promise.then(uri => setBlobUri(uri)).catch(() => raiseOnErrorEvent(request.current.source.uri, {
+ onError,
+ onLoadEnd
+ }));
+ }, [nextSource, onLoadStart, onError, onLoadEnd]);
+
+ // Cancel any request on unmount
+ React.useEffect(() => request.current.cancel, []);
+ var propsToPass = _objectSpread(_objectSpread({}, props), {}, {
+ // `onLoadStart` is called from the current component
+ // We skip passing it down to prevent BaseImage raising it a 2nd time
+ onLoadStart: undefined,
+ // Until the current component resolves the request (using headers)
+ // we skip forwarding the source so the base component doesn't attempt
+ // to load the original source
+ source: blobUri ? _objectSpread(_objectSpread({}, nextSource), {}, {
+ uri: blobUri
+ }) : undefined
+ });
+ return /*#__PURE__*/React.createElement(BaseImage, _extends({
+ ref: ref
+ }, propsToPass));
+});

// $FlowIgnore: This is the correct type, but casting makes it unhappy since the variables aren't defined yet
-var ImageWithStatics = Image;
+var ImageWithStatics = /*#__PURE__*/React.forwardRef((props, ref) => {
+ if (props.source && props.source.headers) {
+ return /*#__PURE__*/React.createElement(ImageWithHeaders, _extends({
+ ref: ref
+ }, props));
+ }
+ return /*#__PURE__*/React.createElement(BaseImage, _extends({
+ ref: ref
+ }, props));
+});
ImageWithStatics.getSize = function (uri, success, failure) {
ImageLoader.getSize(uri, success, failure);
};
diff --git a/node_modules/react-native-web/dist/modules/ImageLoader/index.js b/node_modules/react-native-web/dist/modules/ImageLoader/index.js
index bc06a87..e309394 100644
--- a/node_modules/react-native-web/dist/modules/ImageLoader/index.js
+++ b/node_modules/react-native-web/dist/modules/ImageLoader/index.js
@@ -76,7 +76,7 @@ var ImageLoader = {
var image = requests["" + requestId];
if (image) {
var naturalHeight = image.naturalHeight,
- naturalWidth = image.naturalWidth;
+ naturalWidth = image.naturalWidth;
if (naturalHeight && naturalWidth) {
success(naturalWidth, naturalHeight);
complete = true;
@@ -102,11 +102,19 @@ var ImageLoader = {
id += 1;
var image = new window.Image();
image.onerror = onError;
- image.onload = e => {
+ image.onload = nativeEvent => {
// avoid blocking the main thread
- var onDecode = () => onLoad({
- nativeEvent: e
- });
+ var onDecode = () => {
+ // Append `source` to match RN's ImageLoadEvent interface
+ nativeEvent.source = {
+ uri: image.src,
+ width: image.naturalWidth,
+ height: image.naturalHeight
+ };
+ onLoad({
+ nativeEvent
+ });
+ };
if (typeof image.decode === 'function') {
// Safari currently throws exceptions when decoding svgs.
// We want to catch that error and allow the load handler
@@ -120,6 +128,32 @@ var ImageLoader = {
requests["" + id] = image;
return id;
},
+ loadWithHeaders(source) {
+ var uri;
+ var abortController = new AbortController();
+ var request = new Request(source.uri, {
+ headers: source.headers,
+ signal: abortController.signal
+ });
+ request.headers.append('accept', 'image/*');
+ var promise = fetch(request).then(response => response.blob()).then(blob => {
+ uri = URL.createObjectURL(blob);
+ return uri;
+ }).catch(error => {
+ if (error.name === 'AbortError') {
+ return '';
+ }
+ throw error;
+ });
+ return {
+ promise,
+ source,
+ cancel: () => {
+ abortController.abort();
+ URL.revokeObjectURL(uri);
+ }
+ };
+ },
prefetch(uri) {
return new Promise((resolve, reject) => {
ImageLoader.load(uri, () => {

0 comments on commit 19b605e

Please sign in to comment.