8
8
* @flow
9
9
*/
10
10
11
- import type { ImageProps } from './types' ;
11
+ import type { ImageProps , SourceObject } from './types' ;
12
12
13
13
import * as React from 'react' ;
14
14
import createElement from '../createElement' ;
@@ -146,6 +146,12 @@ function resolveAssetUri(source): ?string {
146
146
return uri ;
147
147
}
148
148
149
+ function hasSourceDiff ( a : SourceObject , b : SourceObject ) {
150
+ return (
151
+ a . uri !== b . uri || JSON . stringify ( a . headers ) !== JSON . stringify ( b . headers )
152
+ ) ;
153
+ }
154
+
149
155
interface ImageStatics {
150
156
getSize : (
151
157
uri : string ,
@@ -158,10 +164,12 @@ interface ImageStatics {
158
164
) => Promise < { | [ uri : string ] : 'disk/memory' | } > ;
159
165
}
160
166
161
- const Image : React . AbstractComponent <
167
+ type ImageComponent = React . AbstractComponent <
162
168
ImageProps ,
163
169
React . ElementRef < typeof View >
164
- > = React . forwardRef ( ( props , ref ) => {
170
+ > ;
171
+
172
+ const BaseImage : ImageComponent = React . forwardRef ( ( props , ref ) => {
165
173
const {
166
174
accessibilityLabel,
167
175
blurRadius,
@@ -332,24 +340,81 @@ const Image: React.AbstractComponent<
332
340
) ;
333
341
} ) ;
334
342
335
- Image . displayName = 'Image' ;
343
+ /**
344
+ * This component handles specifically loading an image source with header
345
+ */
346
+ const ImageWithHeaders : ImageComponent = React . forwardRef ( ( props , ref ) => {
347
+ // $FlowIgnore
348
+ const nextSource : SourceObject = props . source ;
349
+ const prevSource = React . useRef < SourceObject > ( { } ) ;
350
+ const [ blobUri , setBlobUri ] = React . useState ( '' ) ;
351
+
352
+ const { onError, onLoadStart } = props ;
353
+
354
+ React . useEffect ( ( ) => {
355
+ if ( ! hasSourceDiff ( nextSource , prevSource . current ) ) return ;
356
+
357
+ prevSource . current = nextSource ;
358
+
359
+ let uri ;
360
+ const abortCtrl = new AbortController ( ) ;
361
+ const request = new Request ( nextSource . uri , {
362
+ headers : nextSource . headers ,
363
+ signal : abortCtrl . signal
364
+ } ) ;
365
+ request . headers . append ( 'accept' , 'image/*' ) ;
366
+
367
+ if ( onLoadStart ) onLoadStart ( ) ;
368
+
369
+ fetch ( request )
370
+ . then ( ( response ) => response . blob ( ) )
371
+ . then ( ( blob ) => {
372
+ uri = URL . createObjectURL ( blob ) ;
373
+ setBlobUri ( uri ) ;
374
+ } )
375
+ . catch ( ( error ) => {
376
+ if ( error . name !== 'AbortError' && onError ) {
377
+ onError ( { nativeEvent : error . message } ) ;
378
+ }
379
+ } ) ;
380
+
381
+ return ( ) => {
382
+ abortCtrl . abort ( ) ;
383
+ setBlobUri ( '' ) ;
384
+ URL . revokeObjectURL ( uri ) ;
385
+ } ;
386
+ } , [ nextSource , onLoadStart , onError ] ) ;
387
+
388
+ const propsToPass = {
389
+ ...props ,
390
+ // Omit load start because we already triggered it here
391
+ onLoadStart : undefined ,
392
+ source : { ...nextSource , uri : blobUri }
393
+ } ;
394
+
395
+ return < BaseImage ref = { ref } { ...propsToPass } /> ;
396
+ } ) ;
336
397
337
398
// $FlowIgnore: This is the correct type, but casting makes it unhappy since the variables aren't defined yet
338
- const ImageWithStatics = ( Image : React . AbstractComponent <
339
- ImageProps ,
340
- React . ElementRef < typeof View >
341
- > &
342
- ImageStatics ) ;
399
+ const Image : ImageComponent & ImageStatics = React . forwardRef ( ( props , ref ) => {
400
+ if ( props . source && props . source . headers ) {
401
+ return < ImageWithHeaders ref = { ref } { ...props } /> ;
402
+ }
403
+
404
+ return < BaseImage ref = { ref } { ...props } /> ;
405
+ } ) ;
406
+
407
+ Image . displayName = 'Image' ;
343
408
344
- ImageWithStatics . getSize = function ( uri , success , failure ) {
409
+ Image . getSize = function ( uri , success , failure ) {
345
410
ImageLoader . getSize ( uri , success , failure ) ;
346
411
} ;
347
412
348
- ImageWithStatics . prefetch = function ( uri ) {
413
+ Image . prefetch = function ( uri ) {
349
414
return ImageLoader . prefetch ( uri ) ;
350
415
} ;
351
416
352
- ImageWithStatics . queryCache = function ( uris ) {
417
+ Image . queryCache = function ( uris ) {
353
418
return ImageLoader . queryCache ( uris ) ;
354
419
} ;
355
420
@@ -405,4 +470,4 @@ const resizeModeStyles = StyleSheet.create({
405
470
}
406
471
} ) ;
407
472
408
- export default ImageWithStatics ;
473
+ export default Image ;
0 commit comments