react-native-fast-image 10.8.0 → 10.8.2

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.
@@ -1,5 +1,8 @@
1
1
  package com.fastimage;
2
2
 
3
+ import android.os.Handler;
4
+ import android.os.Looper;
5
+
3
6
  import com.facebook.react.bridge.Arguments;
4
7
  import com.facebook.react.bridge.ReactContext;
5
8
  import com.facebook.react.bridge.WritableMap;
@@ -15,6 +18,8 @@ final class FastImageEvents {
15
18
  static final int ERROR = 1 << 3;
16
19
  static final int LOAD_END = 1 << 4;
17
20
 
21
+ private static final Handler MAIN = new Handler(Looper.getMainLooper());
22
+
18
23
  private FastImageEvents() {
19
24
  }
20
25
 
@@ -57,8 +62,21 @@ final class FastImageEvents {
57
62
  send(view, FastImageRequestListener.REACT_ON_LOAD_END_EVENT, event);
58
63
  }
59
64
 
60
- // Only the events JS has a handler for: most images have none.
65
+ // Only the events JS has a handler for: most images have none. Posted to
66
+ // the main thread rather than dispatched here: this is called from Glide's
67
+ // onResourceReady and onLoadFailed, and EventDispatcher calls its listeners
68
+ // inline. Reanimated's listener flushes pending mount items, which can
69
+ // drop this view, and the drop clears the request inside its own
70
+ // callback, which Glide doesn't allow (it throws). One Handler keeps the
71
+ // events in order.
61
72
  static void send(FastImageViewWithUrl view, String name, WritableMap data) {
73
+ if (!view.handles(bit(name))) return;
74
+ MAIN.post(() -> dispatch(view, name, data));
75
+ }
76
+
77
+ // Dispatches the event now: for callers on the main thread outside Glide's
78
+ // callbacks (e.g. progress, which is posted already).
79
+ static void dispatch(FastImageViewWithUrl view, String name, WritableMap data) {
62
80
  if (!view.handles(bit(name))) return;
63
81
  ReactContext context = FastImageViewManager.getReactContext(view.getContext());
64
82
  if (context == null) return;
@@ -555,7 +555,7 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl>
555
555
  // Downloads only report a known length (see FastImageSharedDownloads).
556
556
  event.putDouble("progress", Math.min(1.0, Math.max(0.0, (double) loaded / total)));
557
557
  try {
558
- FastImageEvents.send(view, REACT_ON_PROGRESS_EVENT, event);
558
+ FastImageEvents.dispatch(view, REACT_ON_PROGRESS_EVENT, event);
559
559
  } catch (RuntimeException e) {
560
560
  // E.g. the view's React instance is gone: progress isn't worth
561
561
  // crashing the app for.
@@ -505,7 +505,7 @@ static NSDictionary *FFFFile(NSString *path)
505
505
  - (void)clearDiskCache:(RCTPromiseResolveBlock)resolve reject:(__unused RCTPromiseRejectBlock)reject
506
506
  {
507
507
  // And the HTTP cache of `cache: 'web'` images.
508
- [FFFastImageSource.webURLCache removeAllCachedResponses];
508
+ [FFFastImageSource clearWebURLCache];
509
509
  [SDImageCache.sharedImageCache clearDiskOnCompletion:^(){
510
510
  resolve(@{@"ok": @YES});
511
511
  }];
@@ -84,6 +84,14 @@ typedef NS_ENUM(NSInteger, FFFCacheControl) {
84
84
  // responses. 50 MB, as on Android.
85
85
  + (NSURLCache *)webURLCache;
86
86
 
87
+ // Empties webURLCache (for clearDiskCache). Its removal takes effect a few
88
+ // milliseconds after removeAllCachedResponses returns, and NSURLCache doesn't
89
+ // say when: until then a `cache: 'web'` load could still get a response from
90
+ // before the clear. So each response the web loader stores records the clear
91
+ // it came after, and for 5 s after a clear, a load ignores a cached response
92
+ // stored before it.
93
+ + (void)clearWebURLCache;
94
+
87
95
  // After a load of this source failed: for a `cache: 'web'` image whose data
88
96
  // isn't an image, removes the response from the HTTP cache, which stored it as
89
97
  // it downloaded. Otherwise the next loads would get it from there until it
@@ -215,6 +215,31 @@ static NSString *FFFBundleAssetName(NSURL *url)
215
215
  @interface FFFWebDownloaderOperation : SDWebImageDownloaderOperation
216
216
  @end
217
217
 
218
+ // The key, in a stored response's userInfo, of the clear it came after (see
219
+ // clearWebURLCache).
220
+ static NSString *const FFFCacheTokenKey = @"FFFastImageCacheToken";
221
+
222
+ // A new value at each clearWebURLCache (and one for the launch until then).
223
+ // Not the time: a change of the device's clock would move it.
224
+ static NSString *FFFWebURLCacheToken;
225
+
226
+ // When clearWebURLCache last ran, in seconds since the device started, or 0.
227
+ static NSTimeInterval FFFWebURLCacheClearedAt;
228
+
229
+ // How long after a clear loads check the response they'd get: the removal
230
+ // takes about 10 ms, longer on a busy device. Each check is a cache lookup.
231
+ static const NSTimeInterval FFFWebURLCacheClearCheck = 5;
232
+
233
+ static NSString *FFFCurrentWebURLCacheToken(void)
234
+ {
235
+ @synchronized ([FFFastImageSource class]) {
236
+ if (!FFFWebURLCacheToken) {
237
+ FFFWebURLCacheToken = NSUUID.UUID.UUIDString;
238
+ }
239
+ return FFFWebURLCacheToken;
240
+ }
241
+ }
242
+
218
243
  @implementation FFFWebDownloaderOperation
219
244
 
220
245
  // Whether data is an SVG document: its first element (after an XML
@@ -279,7 +304,14 @@ static BOOL FFFLooksLikeSVG(NSData *data)
279
304
  completionHandler(nil);
280
305
  return;
281
306
  }
282
- [super URLSession:session dataTask:dataTask willCacheResponse:proposedResponse completionHandler:completionHandler];
307
+ // The clear it came after (see clearWebURLCache).
308
+ NSMutableDictionary *userInfo = [proposedResponse.userInfo mutableCopy] ?: [NSMutableDictionary dictionary];
309
+ userInfo[FFFCacheTokenKey] = FFFCurrentWebURLCacheToken();
310
+ NSCachedURLResponse *response = [[NSCachedURLResponse alloc] initWithResponse:proposedResponse.response
311
+ data:proposedResponse.data
312
+ userInfo:userInfo
313
+ storagePolicy:proposedResponse.storagePolicy];
314
+ [super URLSession:session dataTask:dataTask willCacheResponse:response completionHandler:completionHandler];
283
315
  }
284
316
 
285
317
  @end
@@ -481,15 +513,46 @@ static BOOL FFFLooksLikeSVG(NSData *data)
481
513
  return cache;
482
514
  }
483
515
 
516
+ + (void)clearWebURLCache
517
+ {
518
+ @synchronized (self) {
519
+ FFFWebURLCacheToken = NSUUID.UUID.UUIDString;
520
+ FFFWebURLCacheClearedAt = NSProcessInfo.processInfo.systemUptime;
521
+ }
522
+ [self.webURLCache removeAllCachedResponses];
523
+ }
524
+
525
+ // Whether the HTTP cache's response to the request was stored before the last
526
+ // clearWebURLCache (or has no token: stored by an app's own operation class),
527
+ // in the few seconds after it.
528
+ + (BOOL)isCachedBeforeClear:(NSURLRequest *)request
529
+ {
530
+ NSString *token;
531
+ NSTimeInterval clearedAt;
532
+ @synchronized (self) {
533
+ token = FFFWebURLCacheToken;
534
+ clearedAt = FFFWebURLCacheClearedAt;
535
+ }
536
+ if (clearedAt == 0 || NSProcessInfo.processInfo.systemUptime - clearedAt > FFFWebURLCacheClearCheck) {
537
+ return NO;
538
+ }
539
+ NSCachedURLResponse *cached = [self.webURLCache cachedResponseForRequest:request];
540
+ return cached && ![cached.userInfo[FFFCacheTokenKey] isEqual:token];
541
+ }
542
+
484
543
  - (SDWebImageDownloaderRequestModifier *)requestModifier
485
544
  {
486
545
  NSDictionary* headers = _headers;
546
+ BOOL web = _cacheControl == FFFCacheControlWeb;
487
547
  return [SDWebImageDownloaderRequestModifier requestModifierWithBlock: ^NSURLRequest* _Nullable (NSURLRequest* _Nonnull request) {
488
548
  NSMutableURLRequest* mutableRequest = [request mutableCopy];
489
549
  for (NSString* header in headers) {
490
550
  NSString* value = headers[header];
491
551
  [mutableRequest setValue: value forHTTPHeaderField: header];
492
552
  }
553
+ if (web && [FFFastImageSource isCachedBeforeClear:mutableRequest]) {
554
+ mutableRequest.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
555
+ }
493
556
  return [mutableRequest copy];
494
557
  }];
495
558
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-fast-image",
3
- "version": "10.8.0",
3
+ "version": "10.8.2",
4
4
  "description": "FastImage, performant React Native image component.",
5
5
  "keywords": [
6
6
  "cache",