@@ -38,41 +38,57 @@ const getImageState = (uri, shouldDisplaySource) => {
3838} ;
3939
4040const resolveAssetDimensions = source => {
41- if ( typeof source === 'number' ) {
42- const { height, width } = getAssetByID ( source ) ;
43- return { height, width } ;
44- } else if ( typeof source === 'object' ) {
45- const { height, width } = source ;
46- return { height, width } ;
47- }
41+ return {
42+ height : source . height ,
43+ width : source . width
44+ } ;
4845} ;
4946
5047const svgDataUriPattern = / ^ ( d a t a : i m a g e \/ s v g \+ x m l ; u t f 8 , ) ( .* ) / ;
51- const resolveAssetUri = source => {
52- let uri = '' ;
48+ const resolveAssetSource = source => {
49+ let resolvedSource = {
50+ method : 'GET' ,
51+ uri : '' ,
52+ headers : { } ,
53+ width : undefined ,
54+ height : undefined
55+ } ;
5356 if ( typeof source === 'number' ) {
5457 // get the URI from the packager
5558 const asset = getAssetByID ( source ) ;
5659 const scale = asset . scales [ 0 ] ;
5760 const scaleSuffix = scale !== 1 ? `@${ scale } x` : '' ;
58- uri = asset ? `${ asset . httpServerLocation } /${ asset . name } ${ scaleSuffix } .${ asset . type } ` : '' ;
61+ resolvedSource . uri = asset
62+ ? `${ asset . httpServerLocation } /${ asset . name } ${ scaleSuffix } .${ asset . type } `
63+ : '' ;
64+ resolvedSource . width = asset . width ;
65+ resolvedSource . height = asset . height ;
5966 } else if ( typeof source === 'string' ) {
60- uri = source ;
61- } else if ( source && typeof source . uri === 'string' ) {
62- uri = source . uri ;
67+ resolvedSource . uri = source ;
68+ } else if ( typeof source === 'object' ) {
69+ resolvedSource = {
70+ ...resolvedSource ,
71+ ...source
72+ } ;
6373 }
6474
65- if ( uri ) {
66- const match = uri . match ( svgDataUriPattern ) ;
75+ if ( resolvedSource . uri ) {
76+ const match = resolvedSource . uri . match ( svgDataUriPattern ) ;
6777 // inline SVG markup may contain characters (e.g., #, ") that need to be escaped
6878 if ( match ) {
6979 const [ , prefix , svg ] = match ;
7080 const encodedSvg = encodeURIComponent ( svg ) ;
71- return `${ prefix } ${ encodedSvg } ` ;
81+ resolvedSource . uri = `${ prefix } ${ encodedSvg } ` ;
7282 }
7383 }
7484
75- return uri ;
85+ return resolvedSource ;
86+ } ;
87+ const getCacheId = source => {
88+ return JSON . stringify ( resolveAssetSource ( source ) ) ;
89+ } ;
90+ const getCacheUrl = e => {
91+ return e . path && e . path [ 0 ] . src ;
7692} ;
7793
7894let filterId = 0 ;
@@ -91,7 +107,8 @@ const createTintColorSVG = (tintColor, id) =>
91107
92108type State = {
93109 layout : Object ,
94- shouldDisplaySource : boolean
110+ shouldDisplaySource : boolean ,
111+ displayImageUri : string
95112} ;
96113
97114class Image extends Component < * , State > {
@@ -130,10 +147,10 @@ class Image extends Component<*, State> {
130147 }
131148
132149 static prefetch ( uri ) {
133- return ImageLoader . prefetch ( uri ) . then ( ( ) => {
150+ return ImageLoader . prefetch ( uri ) . then ( e => {
134151 // Add the uri to the cache so it can be immediately displayed when used
135152 // but also immediately remove it to correctly reflect that it has no active references
136- ImageUriCache . add ( uri ) ;
153+ ImageUriCache . add ( uri , getCacheUrl ( e ) ) ;
137154 ImageUriCache . remove ( uri ) ;
138155 } ) ;
139156 }
@@ -157,10 +174,19 @@ class Image extends Component<*, State> {
157174 constructor ( props , context ) {
158175 super ( props , context ) ;
159176 // If an image has been loaded before, render it immediately
160- const uri = resolveAssetUri ( props . source ) ;
161- const shouldDisplaySource = ImageUriCache . has ( uri ) ;
162- this . state = { layout : { } , shouldDisplaySource } ;
163- this . _imageState = getImageState ( uri , shouldDisplaySource ) ;
177+ const cacheId = getCacheId ( props . source ) ;
178+ const resolvedSource = resolveAssetSource ( props . source ) ;
179+ const resolvedDefaultSource = resolveAssetSource ( props . defaultSource ) ;
180+ const cachedSource = ImageUriCache . get ( cacheId ) ;
181+ const shouldDisplaySource = ! ! cachedSource ;
182+ this . state = {
183+ layout : { } ,
184+ shouldDisplaySource,
185+ displayImageUri : shouldDisplaySource
186+ ? cachedSource . uri
187+ : resolvedDefaultSource . uri || resolvedSource . uri
188+ } ;
189+ this . _imageState = getImageState ( resolvedSource . uri , shouldDisplaySource ) ;
164190 this . _filterId = filterId ;
165191 filterId ++ ;
166192 }
@@ -175,14 +201,14 @@ class Image extends Component<*, State> {
175201 }
176202
177203 componentDidUpdate ( prevProps ) {
178- const prevUri = resolveAssetUri ( prevProps . source ) ;
179- const uri = resolveAssetUri ( this . props . source ) ;
204+ const prevCacheId = getCacheId ( prevProps . source ) ;
205+ const cacheId = getCacheId ( this . props . source ) ;
180206 const hasDefaultSource = this . props . defaultSource != null ;
181- if ( prevUri !== uri ) {
182- ImageUriCache . remove ( prevUri ) ;
183- const isPreviouslyLoaded = ImageUriCache . has ( uri ) ;
184- isPreviouslyLoaded && ImageUriCache . add ( uri ) ;
185- this . _updateImageState ( getImageState ( uri , isPreviouslyLoaded ) , hasDefaultSource ) ;
207+ if ( prevCacheId !== cacheId ) {
208+ ImageUriCache . remove ( prevCacheId ) ;
209+ const isPreviouslyLoaded = ImageUriCache . has ( cacheId ) ;
210+ isPreviouslyLoaded && ImageUriCache . add ( cacheId ) ;
211+ this . _updateImageState ( getImageState ( cacheId , isPreviouslyLoaded ) , hasDefaultSource ) ;
186212 } else if ( hasDefaultSource && prevProps . defaultSource !== this . props . defaultSource ) {
187213 this . _updateImageState ( this . _imageState , hasDefaultSource ) ;
188214 }
@@ -192,14 +218,14 @@ class Image extends Component<*, State> {
192218 }
193219
194220 componentWillUnmount ( ) {
195- const uri = resolveAssetUri ( this . props . source ) ;
196- ImageUriCache . remove ( uri ) ;
221+ const cacheId = getCacheId ( this . props . source ) ;
222+ ImageUriCache . remove ( cacheId ) ;
197223 this . _destroyImageLoader ( ) ;
198224 this . _isMounted = false ;
199225 }
200226
201227 render ( ) {
202- const { shouldDisplaySource } = this . state ;
228+ const { displayImageUri , shouldDisplaySource } = this . state ;
203229 const {
204230 accessibilityLabel,
205231 accessible,
@@ -233,8 +259,7 @@ class Image extends Component<*, State> {
233259 }
234260 }
235261
236- const selectedSource = shouldDisplaySource ? source : defaultSource ;
237- const displayImageUri = resolveAssetUri ( selectedSource ) ;
262+ const selectedSource = resolveAssetSource ( shouldDisplaySource ? source : defaultSource ) ;
238263 const imageSizeStyle = resolveAssetDimensions ( selectedSource ) ;
239264 const backgroundImage = displayImageUri ? `url("${ displayImageUri } ")` : null ;
240265 const flatStyle = { ...StyleSheet . flatten ( this . props . style ) } ;
@@ -312,8 +337,11 @@ class Image extends Component<*, State> {
312337 _createImageLoader ( ) {
313338 const { source } = this . props ;
314339 this . _destroyImageLoader ( ) ;
315- const uri = resolveAssetUri ( source ) ;
316- this . _imageRequestId = ImageLoader . load ( uri , this . _onLoad , this . _onError ) ;
340+ this . _imageRequestId = ImageLoader . load (
341+ resolveAssetSource ( source ) ,
342+ this . _onLoad ,
343+ this . _onError
344+ ) ;
317345 this . _onLoadStart ( ) ;
318346 }
319347
@@ -356,7 +384,7 @@ class Image extends Component<*, State> {
356384 if ( onError ) {
357385 onError ( {
358386 nativeEvent : {
359- error : `Failed to load resource ${ resolveAssetUri ( source ) } (404)`
387+ error : `Failed to load resource ${ resolveAssetSource ( source ) . uri } (404)`
360388 }
361389 } ) ;
362390 }
@@ -366,7 +394,7 @@ class Image extends Component<*, State> {
366394 _onLoad = e => {
367395 const { onLoad, source } = this . props ;
368396 const event = { nativeEvent : e } ;
369- ImageUriCache . add ( resolveAssetUri ( source ) ) ;
397+ ImageUriCache . add ( getCacheId ( source ) , getCacheUrl ( e ) ) ;
370398 this . _updateImageState ( STATUS_LOADED ) ;
371399 if ( onLoad ) {
372400 onLoad ( event ) ;
@@ -394,14 +422,26 @@ class Image extends Component<*, State> {
394422 } ;
395423
396424 _updateImageState ( status : ?string , hasDefaultSource : ?boolean = false ) {
425+ const { source } = this . props ;
397426 this . _imageState = status ;
398427 const shouldDisplaySource =
399428 this . _imageState === STATUS_LOADED ||
400429 ( this . _imageState === STATUS_LOADING && ! hasDefaultSource ) ;
430+ const cachedId = getCacheId ( source ) ;
431+ const { displayImageUri } = ImageUriCache . has ( cachedId )
432+ ? ImageUriCache . get ( cachedId )
433+ : this . state ;
434+
401435 // only triggers a re-render when the image is loading and has no default image (to support PJPEG), loaded, or failed
402- if ( shouldDisplaySource !== this . state . shouldDisplaySource ) {
436+ if (
437+ shouldDisplaySource !== this . state . shouldDisplaySource ||
438+ displayImageUri !== this . state . displayImageUri
439+ ) {
403440 if ( this . _isMounted ) {
404- this . setState ( ( ) => ( { shouldDisplaySource } ) ) ;
441+ this . setState ( ( ) => ( {
442+ shouldDisplaySource,
443+ displayImageUri
444+ } ) ) ;
405445 }
406446 }
407447 }
0 commit comments