react-native-fast-image 8.16.2 → 8.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -163,6 +163,12 @@ Not used with `cache: 'web'`, which follows the HTTP cache (keyed by url).
163
163
 
164
164
  ---
165
165
 
166
+ ### `source.memoryCache?: boolean`
167
+
168
+ Whether the decoded image is kept in the memory cache. **Default: true.** With `false` it's only kept on disk: a view doesn't leave it in memory once it stops showing it, and `FastImage.preload` downloads it without decoding it. Use it for large images that are shown once or rarely, like a full-screen photo: a decoded photo can take tens of MB of memory. Images shown again, like a list scrolled back, are decoded from disk again.
169
+
170
+ ---
171
+
166
172
  ### `defaultSource?: number`
167
173
 
168
174
  - An asset loaded with `require(...)`.
@@ -351,6 +357,12 @@ for (const result of results) {
351
357
 
352
358
  Each source's `cache` applies, as for a view: with `web` the preload follows the HTTP cache, and with `cacheOnly` it doesn't download. A `cacheOnly` preload resolves `ok` only if the image is cached, and loads it from the disk cache into memory, so a view shows it at once.
353
359
 
360
+ A source with `memoryCache: false` is only downloaded to the disk cache, without being decoded into memory, and is decoded when it's shown. Use it to preload many images, or large ones, e.g. the next pages of a feed: a decoded photo can take tens of MB of memory. Other sources are also kept decoded in memory, so they show at once.
361
+
362
+ ```js
363
+ await FastImage.preload(photos.map((uri) => ({ uri, memoryCache: false })))
364
+ ```
365
+
354
366
  ### `FastImage.clearMemoryCache: () => Promise<void>`
355
367
 
356
368
  Clear all images from memory cache.
@@ -124,6 +124,27 @@ public class FastImageSource extends ImageSource {
124
124
  mWebCache = webCache;
125
125
  }
126
126
 
127
+ boolean isWebCache() {
128
+ return mWebCache;
129
+ }
130
+
131
+ // `memoryCache`: whether the decoded image is kept in the memory cache.
132
+ private boolean mMemoryCache = true;
133
+
134
+ void setMemoryCache(boolean memoryCache) {
135
+ mMemoryCache = memoryCache;
136
+ }
137
+
138
+ boolean isMemoryCache() {
139
+ return mMemoryCache;
140
+ }
141
+
142
+ // An http(s) url.
143
+ boolean isRemote() {
144
+ String scheme = mUri == null ? null : mUri.getScheme();
145
+ return "http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme);
146
+ }
147
+
127
148
  // `cacheKey`: the key to cache the image under instead of its url.
128
149
  @Nullable
129
150
  private String mCacheKey = null;
@@ -64,6 +64,9 @@ class FastImageViewConverter {
64
64
  if (source == null) return null;
65
65
  FastImageSource imageSource = new FastImageSource(context, source.getString("uri"), getHeaders(source));
66
66
  imageSource.setWebCache(getCacheControl(source) == FastImageCacheControl.WEB);
67
+ if (source.hasKey("memoryCache") && source.getType("memoryCache") == ReadableType.Boolean) {
68
+ imageSource.setMemoryCache(source.getBoolean("memoryCache"));
69
+ }
67
70
  if (source.hasKey("cacheKey") && source.getType("cacheKey") == ReadableType.String) {
68
71
  imageSource.setCacheKey(source.getString("cacheKey"));
69
72
  }
@@ -116,7 +119,8 @@ class FastImageViewConverter {
116
119
  RequestOptions options = new RequestOptions()
117
120
  .diskCacheStrategy(diskCacheStrategy)
118
121
  .onlyRetrieveFromCache(onlyFromCache)
119
- .skipMemoryCache(skipMemoryCache)
122
+ // memoryCache false: only kept on disk.
123
+ .skipMemoryCache(skipMemoryCache || (imageSource != null && !imageSource.isMemoryCache()))
120
124
  .priority(priority)
121
125
  .placeholder(TRANSPARENT_DRAWABLE);
122
126
 
@@ -1,6 +1,8 @@
1
1
  package com.dylanvann.fastimage;
2
2
 
3
3
  import android.app.Activity;
4
+ import android.content.Context;
5
+ import android.graphics.BitmapFactory;
4
6
  import android.graphics.drawable.Drawable;
5
7
 
6
8
  import androidx.annotation.NonNull;
@@ -9,6 +11,8 @@ import androidx.annotation.Nullable;
9
11
  import com.bumptech.glide.Glide;
10
12
  import com.bumptech.glide.Priority;
11
13
  import com.bumptech.glide.load.DataSource;
14
+ import com.bumptech.glide.load.ImageHeaderParser;
15
+ import com.bumptech.glide.load.ImageHeaderParserUtils;
12
16
  import com.bumptech.glide.load.engine.GlideException;
13
17
  import com.bumptech.glide.request.RequestListener;
14
18
  import com.bumptech.glide.request.RequestOptions;
@@ -24,8 +28,13 @@ import com.facebook.react.bridge.UiThreadUtil;
24
28
  import com.facebook.react.bridge.WritableArray;
25
29
  import com.facebook.react.bridge.WritableMap;
26
30
 
31
+ import java.io.File;
32
+ import java.io.FileInputStream;
27
33
  import java.io.IOException;
34
+ import java.io.InputStream;
28
35
  import java.util.ArrayDeque;
36
+ import java.util.concurrent.Executor;
37
+ import java.util.concurrent.Executors;
29
38
 
30
39
  class FastImageViewModule extends ReactContextBaseJavaModule {
31
40
 
@@ -63,8 +72,44 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
63
72
  }
64
73
  }
65
74
 
75
+ // Reads the size of images preloaded to disk only, off the UI thread.
76
+ private static final Executor sizeExecutor = Executors.newSingleThreadExecutor();
77
+
78
+ // The image's size from its file's header, as a decoded image has it (with
79
+ // its EXIF orientation), or null if it can't be read.
80
+ @Nullable
81
+ private static int[] imageSize(Context context, File file) {
82
+ BitmapFactory.Options bounds = new BitmapFactory.Options();
83
+ bounds.inJustDecodeBounds = true;
84
+ BitmapFactory.decodeFile(file.getPath(), bounds);
85
+ if (bounds.outWidth <= 0 || bounds.outHeight <= 0) return null;
86
+ int orientation = ImageHeaderParser.UNKNOWN_ORIENTATION;
87
+ Glide glide = Glide.get(context);
88
+ try (InputStream stream = new FileInputStream(file)) {
89
+ orientation = ImageHeaderParserUtils.getOrientation(
90
+ glide.getRegistry().getImageHeaderParsers(), stream, glide.getArrayPool());
91
+ } catch (IOException e) {
92
+ // Taken as not rotated.
93
+ }
94
+ // 5 to 8 turn the image by 90 degrees.
95
+ boolean transposed = orientation >= 5 && orientation <= 8;
96
+ return transposed
97
+ ? new int[] {bounds.outHeight, bounds.outWidth}
98
+ : new int[] {bounds.outWidth, bounds.outHeight};
99
+ }
100
+
101
+ private static WritableMap success(int width, int height) {
102
+ WritableMap result = Arguments.createMap();
103
+ result.putBoolean("ok", true);
104
+ result.putInt("width", width);
105
+ result.putInt("height", height);
106
+ return result;
107
+ }
108
+
66
109
  // Resolves with a result per source, in order, once all have loaded or
67
110
  // failed: { ok, width, height } or { ok: false, error }. Never rejects.
111
+ // A remote source with memoryCache false is only downloaded to the disk
112
+ // cache, without decoding it (its size comes from the header).
68
113
  @ReactMethod
69
114
  public void preload(final ReadableArray sources, final Promise promise) {
70
115
  final ReactApplicationContext context = getReactApplicationContext();
@@ -111,6 +156,58 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
111
156
  final RequestOptions preloadOptions = source.hasKey("priority") && !source.isNull("priority")
112
157
  ? options
113
158
  : options.priority(Priority.LOW);
159
+ // A source's result, which frees its slot.
160
+ final ResultCallback done = new ResultCallback() {
161
+ @Override
162
+ public void run(WritableMap result) {
163
+ results[index] = result;
164
+ preloadsInFlight--;
165
+ finishOne.run();
166
+ startPendingPreloads();
167
+ }
168
+ };
169
+ // Remote images (web ones are in the HTTP cache instead,
170
+ // and local ones are already on disk).
171
+ if (!imageSource.isMemoryCache() && !imageSource.isWebCache() && imageSource.isRemote()) {
172
+ pendingPreloads.add(new Runnable() {
173
+ @Override
174
+ public void run() {
175
+ Glide
176
+ .with(context)
177
+ .asFile()
178
+ .load(imageSource.getSourceForLoad())
179
+ .apply(preloadOptions)
180
+ .listener(new RequestListener<File>() {
181
+ @Override
182
+ public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<File> target, boolean isFirstResource) {
183
+ done.run(failure(FastImageRequestListener.errorMessage(e)));
184
+ return false;
185
+ }
186
+
187
+ @Override
188
+ public boolean onResourceReady(final File file, Object model, Target<File> target, DataSource dataSource, boolean isFirstResource) {
189
+ sizeExecutor.execute(new Runnable() {
190
+ @Override
191
+ public void run() {
192
+ final int[] size = imageSize(context, file);
193
+ UiThreadUtil.runOnUiThread(new Runnable() {
194
+ @Override
195
+ public void run() {
196
+ done.run(size != null
197
+ ? success(size[0], size[1])
198
+ : failure("Failed to read the image's size"));
199
+ }
200
+ });
201
+ }
202
+ });
203
+ return false;
204
+ }
205
+ })
206
+ .preload();
207
+ }
208
+ });
209
+ continue;
210
+ }
114
211
  pendingPreloads.add(new Runnable() {
115
212
  @Override
116
213
  public void run() {
@@ -123,10 +220,7 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
123
220
  .listener(new RequestListener<Drawable>() {
124
221
  @Override
125
222
  public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
126
- results[index] = failure(FastImageRequestListener.errorMessage(e));
127
- preloadsInFlight--;
128
- finishOne.run();
129
- startPendingPreloads();
223
+ done.run(failure(FastImageRequestListener.errorMessage(e)));
130
224
  return false;
131
225
  }
132
226
 
@@ -134,14 +228,7 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
134
228
  public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
135
229
  // Preloaded at its original size, so this is
136
230
  // the image's own size.
137
- WritableMap result = Arguments.createMap();
138
- result.putBoolean("ok", true);
139
- result.putInt("width", resource.getIntrinsicWidth());
140
- result.putInt("height", resource.getIntrinsicHeight());
141
- results[index] = result;
142
- preloadsInFlight--;
143
- finishOne.run();
144
- startPendingPreloads();
231
+ done.run(success(resource.getIntrinsicWidth(), resource.getIntrinsicHeight()));
145
232
  return false;
146
233
  }
147
234
  })
@@ -158,6 +245,10 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
158
245
  });
159
246
  }
