react-native-fast-image 8.15.0 → 8.16.0

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
@@ -146,6 +146,23 @@ Indicates the load order priority of an image. Images with `FastImage.priority.h
146
146
 
147
147
  ---
148
148
 
149
+ ### `source.cacheKey?: string`
150
+
151
+ The key the image is cached under, instead of its uri. Use it for urls that change while the image stays the same, e.g. signed urls with a token or an expiry. Use something that identifies the image, and change it when the image changes (e.g. include a version or the time it was updated), or the old image keeps showing:
152
+
153
+ ```jsx
154
+ <FastImage
155
+ source={{
156
+ uri: signedUrl,
157
+ cacheKey: `avatar-${user.id}-${user.avatarUpdatedAt}`,
158
+ }}
159
+ />
160
+ ```
161
+
162
+ Not used with `cache: 'web'`, which follows the HTTP cache (keyed by url).
163
+
164
+ ---
165
+
149
166
  ### `defaultSource?: number`
150
167
 
151
168
  - An asset loaded with `require(...)`.
@@ -0,0 +1,37 @@
1
+ package com.dylanvann.fastimage;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import androidx.annotation.Nullable;
5
+
6
+ import com.bumptech.glide.load.model.GlideUrl;
7
+ import com.bumptech.glide.load.model.Headers;
8
+
9
+ // A url cached under the source's `cacheKey` instead of the url. Glide keys
10
+ // its disk cache by getCacheKey(), and its memory cache by equals() and
11
+ // hashCode(), which also compare the headers: here they only use the key, so
12
+ // a url whose token or headers change still hits both caches. The request
13
+ // still uses the url and headers.
14
+ class FastImageKeyedGlideUrl extends GlideUrl {
15
+ private final String mCacheKey;
16
+
17
+ FastImageKeyedGlideUrl(String url, Headers headers, @NonNull String cacheKey) {
18
+ super(url, headers);
19
+ mCacheKey = cacheKey;
20
+ }
21
+
22
+ @Override
23
+ public String getCacheKey() {
24
+ return mCacheKey;
25
+ }
26
+
27
+ @Override
28
+ public boolean equals(@Nullable Object other) {
29
+ return other instanceof FastImageKeyedGlideUrl
30
+ && mCacheKey.equals(((FastImageKeyedGlideUrl) other).mCacheKey);
31
+ }
32
+
33
+ @Override
34
+ public int hashCode() {
35
+ return mCacheKey.hashCode();
36
+ }
37
+ }
@@ -124,9 +124,23 @@ public class FastImageSource extends ImageSource {
124
124
  mWebCache = webCache;
125
125
  }
126
126
 
127
+ // `cacheKey`: the key to cache the image under instead of its url.
128
+ @Nullable
129
+ private String mCacheKey = null;
130
+
131
+ void setCacheKey(@Nullable String cacheKey) {
132
+ mCacheKey = cacheKey == null || cacheKey.isEmpty() ? null : cacheKey;
133
+ }
134
+
127
135
  public GlideUrl getGlideUrl() {
128
- return mWebCache
129
- ? new FastImageWebGlideUrl(getUri().toString(), getHeaders())
130
- : new GlideUrl(getUri().toString(), getHeaders());
136
+ String url = getUri().toString();
137
+ if (mWebCache) {
138
+ // Follows the HTTP cache, which is keyed by url.
139
+ return new FastImageWebGlideUrl(url, getHeaders());
140
+ }
141
+ if (mCacheKey != null) {
142
+ return new FastImageKeyedGlideUrl(url, getHeaders(), mCacheKey);
143
+ }
144
+ return new GlideUrl(url, getHeaders());
131
145
  }
132
146
  }
@@ -19,6 +19,7 @@ import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
19
19
  import com.facebook.react.bridge.NoSuchKeyException;
20
20
  import com.facebook.react.bridge.ReadableMap;
21
21
  import com.facebook.react.bridge.ReadableMapKeySetIterator;
22
+ import com.facebook.react.bridge.ReadableType;
22
23
 
23
24
  import java.util.HashMap;
24
25
  import java.util.Map;
@@ -63,6 +64,9 @@ class FastImageViewConverter {
63
64
  if (source == null) return null;
64
65
  FastImageSource imageSource = new FastImageSource(context, source.getString("uri"), getHeaders(source));
65
66
  imageSource.setWebCache(getCacheControl(source) == FastImageCacheControl.WEB);
67
+ if (source.hasKey("cacheKey") && source.getType("cacheKey") == ReadableType.String) {
68
+ imageSource.setCacheKey(source.getString("cacheKey"));
69
+ }
66
70
  return imageSource;
67
71
  }
68
72
 
@@ -76,6 +76,7 @@ export type FastImageSource = {
76
76
  headers?: Object,
77
77
  priority?: Priorities,
78
78
  cache?: CacheControls,
79
+ cacheKey?: string,
79
80
  }
80
81
 
81
82
  export type FastImageProps = $ReadOnly<{|
package/dist/index.d.ts CHANGED
@@ -27,6 +27,12 @@ export type Source = {
27
27
  };
28
28
  priority?: Priority;
29
29
  cache?: Cache;
30
+ /**
31
+ * The key the image is cached under, instead of its uri. For urls that
32
+ * change while the image stays the same, e.g. signed urls with a token or
33
+ * an expiry: use something stable, like the image's id.
34
+ */
35
+ cacheKey?: string;
30
36
  };
31
37
  export interface OnLoadEvent {
32
38
  nativeEvent: {
@@ -76,6 +76,7 @@ export type FastImageSource = {
76
76
  headers?: Object,
77
77
  priority?: Priorities,
78
78
  cache?: CacheControls,
79
+ cacheKey?: string,
79
80
  }
80
81
 
81
82
  export type FastImageProps = $ReadOnly<{|
@@ -14,8 +14,8 @@
14
14
  + (NSURL*) loadURLForURL: (NSURL*)url;
15
15
 
16
16
  // Sets a load's context to decode the image for a box (in pixels) to cover
17
- // or fit in.
18
- + (void) addToContext: (SDWebImageMutableContext*)context forURL: (NSURL*)url box: (CGSize)box cover: (BOOL)cover;
17
+ // or fit in. `key` is the source's cache key (its url, or its cacheKey).
18
+ + (void) addToContext: (SDWebImageMutableContext*)context forKey: (NSString*)key box: (CGSize)box cover: (BOOL)cover;
19
19
 
20
20
  // The size of the full image (in points, like UIImage's size) that an image
21
21
  // loaded this way was decoded from, for onLoad, or zero for other images.
@@ -100,11 +100,11 @@ static CGSize FFFPixelSize(NSData* data) {
100
100
  return components.URL ?: url;
101
101
  }
102
102
 
103
- + (void) addToContext: (SDWebImageMutableContext*)context forURL: (NSURL*)url box: (CGSize)box cover: (BOOL)cover {
103
+ + (void) addToContext: (SDWebImageMutableContext*)context forKey: (NSString*)key box: (CGSize)box cover: (BOOL)cover {
104
104
  context[SDWebImageContextAnimatedImageClass] = [FFFDownsampledImage class];
105
- // Cached under the url itself (see loadURLForURL:), so the disk cache
106
- // keeps one download for every size and for loads that aren't downsampled.
107
- NSString* key = url.absoluteString;
105
+ // Cached under the source's own key (not the url from loadURLForURL:),
106
+ // so the disk cache keeps one download for every size and for loads
107
+ // that aren't downsampled.
108
108
  context[SDWebImageContextCacheKeyFilter] = [SDWebImageCacheKeyFilter cacheKeyFilterWithBlock: ^NSString* (NSURL* _Nonnull loadURL) {
109
109
  return key;
110
110
  }];
@@ -1,6 +1,7 @@
1
1
  #import <Foundation/Foundation.h>
2
2
  #import <UIKit/UIKit.h>
3
3
  #import <SDWebImage/SDWebImageDownloaderRequestModifier.h>
4
+ #import <SDWebImage/SDWebImageCacheKeyFilter.h>
4
5
 
5
6
  typedef NS_ENUM(NSInteger, FFFPriority) {
6
7
  FFFPriorityLow,
@@ -27,6 +28,8 @@ typedef NS_ENUM(NSInteger, FFFCacheControl) {
27
28
  @property (nonatomic) NSDictionary *headers;
28
29
  // cache control mode
29
30
  @property (nonatomic) FFFCacheControl cacheControl;
31
+ // The key to cache the image under instead of its url, or nil.
32
+ @property (nonatomic, copy) NSString *cacheKey;
30
33
 
31
34
  - (instancetype)initWithURL:(NSURL *)url
32
35
  priority:(FFFPriority)priority
@@ -36,4 +39,7 @@ typedef NS_ENUM(NSInteger, FFFCacheControl) {
36
39
  // Adds this source's headers to its image requests.
37
40
  - (SDWebImageDownloaderRequestModifier *)requestModifier;
38
41
 
42
+ // Caches the image under cacheKey, or nil without one.
43
+ - (SDWebImageCacheKeyFilter *)cacheKeyFilter;
44
+
39
45
  @end
@@ -17,6 +17,18 @@
17
17
  return self;
18
18
  }
19
19
 
20
+ - (SDWebImageCacheKeyFilter *)cacheKeyFilter
21
+ {
22
+ NSString* cacheKey = _cacheKey;
23
+ // `cache: 'web'` follows the HTTP cache, which is keyed by url.
24
+ if (cacheKey.length == 0 || _cacheControl == FFFCacheControlWeb) {
25
+ return nil;
26
+ }
27
+ return [SDWebImageCacheKeyFilter cacheKeyFilterWithBlock: ^NSString* _Nullable (NSURL* _Nonnull url) {
28
+ return cacheKey;
29
+ }];
30
+ }
31
+
20
32
  - (SDWebImageDownloaderRequestModifier *)requestModifier
21
33
  {
22
34
  NSDictionary* headers = _headers;
@@ -518,6 +518,7 @@ NSString *FFFErrorMessage(NSError *error)
518
518
  - (SDWebImageContext*) loadContext {
519
519
  SDWebImageMutableContext* context = [NSMutableDictionary dictionary];
520
520
  context[SDWebImageContextDownloadRequestModifier] = _source.requestModifier;
521
+ context[SDWebImageContextCacheKeyFilter] = _source.cacheKeyFilter;
521
522
  CGSize box = [self decodeBox];
522
523
  self.decodedBox = box;
523
524
  self.decodedCover = [self decodeCovers];
@@ -525,7 +526,8 @@ NSString *FFFErrorMessage(NSError *error)
525
526
  context[SDWebImageContextAnimatedImageClass] = [SDAnimatedImage class];
526
527
  return context;
527
528
  }
528
- [FFFDownsampledImage addToContext: context forURL: _source.url box: box cover: self.decodedCover];
529
+ NSString* key = _source.cacheKeyFilter ? _source.cacheKey : _source.url.absoluteString;
530
+ [FFFDownsampledImage addToContext: context forKey: key box: box cover: self.decodedCover];
529
531
  return context;
530
532
  }
531
533
 
@@ -37,6 +37,15 @@ RCT_REMAP_VIEW_PROPERTY(tintColor, imageColor, UIColor)
37
37
  static NSMutableArray<dispatch_block_t> *FFFPendingPreloads;
38
38
  static NSUInteger FFFPreloadsInFlight;
39
39
 
40
+ // A preload's context: the source's headers and cache key.
41
+ static SDWebImageMutableContext *FFFPreloadContext(FFFastImageSource *source)
42
+ {
43
+ SDWebImageMutableContext *context = [NSMutableDictionary dictionary];
44
+ context[SDWebImageContextDownloadRequestModifier] = source.requestModifier;
45
+ context[SDWebImageContextCacheKeyFilter] = source.cacheKeyFilter;
46
+ return context;
47
+ }
48
+
40
49
  static NSUInteger FFFPreloadLimit(void)
41
50
  {
42
51
  NSUInteger limit = [SDWebImagePrefetcher sharedImagePrefetcher].maxConcurrentPrefetchCount;
@@ -109,7 +118,7 @@ RCT_EXPORT_METHOD(preload:(nonnull NSArray<FFFastImageSource *> *)sources
109
118
  __block BOOL done = NO;
110
119
  [[SDWebImageManager sharedManager] loadImageWithURL:source.url
111
120
  options:options
112
- context:@{SDWebImageContextDownloadRequestModifier: source.requestModifier}
121
+ context:[FFFPreloadContext(source) copy]
113
122
  progress:nil
114
123
  completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
115
124
  if (!finished || done) {
@@ -47,6 +47,8 @@ RCT_ENUM_CONVERTER(FFFCacheControl, (@{
47
47
  FFFastImageSource *imageSource = [[FFFastImageSource alloc] initWithURL:uri priority:priority headers:headers cacheControl:cacheControl];
48
48
  id priorityJSON = json[@"priority"];
49
49
  imageSource.hasPriority = priorityJSON && priorityJSON != (id)kCFNull;
50
+ NSString *cacheKey = [self NSString:json[@"cacheKey"]];
51
+ imageSource.cacheKey = cacheKey.length > 0 ? cacheKey : nil;
50
52
 
51
53
  return imageSource;
52
54
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-fast-image",
3
- "version": "8.15.0",
3
+ "version": "8.16.0",
4
4
  "description": "🚩 FastImage, performant React Native image component.",
5
5
  "keywords": [
6
6
  "cache",