react-native-fast-image 8.18.1 → 8.19.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 +67 -2
- package/android/build.gradle +1 -0
- package/android/consumer-rules.pro +30 -4
- package/android/src/main/java/com/dylanvann/fastimage/FastImageCacheLimits.java +101 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageCacheWrite.java +111 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageGlideModule.java +14 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageKeyedGlideUrl.java +7 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java +4 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewConverter.java +10 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewModule.java +207 -81
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewPackage.java +2 -0
- package/app.plugin.js +62 -0
- package/dist/index.cjs.js +2 -0
- package/dist/index.cjs.js.flow +18 -0
- package/dist/index.d.ts +31 -1
- package/dist/index.js +2 -0
- package/dist/index.js.flow +18 -0
- package/ios/FastImage/FFFastImageSource.h +6 -3
- package/ios/FastImage/FFFastImageSource.m +30 -0
- package/ios/FastImage/FFFastImageViewManager.m +294 -79
- package/package.json +2 -1
|
@@ -5,11 +5,97 @@
|
|
|
5
5
|
#import <SDWebImage/SDWebImageManager.h>
|
|
6
6
|
#import <SDWebImage/SDWebImageError.h>
|
|
7
7
|
#import <SDWebImage/SDWebImagePrefetcher.h>
|
|
8
|
+
#import <SDWebImage/NSData+ImageContentType.h>
|
|
9
|
+
|
|
10
|
+
// configureCache's limits, by their names in JS. Each is set in the app's
|
|
11
|
+
// Info.plist (FastImageMaxDiskSize, ...) or changed at runtime, which is saved
|
|
12
|
+
// (FastImage.maxDiskSize, ...) and wins until it's reset with null.
|
|
13
|
+
static NSArray<NSString *> *FFFCacheLimitNames(void)
|
|
14
|
+
{
|
|
15
|
+
return @[@"maxDiskSize", @"maxDiskAge", @"maxMemorySize"];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static NSString *FFFSavedCacheLimitKey(NSString *name)
|
|
19
|
+
{
|
|
20
|
+
return [@"FastImage." stringByAppendingString:name];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// A limit's value: the one saved by configureCache, or the app's Info.plist's,
|
|
24
|
+
// or nil for SDWebImage's default.
|
|
25
|
+
static NSNumber *FFFCacheLimit(NSString *name)
|
|
26
|
+
{
|
|
27
|
+
id saved = [NSUserDefaults.standardUserDefaults objectForKey:FFFSavedCacheLimitKey(name)];
|
|
28
|
+
if ([saved isKindOfClass:NSNumber.class]) {
|
|
29
|
+
return saved;
|
|
30
|
+
}
|
|
31
|
+
NSString *infoKey = [NSString stringWithFormat:@"FastImage%@%@",
|
|
32
|
+
[name substringToIndex:1].uppercaseString, [name substringFromIndex:1]];
|
|
33
|
+
id info = [NSBundle.mainBundle objectForInfoDictionaryKey:infoKey];
|
|
34
|
+
if ([info isKindOfClass:NSNumber.class]) {
|
|
35
|
+
return info;
|
|
36
|
+
}
|
|
37
|
+
if ([info isKindOfClass:NSString.class]) {
|
|
38
|
+
return @([info doubleValue]);
|
|
39
|
+
}
|
|
40
|
+
return nil;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Sets a limit in SDWebImage's cache config: 0 for no limit, nil for
|
|
44
|
+
// SDWebImage's default. maxDiskAge counts from when an image was last used
|
|
45
|
+
// (SDWebImage's default from 5.21; before, from when it was stored).
|
|
46
|
+
static void FFFSetCacheLimit(SDImageCacheConfig *config, NSString *name, NSNumber *value)
|
|
47
|
+
{
|
|
48
|
+
double limit = MAX(value.doubleValue, 0);
|
|
49
|
+
if ([name isEqualToString:@"maxDiskSize"]) {
|
|
50
|
+
config.maxDiskSize = limit;
|
|
51
|
+
} else if ([name isEqualToString:@"maxMemorySize"]) {
|
|
52
|
+
config.maxMemoryCost = limit;
|
|
53
|
+
} else if (value) {
|
|
54
|
+
// SDWebImage never expires images with a negative age (with 0 it
|
|
55
|
+
// would remove them all).
|
|
56
|
+
config.maxDiskAge = limit > 0 ? limit : -1;
|
|
57
|
+
config.diskCacheExpireType = SDImageCacheConfigExpireTypeAccessDate;
|
|
58
|
+
} else {
|
|
59
|
+
config.maxDiskAge = 60 * 60 * 24 * 7;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// The bytes of the files in a folder.
|
|
64
|
+
static unsigned long long FFFFolderSize(NSString *path)
|
|
65
|
+
{
|
|
66
|
+
unsigned long long size = 0;
|
|
67
|
+
NSDirectoryEnumerator<NSURL *> *files = [NSFileManager.defaultManager enumeratorAtURL:[NSURL fileURLWithPath:path isDirectory:YES]
|
|
68
|
+
includingPropertiesForKeys:@[NSURLFileSizeKey]
|
|
69
|
+
options:0
|
|
70
|
+
errorHandler:nil];
|
|
71
|
+
for (NSURL *file in files) {
|
|
72
|
+
NSNumber *fileSize;
|
|
73
|
+
[file getResourceValue:&fileSize forKey:NSURLFileSizeKey error:nil];
|
|
74
|
+
size += fileSize.unsignedLongLongValue;
|
|
75
|
+
}
|
|
76
|
+
return size;
|
|
77
|
+
}
|
|
8
78
|
|
|
9
79
|
@implementation FFFastImageViewManager
|
|
10
80
|
|
|
11
81
|
RCT_EXPORT_MODULE(FastImageView)
|
|
12
82
|
|
|
83
|
+
// The app's cache limits (saved or in its Info.plist), before FastImage loads
|
|
84
|
+
// any image: this class is used first, to create the views and for preload.
|
|
85
|
+
+ (void)initialize
|
|
86
|
+
{
|
|
87
|
+
if (self != [FFFastImageViewManager class]) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
SDImageCacheConfig *config = SDImageCache.sharedImageCache.config;
|
|
91
|
+
for (NSString *name in FFFCacheLimitNames()) {
|
|
92
|
+
NSNumber *value = FFFCacheLimit(name);
|
|
93
|
+
if (value) {
|
|
94
|
+
FFFSetCacheLimit(config, name, value);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
13
99
|
- (FFFastImageView*)view {
|
|
14
100
|
return [[FFFastImageView alloc] init];
|
|
15
101
|
}
|
|
@@ -68,6 +154,20 @@ static SDWebImageOptions FFFPreloadOptions(FFFastImageSource *source)
|
|
|
68
154
|
return options | [source cacheOptions];
|
|
69
155
|
}
|
|
70
156
|
|
|
157
|
+
// The key a source's image is in SDWebImage's cache under: its cacheKey
|
|
158
|
+
// (without needing a uri), or its url as a view converts it. `web` images
|
|
159
|
+
// ignore cacheKey. nil without either.
|
|
160
|
+
static NSString *FFFDiskCacheKey(FFFastImageSource *source)
|
|
161
|
+
{
|
|
162
|
+
if (source.cacheKey.length > 0 && source.cacheControl != FFFCacheControlWeb) {
|
|
163
|
+
return source.cacheKey;
|
|
164
|
+
}
|
|
165
|
+
if (!source.url) {
|
|
166
|
+
return nil;
|
|
167
|
+
}
|
|
168
|
+
return [SDWebImageManager.sharedManager cacheKeyForURL:source.url context:FFFPreloadContext(source)];
|
|
169
|
+
}
|
|
170
|
+
|
|
71
171
|
static NSUInteger FFFPreloadLimit(void)
|
|
72
172
|
{
|
|
73
173
|
NSUInteger limit = [SDWebImagePrefetcher sharedImagePrefetcher].maxConcurrentPrefetchCount;
|
|
@@ -89,6 +189,64 @@ static void FFFStartPendingPreloads(void)
|
|
|
89
189
|
}
|
|
90
190
|
}
|
|
91
191
|
|
|
192
|
+
// Loads the source in the preloads' queue, then calls completed once with its
|
|
193
|
+
// image or error. Only on the main queue (SDWebImageManager calls its
|
|
194
|
+
// completion blocks there); call FFFStartPendingPreloads after adding loads.
|
|
195
|
+
static void FFFQueueLoad(FFFastImageSource *source,
|
|
196
|
+
SDWebImageOptions options,
|
|
197
|
+
SDWebImageContext *context,
|
|
198
|
+
void (^completed)(UIImage *image, NSError *error))
|
|
199
|
+
{
|
|
200
|
+
if (!FFFPendingPreloads) {
|
|
201
|
+
FFFPendingPreloads = [NSMutableArray array];
|
|
202
|
+
}
|
|
203
|
+
[FFFPendingPreloads addObject:[^{
|
|
204
|
+
// Once per slot: SDWebImage calls this once with finished set (a
|
|
205
|
+
// progressive load calls it more, without).
|
|
206
|
+
__block BOOL done = NO;
|
|
207
|
+
[[SDWebImageManager sharedManager] loadImageWithURL:source.url
|
|
208
|
+
options:options
|
|
209
|
+
context:context
|
|
210
|
+
progress:nil
|
|
211
|
+
completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
|
|
212
|
+
if (!finished || done) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
done = YES;
|
|
216
|
+
if (error) {
|
|
217
|
+
[source forgetResponseAfterError:error];
|
|
218
|
+
}
|
|
219
|
+
FFFPreloadsInFlight--;
|
|
220
|
+
completed(image, error);
|
|
221
|
+
FFFStartPendingPreloads();
|
|
222
|
+
}];
|
|
223
|
+
} copy]];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Calls found with the path of the file cached under key, or missing if it
|
|
227
|
+
// isn't on disk. On the main queue.
|
|
228
|
+
static void FFFFindCachedFile(NSString *key, void (^found)(NSString *path), dispatch_block_t missing)
|
|
229
|
+
{
|
|
230
|
+
SDImageCache *cache = SDImageCache.sharedImageCache;
|
|
231
|
+
[cache diskImageExistsWithKey:key completion:^(BOOL exists) {
|
|
232
|
+
if (exists) {
|
|
233
|
+
found([cache cachePathForKey:key]);
|
|
234
|
+
} else {
|
|
235
|
+
missing();
|
|
236
|
+
}
|
|
237
|
+
}];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
static NSDictionary *FFFFailure(NSString *error)
|
|
241
|
+
{
|
|
242
|
+
return @{@"ok": @NO, @"error": error};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
static NSDictionary *FFFFile(NSString *path)
|
|
246
|
+
{
|
|
247
|
+
return @{@"ok": @YES, @"path": path};
|
|
248
|
+
}
|
|
249
|
+
|
|
92
250
|
// Resolves with a result per source, in order, once all have loaded or failed:
|
|
93
251
|
// { ok, width, height } or { ok: false, error }. Never rejects. A source with
|
|
94
252
|
// memoryCache false is only stored on disk, without decoding it (its size
|
|
@@ -100,9 +258,6 @@ RCT_EXPORT_METHOD(preload:(nonnull NSArray<FFFastImageSource *> *)sources
|
|
|
100
258
|
// This runs on the UIManager queue; the queue state is only touched on
|
|
101
259
|
// the main queue.
|
|
102
260
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
103
|
-
if (!FFFPendingPreloads) {
|
|
104
|
-
FFFPendingPreloads = [NSMutableArray array];
|
|
105
|
-
}
|
|
106
261
|
NSMutableArray *results = [NSMutableArray arrayWithCapacity:sources.count];
|
|
107
262
|
// Sources of this call still to finish. It starts at the number of
|
|
108
263
|
// sources, so the promise can't resolve before they've all been added.
|
|
@@ -117,7 +272,7 @@ RCT_EXPORT_METHOD(preload:(nonnull NSArray<FFFastImageSource *> *)sources
|
|
|
117
272
|
if (!source.url) {
|
|
118
273
|
// An empty, missing or null uri (JS sends null sources as {}).
|
|
119
274
|
// It fails without taking a slot.
|
|
120
|
-
[results addObject
|
|
275
|
+
[results addObject:FFFFailure(@"Invalid source: no uri")];
|
|
121
276
|
finishOne();
|
|
122
277
|
return;
|
|
123
278
|
}
|
|
@@ -130,30 +285,12 @@ RCT_EXPORT_METHOD(preload:(nonnull NSArray<FFFastImageSource *> *)sources
|
|
|
130
285
|
options |= SDWebImageAvoidDecodeImage;
|
|
131
286
|
context[SDWebImageContextStoreCacheType] = @(SDImageCacheTypeDisk);
|
|
132
287
|
}
|
|
133
|
-
[
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
context:[context copy]
|
|
140
|
-
progress:nil
|
|
141
|
-
completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
|
|
142
|
-
if (!finished || done) {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
done = YES;
|
|
146
|
-
if (error) {
|
|
147
|
-
[source forgetResponseAfterError:error];
|
|
148
|
-
}
|
|
149
|
-
results[idx] = image
|
|
150
|
-
? @{@"ok": @YES, @"width": @(image.size.width), @"height": @(image.size.height)}
|
|
151
|
-
: @{@"ok": @NO, @"error": FFFErrorMessage(error)};
|
|
152
|
-
FFFPreloadsInFlight--;
|
|
153
|
-
finishOne();
|
|
154
|
-
FFFStartPendingPreloads();
|
|
155
|
-
}];
|
|
156
|
-
} copy]];
|
|
288
|
+
FFFQueueLoad(source, options, [context copy], ^(UIImage *image, NSError *error) {
|
|
289
|
+
results[idx] = image
|
|
290
|
+
? @{@"ok": @YES, @"width": @(image.size.width), @"height": @(image.size.height)}
|
|
291
|
+
: FFFFailure(FFFErrorMessage(error));
|
|
292
|
+
finishOne();
|
|
293
|
+
});
|
|
157
294
|
}];
|
|
158
295
|
|
|
159
296
|
if (sources.count == 0) {
|
|
@@ -167,77 +304,155 @@ RCT_EXPORT_METHOD(preload:(nonnull NSArray<FFFastImageSource *> *)sources
|
|
|
167
304
|
// The source's downloaded file in the disk cache, downloading it first if it
|
|
168
305
|
// isn't there (without decoding it or keeping it in memory, and in the
|
|
169
306
|
// preloads' queue): { ok, path } or { ok: false, error }. Never rejects. With
|
|
170
|
-
// `cacheOnly
|
|
307
|
+
// `cacheOnly`, or a cacheKey without a uri, it doesn't download. A local
|
|
308
|
+
// file is its own path.
|
|
171
309
|
RCT_EXPORT_METHOD(getCachePath:(FFFastImageSource *)source
|
|
172
310
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
173
311
|
reject:(__unused RCTPromiseRejectBlock)reject)
|
|
174
312
|
{
|
|
175
313
|
if (!source.url) {
|
|
176
|
-
|
|
314
|
+
NSString *key = FFFDiskCacheKey(source);
|
|
315
|
+
if (!key) {
|
|
316
|
+
resolve(FFFFailure(@"Invalid source: no uri or cacheKey"));
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
FFFFindCachedFile(key, ^(NSString *path) {
|
|
320
|
+
resolve(FFFFile(path));
|
|
321
|
+
}, ^{
|
|
322
|
+
resolve(FFFFailure(@"Not in the disk cache"));
|
|
323
|
+
});
|
|
177
324
|
return;
|
|
178
325
|
}
|
|
179
326
|
if (source.url.isFileURL) {
|
|
180
327
|
BOOL exists = [NSFileManager.defaultManager fileExistsAtPath:source.url.path];
|
|
181
|
-
resolve(exists
|
|
182
|
-
? @{@"ok": @YES, @"path": source.url.path}
|
|
183
|
-
: @{@"ok": @NO, @"error": @"No file at this uri"});
|
|
328
|
+
resolve(exists ? FFFFile(source.url.path) : FFFFailure(@"No file at this uri"));
|
|
184
329
|
return;
|
|
185
330
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
SDImageCache *cache = SDImageCache.sharedImageCache;
|
|
190
|
-
void (^resolveFromDisk)(NSString *) = ^(NSString *error) {
|
|
191
|
-
[cache diskImageExistsWithKey:key completion:^(BOOL exists) {
|
|
192
|
-
resolve(exists
|
|
193
|
-
? @{@"ok": @YES, @"path": [cache cachePathForKey:key]}
|
|
194
|
-
: @{@"ok": @NO, @"error": error});
|
|
195
|
-
}];
|
|
331
|
+
NSString *key = FFFDiskCacheKey(source);
|
|
332
|
+
void (^resolveFile)(NSString *) = ^(NSString *path) {
|
|
333
|
+
resolve(FFFFile(path));
|
|
196
334
|
};
|
|
197
|
-
|
|
198
|
-
// On the main queue, where the preloads' queue is used.
|
|
199
|
-
if (exists) {
|
|
200
|
-
resolve(@{@"ok": @YES, @"path": [cache cachePathForKey:key]});
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
335
|
+
FFFFindCachedFile(key, resolveFile, ^{
|
|
203
336
|
if (source.cacheControl == FFFCacheControlCacheOnly) {
|
|
204
|
-
resolve(@
|
|
337
|
+
resolve(FFFFailure(@"Not in the disk cache"));
|
|
205
338
|
return;
|
|
206
339
|
}
|
|
207
|
-
if (!FFFPendingPreloads) {
|
|
208
|
-
FFFPendingPreloads = [NSMutableArray array];
|
|
209
|
-
}
|
|
210
340
|
// As a disk-only preload: not decoded, and not looked up in or kept in
|
|
211
|
-
// memory (an image in memory may not be on disk). It
|
|
212
|
-
// file is written.
|
|
341
|
+
// memory (an image in memory may not be on disk). It completes once
|
|
342
|
+
// the file is written.
|
|
213
343
|
SDWebImageOptions options = FFFPreloadOptions(source) | SDWebImageAvoidDecodeImage | SDWebImageWaitStoreCache;
|
|
344
|
+
SDWebImageMutableContext *context = FFFPreloadContext(source);
|
|
214
345
|
context[SDWebImageContextQueryCacheType] = @(SDImageCacheTypeDisk);
|
|
215
346
|
context[SDWebImageContextStoreCacheType] = @(SDImageCacheTypeDisk);
|
|
216
|
-
[
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
done = YES;
|
|
227
|
-
FFFPreloadsInFlight--;
|
|
228
|
-
FFFStartPendingPreloads();
|
|
229
|
-
if (error) {
|
|
230
|
-
[source forgetResponseAfterError:error];
|
|
231
|
-
}
|
|
232
|
-
if (!image) {
|
|
233
|
-
resolve(@{@"ok": @NO, @"error": FFFErrorMessage(error)});
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
resolveFromDisk(@"Not stored in the disk cache");
|
|
237
|
-
}];
|
|
238
|
-
} copy]];
|
|
347
|
+
FFFQueueLoad(source, options, [context copy], ^(UIImage *image, NSError *error) {
|
|
348
|
+
if (!image) {
|
|
349
|
+
resolve(FFFFailure(FFFErrorMessage(error)));
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
FFFFindCachedFile(key, resolveFile, ^{
|
|
353
|
+
resolve(FFFFailure(@"Not stored in the disk cache"));
|
|
354
|
+
});
|
|
355
|
+
});
|
|
239
356
|
FFFStartPendingPreloads();
|
|
240
|
-
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Stores a local image file as the source's image in the disk cache, so views
|
|
361
|
+
// and preloads of the source load it without downloading it, and resolves
|
|
362
|
+
// with its cached file: { ok, path } or { ok: false, error }. Never rejects.
|
|
363
|
+
// It doesn't replace an image that's already cached (Glide can't on Android;
|
|
364
|
+
// a new image should get a new cacheKey), and doesn't store `web` sources
|
|
365
|
+
// (Android keeps them only in an HTTP cache).
|
|
366
|
+
RCT_EXPORT_METHOD(writeToCache:(FFFastImageSource *)source
|
|
367
|
+
file:(NSString *)file
|
|
368
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
369
|
+
reject:(__unused RCTPromiseRejectBlock)reject)
|
|
370
|
+
{
|
|
371
|
+
if (source.cacheControl == FFFCacheControlWeb) {
|
|
372
|
+
resolve(FFFFailure(@"Can't store cache: 'web' images (they're kept in an HTTP cache)"));
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
// A cacheKey is enough (the image doesn't need a url yet); otherwise a
|
|
376
|
+
// remote url.
|
|
377
|
+
NSString *scheme = source.url.scheme.lowercaseString;
|
|
378
|
+
if (source.cacheKey.length == 0 && ![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) {
|
|
379
|
+
resolve(FFFFailure(@"Invalid source: no remote uri or cacheKey"));
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
// A file:// url or a path.
|
|
383
|
+
NSURL *fileURL = [RCTConvert NSURL:file];
|
|
384
|
+
if (!fileURL.isFileURL) {
|
|
385
|
+
resolve(FFFFailure(@"Not a local file"));
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
NSString *key = FFFDiskCacheKey(source);
|
|
389
|
+
FFFFindCachedFile(key, ^(NSString *path) {
|
|
390
|
+
resolve(FFFFailure(@"Already in the disk cache"));
|
|
391
|
+
}, ^{
|
|
392
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
393
|
+
NSData *data = [NSData dataWithContentsOfURL:fileURL];
|
|
394
|
+
if (!data) {
|
|
395
|
+
resolve(FFFFailure(@"Can't read the file"));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if ([NSData sd_imageFormatForImageData:data] == SDImageFormatUndefined) {
|
|
399
|
+
resolve(FFFFailure(@"Not an image"));
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
SDImageCache *cache = SDImageCache.sharedImageCache;
|
|
403
|
+
[cache storeImageDataToDisk:data forKey:key];
|
|
404
|
+
// An image a view showed under this key before, which isn't on
|
|
405
|
+
// disk anymore.
|
|
406
|
+
[cache removeImageFromMemoryForKey:key];
|
|
407
|
+
resolve(FFFFile([cache cachePathForKey:key]));
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Changes cache limits at runtime and saves them, so they're used on the next
|
|
413
|
+
// launches too: a number (0 for no limit), or null to go back to the app's
|
|
414
|
+
// Info.plist or SDWebImage's default. A lower disk limit applies at once (what's
|
|
415
|
+
// over it is removed; the disk cache is trimmed to half of maxDiskSize when
|
|
416
|
+
// it's over), instead of when the app next goes to the background. Resolves
|
|
417
|
+
// with the limits in effect (0 for no limit), and diskSize: the bytes the disk
|
|
418
|
+
// cache uses now.
|
|
419
|
+
RCT_EXPORT_METHOD(configureCache:(NSDictionary *)limits
|
|
420
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
421
|
+
reject:(__unused RCTPromiseRejectBlock)reject)
|
|
422
|
+
{
|
|
423
|
+
SDImageCache *cache = SDImageCache.sharedImageCache;
|
|
424
|
+
SDImageCacheConfig *config = cache.config;
|
|
425
|
+
NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
|
|
426
|
+
NSUInteger maxDiskSize = config.maxDiskSize;
|
|
427
|
+
NSTimeInterval maxDiskAge = config.maxDiskAge;
|
|
428
|
+
for (NSString *name in FFFCacheLimitNames()) {
|
|
429
|
+
id value = limits[name];
|
|
430
|
+
if (value == (id)kCFNull) {
|
|
431
|
+
[defaults removeObjectForKey:FFFSavedCacheLimitKey(name)];
|
|
432
|
+
} else if ([value isKindOfClass:NSNumber.class]) {
|
|
433
|
+
[defaults setObject:value forKey:FFFSavedCacheLimitKey(name)];
|
|
434
|
+
} else {
|
|
435
|
+
// Not given: unchanged.
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
FFFSetCacheLimit(config, name, FFFCacheLimit(name));
|
|
439
|
+
}
|
|
440
|
+
void (^finish)(void) = ^{
|
|
441
|
+
// Not on the disk cache's queue, which image loads use.
|
|
442
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
|
|
443
|
+
resolve(@{
|
|
444
|
+
@"maxDiskSize": @(config.maxDiskSize),
|
|
445
|
+
@"maxDiskAge": @(MAX(config.maxDiskAge, 0)),
|
|
446
|
+
@"maxMemorySize": @(config.maxMemoryCost),
|
|
447
|
+
@"diskSize": @(FFFFolderSize(cache.diskCachePath)),
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
};
|
|
451
|
+
if (config.maxDiskSize != maxDiskSize || config.maxDiskAge != maxDiskAge) {
|
|
452
|
+
[cache deleteOldFilesWithCompletionBlock:finish];
|
|
453
|
+
} else {
|
|
454
|
+
finish();
|
|
455
|
+
}
|
|
241
456
|
}
|
|
242
457
|
|
|
243
458
|
RCT_EXPORT_METHOD(clearMemoryCache:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-fast-image",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.19.0",
|
|
4
4
|
"description": "🚩 FastImage, performant React Native image component.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cache",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"ios/FastImage",
|
|
30
30
|
"ios/FastImage.xcodeproj/project.pbxproj",
|
|
31
31
|
"ios/FastImage.xcodeproj/xcshareddata",
|
|
32
|
+
"app.plugin.js",
|
|
32
33
|
"dist",
|
|
33
34
|
"RNFastImage.podspec"
|
|
34
35
|
],
|