160
247
 
248
+ private interface ResultCallback {
249
+ void run(WritableMap result);
250
+ }
251
+
161
252
  private static WritableMap failure(String error) {
162
253
  WritableMap result = Arguments.createMap();
163
254
  result.putBoolean("ok", false);
@@ -4,6 +4,7 @@ import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_ERROR_EV
4
4
 
5
5
  import android.annotation.SuppressLint;
6
6
  import android.content.Context;
7
+ import android.graphics.Bitmap;
7
8
  import android.graphics.drawable.BitmapDrawable;
8
9
  import android.graphics.drawable.Drawable;
9
10
 
@@ -108,8 +109,20 @@ class FastImageViewWithUrl extends AppCompatImageView {
108
109
  // Shows each GIF as this view's own animation. Glide's target also starts
109
110
  // and stops it with the Activity, as it does Glide's own GifDrawable.
110
111
  private final class OwnGifTarget extends DrawableImageViewTarget {
111
- OwnGifTarget() {
112
+ // Shown instead of the placeholder while loading (see shownCopy).
113
+ @Nullable
114
+ private Drawable mMeanwhile;
115
+
116
+ OwnGifTarget(@Nullable Drawable meanwhile) {
112
117
  super(FastImageViewWithUrl.this);
118
+ mMeanwhile = meanwhile;
119
+ }
120
+
121
+ @Override
122
+ public void onLoadStarted(@Nullable Drawable placeholder) {
123
+ Drawable meanwhile = mMeanwhile;
124
+ mMeanwhile = null;
125
+ super.onLoadStarted(meanwhile != null ? meanwhile : placeholder);
113
126
  }
114
127
 
115
128
  @Override
@@ -287,12 +300,35 @@ class FastImageViewWithUrl extends AppCompatImageView {
287
300
  private void reloadForSize() {
288
301
  RequestBuilder<Drawable> shown = mShownRequest;
289
302
  RequestBuilder<Drawable> current = fromCache(shown);
303
+ Drawable meanwhile = shownCopy(shown);
290
304
  mLoadCount++;
291
305
  clearView(requestManager);
292
- into(shown, shown.clone()
293
- .thumbnail(current)
306
+ RequestBuilder<Drawable> builder = shown.clone()
294
307
  .error(current.clone())
295
- .listener(new FastImageRequestListener(null, null, true, false)));
308
+ .listener(new FastImageRequestListener(null, null, true, false));
309
+ if (meanwhile == null) builder = builder.thumbnail(current);
310
+ into(shown, builder, meanwhile);
311
+ }
312
+
313
+ // A copy of the image showing, for the view to show while the next one
314
+ // loads, when it isn't in Glide's memory cache (source.memoryCache false):
315
+ // a thumbnail of it would come from the disk, after the view has cleared,
316
+ // and its own bitmap is reused once its request is cleared. Only the view
317
+ // keeps the copy, until the next image replaces it. Null when the image
318
+ // is in the memory cache, or isn't a bitmap (a GIF).
319
+ @Nullable
320
+ private Drawable shownCopy(RequestBuilder<Drawable> shown) {
321
+ if (shown.isMemoryCacheable()) return null;
322
+ Drawable drawable = getDrawable();
323
+ if (!(drawable instanceof BitmapDrawable)) return null;
324
+ Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
325
+ if (bitmap == null || bitmap.isRecycled()) return null;
326
+ Bitmap.Config config = bitmap.getConfig();
327
+ Bitmap copy = bitmap.copy(config != null ? config : Bitmap.Config.ARGB_8888, false);
328
+ if (copy == null) return null;
329
+ BitmapDrawable result = new BitmapDrawable(getResources(), copy);
330
+ if (mPixelated) result.setFilterBitmap(false);
331
+ return result;
296
332
  }
297
333
 
298
334
  // The shown image, from the cache only (never loaded again), at the size
@@ -304,15 +340,15 @@ class FastImageViewWithUrl extends AppCompatImageView {
304
340
  return request;
305
341
  }
306
342
 
307
- // Starts loading the request (built by builder), recording the size Glide
308
- // loads it at. The size callback is added first, so it has the size by
343
+ // Starts loading the request (built by builder), showing meanwhile (if
344
+ // not null) until it loads, and recording the size Glide loads it at. The size callback is added first, so it has the size by
309
345
  // the time the image loads, even from the memory cache.
310
- private void into(RequestBuilder<Drawable> request, RequestBuilder<Drawable> builder) {
346
+ private void into(RequestBuilder<Drawable> request, RequestBuilder<Drawable> builder, @Nullable Drawable meanwhile) {
311
347
  final int load = mLoadCount;
312
348
  mLoadingRequest = request;
313
349
  mLoadingWidth = 0;
314
350
  mLoadingHeight = 0;
315
- OwnGifTarget target = new OwnGifTarget();
351
+ OwnGifTarget target = new OwnGifTarget(meanwhile);
316
352
  target.getSize(new SizeReadyCallback() {
317
353
  @Override
318
354
  public void onSizeReady(int width, int height) {
@@ -399,6 +435,9 @@ class FastImageViewWithUrl extends AppCompatImageView {
399
435
  untrackUrl(viewsForUrlsMap);
400
436
  }
401
437
 
438
+ // Before the view clears (see shownCopy).
439
+ Drawable meanwhile = shownRequest != null && imageSource != null ? shownCopy(shownRequest) : null;
440
+
402
441
  // Cancel existing request.
403
442
  this.glideUrl = glideUrl;
404
443
  clearView(requestManager);
@@ -440,7 +479,8 @@ class FastImageViewWithUrl extends AppCompatImageView {
440
479
  .apply(renderingOptions(model));
441
480
  RequestBuilder<Drawable> request = builder.clone();
442
481
 
443
- boolean thumbnail = shownRequest != null && model != null;
482
+ if (model == null) meanwhile = null;
483
+ boolean thumbnail = shownRequest != null && model != null && meanwhile == null;
444
484
  if (thumbnail) {
445
485
  builder = builder.thumbnail(fromCache(shownRequest));
446
486
  }
@@ -448,7 +488,7 @@ class FastImageViewWithUrl extends AppCompatImageView {
448
488
  if (key != null)
449
489
  builder.listener(new FastImageRequestListener(key, imageSource, thumbnail, true));
450
490
 
451
- into(request, builder);
491
+ into(request, builder, meanwhile);
452
492
  }
453
493
  }
454
494
 
@@ -77,6 +77,7 @@ export type FastImageSource = {
77
77
  priority?: Priorities,
78
78
  cache?: CacheControls,
79
79
  cacheKey?: string,
80
+ memoryCache?: boolean,
80
81
  }
81
82
 
82
83
  export type FastImageProps = $ReadOnly<{|
package/dist/index.d.ts CHANGED
@@ -33,6 +33,14 @@ export type Source = {
33
33
  * an expiry: use something stable, like the image's id.
34
34
  */
35
35
  cacheKey?: string;
36
+ /**
37
+ * Whether the decoded image is kept in the memory cache (default true).
38
+ * With false it's only kept on disk: a view doesn't leave it in memory
39
+ * once it stops showing it, and `FastImage.preload` downloads it without
40
+ * decoding it. For large images shown once, like a full-screen photo; a
41
+ * list scrolled back decodes them again.
42
+ */
43
+ memoryCache?: boolean;
36
44
  };
37
45
  export interface OnLoadEvent {
38
46
  nativeEvent: {
@@ -77,6 +77,7 @@ export type FastImageSource = {
77
77
  priority?: Priorities,
78
78
  cache?: CacheControls,
79
79
  cacheKey?: string,
80
+ memoryCache?: boolean,
80
81
  }
81
82
 
82
83
  export type FastImageProps = $ReadOnly<{|
@@ -32,6 +32,10 @@ typedef NS_ENUM(NSInteger, FFFCacheControl) {
32
32
  @property (nonatomic) FFFCacheControl cacheControl;
33
33
  // The key to cache the image under instead of its url, or nil.
34
34
  @property (nonatomic, copy) NSString *cacheKey;
35
+ // Whether the decoded image is kept in the memory cache (`memoryCache`,
36
+ // default YES). Without it, it's only kept on disk: views don't leave it in
37
+ // memory, and preload doesn't decode it.
38
+ @property (nonatomic) BOOL memoryCache;
35
39
 
36
40
  - (instancetype)initWithURL:(NSURL *)url
37
41
  priority:(FFFPriority)priority
@@ -18,6 +18,7 @@ static NSUInteger const FFFWebCacheSize = 50 * 1024 * 1024;
18
18
  _priority = priority;
19
19
  _headers = headers;
20
20
  _cacheControl = cacheControl;
21
+ _memoryCache = YES;
21
22
  }
22
23
  return self;
23
24
  }
@@ -515,10 +515,19 @@ NSString *FFFErrorMessage(NSError *error)
515
515
  self.decodedCover = [self decodeCovers];
516
516
  if (CGSizeEqualToSize(box, CGSizeZero)) {
517
517
  context[SDWebImageContextAnimatedImageClass] = [SDAnimatedImage class];
518
+ if (!_source.memoryCache) {
519
+ // Only on disk, also when it comes from there.
520
+ context[SDWebImageContextStoreCacheType] = @(SDImageCacheTypeDisk);
521
+ }
518
522
  return context;
519
523
  }
520
524
  NSString* key = _source.cacheKeyFilter ? _source.cacheKey : _source.url.absoluteString;
521
525
  [FFFDownsampledImage addToContext: context forKey: key box: box cover: self.decodedCover];
526
+ if (!_source.memoryCache) {
527
+ // The smaller copy is only kept in memory, so it isn't kept (the
528
+ // downloaded file is still on disk).
529
+ context[SDWebImageContextStoreCacheType] = @(SDImageCacheTypeNone);
530
+ }
522
531
  return context;
523
532
  }
524
533
 
@@ -69,7 +69,9 @@ static void FFFStartPendingPreloads(void)
69
69
  }
70
70
 
71
71
  // Resolves with a result per source, in order, once all have loaded or failed:
72
- // { ok, width, height } or { ok: false, error }. Never rejects.
72
+ // { ok, width, height } or { ok: false, error }. Never rejects. A source with
73
+ // memoryCache false is only stored on disk, without decoding it (its size
74
+ // comes from the header).
73
75
  RCT_EXPORT_METHOD(preload:(nonnull NSArray<FFFastImageSource *> *)sources
74
76
  resolve:(RCTPromiseResolveBlock)resolve
75
77
  reject:(__unused RCTPromiseRejectBlock)reject)
@@ -105,7 +107,10 @@ RCT_EXPORT_METHOD(preload:(nonnull NSArray<FFFastImageSource *> *)sources
105
107
  }
106
108
  [results addObject:[NSNull null]];
107
109
  // A source's own priority replaces the prefetcher's, as on Android.
108
- SDWebImageOptions options = prefetcherOptions;
110
+ // Failed urls are tried again, as views do (SDWebImage otherwise
111
+ // fails a url that failed before, e.g. with data that isn't an
112
+ // image, without a request until the app is relaunched, #394).
113
+ SDWebImageOptions options = prefetcherOptions | SDWebImageRetryFailed;
109
114
  if (source.hasPriority) {
110
115
  options &= ~(SDWebImageLowPriority | SDWebImageHighPriority);
111
116
  if (source.priority == FFFPriorityLow) {
@@ -115,13 +120,20 @@ RCT_EXPORT_METHOD(preload:(nonnull NSArray<FFFastImageSource *> *)sources
115
120
  }
116
121
  }
117
122
  options |= [source cacheOptions];
123
+ SDWebImageMutableContext *context = FFFPreloadContext(source);
124
+ if (!source.memoryCache) {
125
+ // Not decoded (only its header is read, for the size), and not
126
+ // kept in memory, also when it comes from the disk cache.
127
+ options |= SDWebImageAvoidDecodeImage;
128
+ context[SDWebImageContextStoreCacheType] = @(SDImageCacheTypeDisk);
129
+ }
118
130
  [FFFPendingPreloads addObject:[^{
119
131
  // Once per slot: SDWebImage calls this once with finished set
120
132
  // (a progressive load calls it more, without).
121
133
  __block BOOL done = NO;
122
134
  [[SDWebImageManager sharedManager] loadImageWithURL:source.url
123
135
  options:options
124
- context:[FFFPreloadContext(source) copy]
136
+ context:[context copy]
125
137
  progress:nil
126
138
  completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
127
139
  if (!finished || done) {
@@ -49,6 +49,8 @@ RCT_ENUM_CONVERTER(FFFCacheControl, (@{
49
49
  imageSource.hasPriority = priorityJSON && priorityJSON != (id)kCFNull;
50
50
  NSString *cacheKey = [self NSString:json[@"cacheKey"]];
51
51
  imageSource.cacheKey = cacheKey.length > 0 ? cacheKey : nil;
52
+ id memoryCache = json[@"memoryCache"];
53
+ imageSource.memoryCache = memoryCache && memoryCache != (id)kCFNull ? [self BOOL:memoryCache] : YES;
52
54
 
53
55
  return imageSource;
54
56
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-fast-image",
3
- "version": "8.16.2",
3
+ "version": "8.17.1",
4
4
  "description": "🚩 FastImage, performant React Native image component.",
5
5
  "keywords": [
6
6
  "cache",