Skip to content

Commit 53337f5

Browse files
committed
Add react-native-web patch for image header support
(cherry picked from commit 19b605e) (cherry picked from commit 37df3e3)
1 parent f52bb02 commit 53337f5

1 file changed

Lines changed: 200 additions & 0 deletions

File tree

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
diff --git a/node_modules/react-native-web/dist/exports/Image/index.js b/node_modules/react-native-web/dist/exports/Image/index.js
2+
index 95355d5..19109fc 100644
3+
--- a/node_modules/react-native-web/dist/exports/Image/index.js
4+
+++ b/node_modules/react-native-web/dist/exports/Image/index.js
5+
@@ -135,7 +135,22 @@ function resolveAssetUri(source) {
6+
}
7+
return uri;
8+
}
9+
-var Image = /*#__PURE__*/React.forwardRef((props, ref) => {
10+
+function raiseOnErrorEvent(uri, _ref) {
11+
+ var onError = _ref.onError,
12+
+ onLoadEnd = _ref.onLoadEnd;
13+
+ if (onError) {
14+
+ onError({
15+
+ nativeEvent: {
16+
+ error: "Failed to load resource " + uri + " (404)"
17+
+ }
18+
+ });
19+
+ }
20+
+ if (onLoadEnd) onLoadEnd();
21+
+}
22+
+function hasSourceDiff(a, b) {
23+
+ return a.uri !== b.uri || JSON.stringify(a.headers) !== JSON.stringify(b.headers);
24+
+}
25+
+var BaseImage = /*#__PURE__*/React.forwardRef((props, ref) => {
26+
var ariaLabel = props['aria-label'],
27+
blurRadius = props.blurRadius,
28+
defaultSource = props.defaultSource,
29+
@@ -236,16 +251,10 @@ var Image = /*#__PURE__*/React.forwardRef((props, ref) => {
30+
}
31+
}, function error() {
32+
updateState(ERRORED);
33+
- if (onError) {
34+
- onError({
35+
- nativeEvent: {
36+
- error: "Failed to load resource " + uri + " (404)"
37+
- }
38+
- });
39+
- }
40+
- if (onLoadEnd) {
41+
- onLoadEnd();
42+
- }
43+
+ raiseOnErrorEvent(uri, {
44+
+ onError,
45+
+ onLoadEnd
46+
+ });
47+
});
48+
}
49+
function abortPendingRequest() {
50+
@@ -277,10 +286,78 @@ var Image = /*#__PURE__*/React.forwardRef((props, ref) => {
51+
suppressHydrationWarning: true
52+
}), hiddenImage, createTintColorSVG(tintColor, filterRef.current));
53+
});
54+
-Image.displayName = 'Image';
55+
+BaseImage.displayName = 'Image';
56+
+
57+
+/**
58+
+ * This component handles specifically loading an image source with headers
59+
+ * default source is never loaded using headers
60+
+ */
61+
+var ImageWithHeaders = /*#__PURE__*/React.forwardRef((props, ref) => {
62+
+ // $FlowIgnore: This component would only be rendered when `source` matches `ImageSource`
63+
+ var nextSource = props.source;
64+
+ var _React$useState3 = React.useState(''),
65+
+ blobUri = _React$useState3[0],
66+
+ setBlobUri = _React$useState3[1];
67+
+ var request = React.useRef({
68+
+ cancel: () => {},
69+
+ source: {
70+
+ uri: '',
71+
+ headers: {}
72+
+ },
73+
+ promise: Promise.resolve('')
74+
+ });
75+
+ var onError = props.onError,
76+
+ onLoadStart = props.onLoadStart,
77+
+ onLoadEnd = props.onLoadEnd;
78+
+ React.useEffect(() => {
79+
+ if (!hasSourceDiff(nextSource, request.current.source)) {
80+
+ return;
81+
+ }
82+
+
83+
+ // When source changes we want to clean up any old/running requests
84+
+ request.current.cancel();
85+
+ if (onLoadStart) {
86+
+ onLoadStart();
87+
+ }
88+
+
89+
+ // Store a ref for the current load request so we know what's the last loaded source,
90+
+ // and so we can cancel it if a different source is passed through props
91+
+ request.current = ImageLoader.loadWithHeaders(nextSource);
92+
+ request.current.promise.then(uri => setBlobUri(uri)).catch(() => raiseOnErrorEvent(request.current.source.uri, {
93+
+ onError,
94+
+ onLoadEnd
95+
+ }));
96+
+ }, [nextSource, onLoadStart, onError, onLoadEnd]);
97+
+
98+
+ // Cancel any request on unmount
99+
+ React.useEffect(() => request.current.cancel, []);
100+
+ var propsToPass = _objectSpread(_objectSpread({}, props), {}, {
101+
+ // `onLoadStart` is called from the current component
102+
+ // We skip passing it down to prevent BaseImage raising it a 2nd time
103+
+ onLoadStart: undefined,
104+
+ // Until the current component resolves the request (using headers)
105+
+ // we skip forwarding the source so the base component doesn't attempt
106+
+ // to load the original source
107+
+ source: blobUri ? _objectSpread(_objectSpread({}, nextSource), {}, {
108+
+ uri: blobUri
109+
+ }) : undefined
110+
+ });
111+
+ return /*#__PURE__*/React.createElement(BaseImage, _extends({
112+
+ ref: ref
113+
+ }, propsToPass));
114+
+});
115+
116+
// $FlowIgnore: This is the correct type, but casting makes it unhappy since the variables aren't defined yet
117+
-var ImageWithStatics = Image;
118+
+var ImageWithStatics = /*#__PURE__*/React.forwardRef((props, ref) => {
119+
+ if (props.source && props.source.headers) {
120+
+ return /*#__PURE__*/React.createElement(ImageWithHeaders, _extends({
121+
+ ref: ref
122+
+ }, props));
123+
+ }
124+
+ return /*#__PURE__*/React.createElement(BaseImage, _extends({
125+
+ ref: ref
126+
+ }, props));
127+
+});
128+
ImageWithStatics.getSize = function (uri, success, failure) {
129+
ImageLoader.getSize(uri, success, failure);
130+
};
131+
diff --git a/node_modules/react-native-web/dist/modules/ImageLoader/index.js b/node_modules/react-native-web/dist/modules/ImageLoader/index.js
132+
index bc06a87..e309394 100644
133+
--- a/node_modules/react-native-web/dist/modules/ImageLoader/index.js
134+
+++ b/node_modules/react-native-web/dist/modules/ImageLoader/index.js
135+
@@ -76,7 +76,7 @@ var ImageLoader = {
136+
var image = requests["" + requestId];
137+
if (image) {
138+
var naturalHeight = image.naturalHeight,
139+
- naturalWidth = image.naturalWidth;
140+
+ naturalWidth = image.naturalWidth;
141+
if (naturalHeight && naturalWidth) {
142+
success(naturalWidth, naturalHeight);
143+
complete = true;
144+
@@ -102,11 +102,19 @@ var ImageLoader = {
145+
id += 1;
146+
var image = new window.Image();
147+
image.onerror = onError;
148+
- image.onload = e => {
149+
+ image.onload = nativeEvent => {
150+
// avoid blocking the main thread
151+
- var onDecode = () => onLoad({
152+
- nativeEvent: e
153+
- });
154+
+ var onDecode = () => {
155+
+ // Append `source` to match RN's ImageLoadEvent interface
156+
+ nativeEvent.source = {
157+
+ uri: image.src,
158+
+ width: image.naturalWidth,
159+
+ height: image.naturalHeight
160+
+ };
161+
+ onLoad({
162+
+ nativeEvent
163+
+ });
164+
+ };
165+
if (typeof image.decode === 'function') {
166+
// Safari currently throws exceptions when decoding svgs.
167+
// We want to catch that error and allow the load handler
168+
@@ -120,6 +128,32 @@ var ImageLoader = {
169+
requests["" + id] = image;
170+
return id;
171+
},
172+
+ loadWithHeaders(source) {
173+
+ var uri;
174+
+ var abortController = new AbortController();
175+
+ var request = new Request(source.uri, {
176+
+ headers: source.headers,
177+
+ signal: abortController.signal
178+
+ });
179+
+ request.headers.append('accept', 'image/*');
180+
+ var promise = fetch(request).then(response => response.blob()).then(blob => {
181+
+ uri = URL.createObjectURL(blob);
182+
+ return uri;
183+
+ }).catch(error => {
184+
+ if (error.name === 'AbortError') {
185+
+ return '';
186+
+ }
187+
+ throw error;
188+
+ });
189+
+ return {
190+
+ promise,
191+
+ source,
192+
+ cancel: () => {
193+
+ abortController.abort();
194+
+ URL.revokeObjectURL(uri);
195+
+ }
196+
+ };
197+
+ },
198+
prefetch(uri) {
199+
return new Promise((resolve, reject) => {
200+
ImageLoader.load(uri, () => {

0 commit comments

Comments
 (0)