react-native-fast-image 10.7.0 → 10.8.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/android/src/main/java/com/fastimage/FastImageViewConverter.java +10 -8
- package/dist/index.cjs.js +9 -1
- package/dist/index.cjs.js.flow +10 -1
- package/dist/index.cjs.web.js.flow +10 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +9 -1
- package/dist/index.js.flow +10 -1
- package/dist/index.web.js.flow +10 -1
- package/dist/reactNativeCache.d.ts +2 -0
- package/ios/FastImage/FFFastImageModule.mm +1 -1
- package/ios/FastImage/FFFastImageSource.h +8 -0
- package/ios/FastImage/FFFastImageSource.m +64 -1
- package/ios/FastImage/RCTConvert+FFFastImage.m +3 -0
- package/package.json +1 -1
- package/src/index.js.flow +10 -1
- package/src/index.tsx +16 -2
- package/src/index.web.tsx +8 -3
- package/src/reactNativeCache.ts +28 -0
|
@@ -16,7 +16,6 @@ import com.bumptech.glide.load.model.LazyHeaders;
|
|
|
16
16
|
import com.bumptech.glide.request.RequestOptions;
|
|
17
17
|
import com.bumptech.glide.signature.ApplicationVersionSignature;
|
|
18
18
|
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
|
19
|
-
import com.facebook.react.bridge.NoSuchKeyException;
|
|
20
19
|
import com.facebook.react.bridge.ReadableMap;
|
|
21
20
|
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
|
22
21
|
import com.facebook.react.bridge.ReadableType;
|
|
@@ -34,6 +33,10 @@ class FastImageViewConverter {
|
|
|
34
33
|
put("immutable", FastImageCacheControl.IMMUTABLE);
|
|
35
34
|
put("web", FastImageCacheControl.WEB);
|
|
36
35
|
put("cacheOnly", FastImageCacheControl.CACHE_ONLY);
|
|
36
|
+
// React Native's ImageURISource value, which views take. Its
|
|
37
|
+
// others (e.g. 'reload') are immutable, as any value this map
|
|
38
|
+
// doesn't have (see getValueFromSource).
|
|
39
|
+
put("only-if-cached", FastImageCacheControl.CACHE_ONLY);
|
|
37
40
|
}};
|
|
38
41
|
|
|
39
42
|
private static final Map<String, Priority> FAST_IMAGE_PRIORITY_MAP =
|
|
@@ -189,13 +192,12 @@ class FastImageViewConverter {
|
|
|
189
192
|
return value;
|
|
190
193
|
}
|
|
191
194
|
|
|
195
|
+
// A value the map doesn't have (e.g. React Native's `cache: 'reload'`, or
|
|
196
|
+
// one that isn't a string) gets the default, as on iOS.
|
|
192
197
|
private static <T> T getValueFromSource(String propName, String defaultProp, Map<String, T> map, ReadableMap source) {
|
|
193
|
-
String
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
propValue = null;
|
|
198
|
-
}
|
|
199
|
-
return getValue(propName, defaultProp, map, propValue);
|
|
198
|
+
T value = source != null && source.hasKey(propName) && source.getType(propName) == ReadableType.String
|
|
199
|
+
? map.get(source.getString(propName))
|
|
200
|
+
: null;
|
|
201
|
+
return value != null ? value : map.get(defaultProp);
|
|
200
202
|
}
|
|
201
203
|
}
|
package/dist/index.cjs.js
CHANGED
|
@@ -301,6 +301,11 @@ function composeRefs(...refs) {
|
|
|
301
301
|
};
|
|
302
302
|
}
|
|
303
303
|
//#endregion
|
|
304
|
+
//#region src/reactNativeCache.ts
|
|
305
|
+
function warnIfReload(source) {
|
|
306
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && (source === null || source === void 0 ? void 0 : source.cache) === "reload") warnOnce("cache-reload", "react-native-fast-image: `cache: 'reload'` loads the image as the default, `'immutable'`: it isn't loaded again each time. For an image that changes at the same url, use `cache: 'web'` (it follows the server's cache headers), or change its `cacheKey`.", "warn");
|
|
307
|
+
}
|
|
308
|
+
//#endregion
|
|
304
309
|
//#region src/index.tsx
|
|
305
310
|
const resolvePlaceholder = (placeholder) => {
|
|
306
311
|
if (typeof placeholder === "string") {
|
|
@@ -374,7 +379,10 @@ function FastImageBase({ source, placeholder, defaultSource, tintColor, onLoadSt
|
|
|
374
379
|
const fit = resolveObjectFit(objectFit, style);
|
|
375
380
|
const position = objectPositionOrCentered(objectPosition);
|
|
376
381
|
const resolvedSource = (0, react.useMemo)(() => {
|
|
377
|
-
for (const entry of [source].flat())
|
|
382
|
+
for (const entry of [source].flat()) {
|
|
383
|
+
dataUri(entry === null || entry === void 0 ? void 0 : entry.blurDataUri);
|
|
384
|
+
warnIfReload(entry);
|
|
385
|
+
}
|
|
378
386
|
const single = Array.isArray(source) && source.length === 1 ? source[0] : source;
|
|
379
387
|
return Array.isArray(single) ? single.map((entry) => react_native.Image.resolveAssetSource(entry)) : react_native.Image.resolveAssetSource(single);
|
|
380
388
|
}, [source]);
|
package/dist/index.cjs.js.flow
CHANGED
|
@@ -137,6 +137,15 @@ export type FastImageSource = {
|
|
|
137
137
|
scale?: number,
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
// React Native's ImageURISource cache values, which the views' `source` takes
|
|
141
|
+
// so a React Native source works as it is: native reads 'only-if-cached' as
|
|
142
|
+
// 'cacheOnly', and the others as the default, 'immutable'.
|
|
143
|
+
type ReactNativeCache = 'default' | 'reload' | 'force-cache' | 'only-if-cached'
|
|
144
|
+
type ViewSource = {
|
|
145
|
+
...FastImageSource,
|
|
146
|
+
cache?: CacheControls | ReactNativeCache,
|
|
147
|
+
}
|
|
148
|
+
|
|
140
149
|
export type FastImageProps = $ReadOnly<{|
|
|
141
150
|
...ViewProps,
|
|
142
151
|
onError?: ?(event: OnErrorEvent) => void,
|
|
@@ -145,7 +154,7 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
145
154
|
onLoadStart?: ?() => void,
|
|
146
155
|
onProgress?: ?(event: OnProgressEvent) => void,
|
|
147
156
|
|
|
148
|
-
source?: ?(
|
|
157
|
+
source?: ?(ViewSource | Array<ViewSource | number> | number),
|
|
149
158
|
placeholder?: ?(number | string | SourcePlaceholder | PlaceholderSource),
|
|
150
159
|
// Deprecated: use placeholder, which is the same.
|
|
151
160
|
defaultSource?: ?number,
|
|
@@ -137,6 +137,15 @@ export type FastImageSource = {
|
|
|
137
137
|
scale?: number,
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
// React Native's ImageURISource cache values, which the views' `source` takes
|
|
141
|
+
// so a React Native source works as it is: native reads 'only-if-cached' as
|
|
142
|
+
// 'cacheOnly', and the others as the default, 'immutable'.
|
|
143
|
+
type ReactNativeCache = 'default' | 'reload' | 'force-cache' | 'only-if-cached'
|
|
144
|
+
type ViewSource = {
|
|
145
|
+
...FastImageSource,
|
|
146
|
+
cache?: CacheControls | ReactNativeCache,
|
|
147
|
+
}
|
|
148
|
+
|
|
140
149
|
export type FastImageProps = $ReadOnly<{|
|
|
141
150
|
...ViewProps,
|
|
142
151
|
onError?: ?(event: OnErrorEvent) => void,
|
|
@@ -145,7 +154,7 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
145
154
|
onLoadStart?: ?() => void,
|
|
146
155
|
onProgress?: ?(event: OnProgressEvent) => void,
|
|
147
156
|
|
|
148
|
-
source?: ?(
|
|
157
|
+
source?: ?(ViewSource | Array<ViewSource | number> | number),
|
|
149
158
|
placeholder?: ?(number | string | SourcePlaceholder | PlaceholderSource),
|
|
150
159
|
// Deprecated: use placeholder, which is the same.
|
|
151
160
|
defaultSource?: ?number,
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { ColorValue, View, StyleProp, ViewStyle, ImageRequireSource, AccessibilityProps, ViewProps } from 'react-native';
|
|
3
3
|
import { cacheControl, priority, resizeMode } from './constants';
|
|
4
4
|
import { type ObjectPosition } from './objectPosition';
|
|
5
|
+
import { type ReactNativeCache } from './reactNativeCache';
|
|
5
6
|
/** How the image fits the view: see [`objectFit`](#objectfit). */
|
|
6
7
|
export type ObjectFit = 'fill' | 'contain' | 'cover' | 'none' | 'scale-down';
|
|
7
8
|
export type { ObjectPosition, PositionLength } from './objectPosition';
|
|
@@ -152,6 +153,9 @@ export type Source = {
|
|
|
152
153
|
/** With several sources, the scale `width` and `height` are multiplied by. */
|
|
153
154
|
scale?: number;
|
|
154
155
|
};
|
|
156
|
+
type ViewSource = Source | (Omit<Source, 'cache'> & {
|
|
157
|
+
cache?: ReactNativeCache;
|
|
158
|
+
});
|
|
155
159
|
/** A `thumbhash` or `blurDataUri` as `placeholder`. */
|
|
156
160
|
export type SourcePlaceholder = Pick<Source, 'thumbhash' | 'blurDataUri'>;
|
|
157
161
|
/** A url as `placeholder`, loaded like a source (see `placeholder`). */
|
|
@@ -334,7 +338,7 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
|
334
338
|
* - A `require()`d image can be one of the sizes: its size is the file's.
|
|
335
339
|
* - An array of one is the same as that source.
|
|
336
340
|
*/
|
|
337
|
-
source?:
|
|
341
|
+
source?: ViewSource | Array<ViewSource | ImageRequireSource> | ImageRequireSource;
|
|
338
342
|
/**
|
|
339
343
|
* A placeholder shown while the image loads, and if it fails to load:
|
|
340
344
|
*
|
package/dist/index.js
CHANGED
|
@@ -274,6 +274,11 @@ function composeRefs(...refs) {
|
|
|
274
274
|
};
|
|
275
275
|
}
|
|
276
276
|
//#endregion
|
|
277
|
+
//#region src/reactNativeCache.ts
|
|
278
|
+
function warnIfReload(source) {
|
|
279
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && (source === null || source === void 0 ? void 0 : source.cache) === "reload") warnOnce("cache-reload", "react-native-fast-image: `cache: 'reload'` loads the image as the default, `'immutable'`: it isn't loaded again each time. For an image that changes at the same url, use `cache: 'web'` (it follows the server's cache headers), or change its `cacheKey`.", "warn");
|
|
280
|
+
}
|
|
281
|
+
//#endregion
|
|
277
282
|
//#region src/index.tsx
|
|
278
283
|
const resolvePlaceholder = (placeholder) => {
|
|
279
284
|
if (typeof placeholder === "string") {
|
|
@@ -347,7 +352,10 @@ function FastImageBase({ source, placeholder, defaultSource, tintColor, onLoadSt
|
|
|
347
352
|
const fit = resolveObjectFit(objectFit, style);
|
|
348
353
|
const position = objectPositionOrCentered(objectPosition);
|
|
349
354
|
const resolvedSource = useMemo(() => {
|
|
350
|
-
for (const entry of [source].flat())
|
|
355
|
+
for (const entry of [source].flat()) {
|
|
356
|
+
dataUri(entry === null || entry === void 0 ? void 0 : entry.blurDataUri);
|
|
357
|
+
warnIfReload(entry);
|
|
358
|
+
}
|
|
351
359
|
const single = Array.isArray(source) && source.length === 1 ? source[0] : source;
|
|
352
360
|
return Array.isArray(single) ? single.map((entry) => Image.resolveAssetSource(entry)) : Image.resolveAssetSource(single);
|
|
353
361
|
}, [source]);
|
package/dist/index.js.flow
CHANGED
|
@@ -137,6 +137,15 @@ export type FastImageSource = {
|
|
|
137
137
|
scale?: number,
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
// React Native's ImageURISource cache values, which the views' `source` takes
|
|
141
|
+
// so a React Native source works as it is: native reads 'only-if-cached' as
|
|
142
|
+
// 'cacheOnly', and the others as the default, 'immutable'.
|
|
143
|
+
type ReactNativeCache = 'default' | 'reload' | 'force-cache' | 'only-if-cached'
|
|
144
|
+
type ViewSource = {
|
|
145
|
+
...FastImageSource,
|
|
146
|
+
cache?: CacheControls | ReactNativeCache,
|
|
147
|
+
}
|
|
148
|
+
|
|
140
149
|
export type FastImageProps = $ReadOnly<{|
|
|
141
150
|
...ViewProps,
|
|
142
151
|
onError?: ?(event: OnErrorEvent) => void,
|
|
@@ -145,7 +154,7 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
145
154
|
onLoadStart?: ?() => void,
|
|
146
155
|
onProgress?: ?(event: OnProgressEvent) => void,
|
|
147
156
|
|
|
148
|
-
source?: ?(
|
|
157
|
+
source?: ?(ViewSource | Array<ViewSource | number> | number),
|
|
149
158
|
placeholder?: ?(number | string | SourcePlaceholder | PlaceholderSource),
|
|
150
159
|
// Deprecated: use placeholder, which is the same.
|
|
151
160
|
defaultSource?: ?number,
|
package/dist/index.web.js.flow
CHANGED
|
@@ -137,6 +137,15 @@ export type FastImageSource = {
|
|
|
137
137
|
scale?: number,
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
// React Native's ImageURISource cache values, which the views' `source` takes
|
|
141
|
+
// so a React Native source works as it is: native reads 'only-if-cached' as
|
|
142
|
+
// 'cacheOnly', and the others as the default, 'immutable'.
|
|
143
|
+
type ReactNativeCache = 'default' | 'reload' | 'force-cache' | 'only-if-cached'
|
|
144
|
+
type ViewSource = {
|
|
145
|
+
...FastImageSource,
|
|
146
|
+
cache?: CacheControls | ReactNativeCache,
|
|
147
|
+
}
|
|
148
|
+
|
|
140
149
|
export type FastImageProps = $ReadOnly<{|
|
|
141
150
|
...ViewProps,
|
|
142
151
|
onError?: ?(event: OnErrorEvent) => void,
|
|
@@ -145,7 +154,7 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
145
154
|
onLoadStart?: ?() => void,
|
|
146
155
|
onProgress?: ?(event: OnProgressEvent) => void,
|
|
147
156
|
|
|
148
|
-
source?: ?(
|
|
157
|
+
source?: ?(ViewSource | Array<ViewSource | number> | number),
|
|
149
158
|
placeholder?: ?(number | string | SourcePlaceholder | PlaceholderSource),
|
|
150
159
|
// Deprecated: use placeholder, which is the same.
|
|
151
160
|
defaultSource?: ?number,
|
|
@@ -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
|
|
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
|
-
|
|
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
|
}
|
|
@@ -21,6 +21,9 @@ RCT_ENUM_CONVERTER(FFFCacheControl, (@{
|
|
|
21
21
|
@"immutable": @(FFFCacheControlImmutable),
|
|
22
22
|
@"web": @(FFFCacheControlWeb),
|
|
23
23
|
@"cacheOnly": @(FFFCacheControlCacheOnly),
|
|
24
|
+
// React Native's ImageURISource value, which views
|
|
25
|
+
// take. Its others (e.g. 'reload') are the default.
|
|
26
|
+
@"only-if-cached": @(FFFCacheControlCacheOnly),
|
|
24
27
|
}), FFFCacheControlImmutable, integerValue);
|
|
25
28
|
|
|
26
29
|
+ (FFFastImageSource *)FFFastImageSource:(id)json {
|
package/package.json
CHANGED
package/src/index.js.flow
CHANGED
|
@@ -137,6 +137,15 @@ export type FastImageSource = {
|
|
|
137
137
|
scale?: number,
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
// React Native's ImageURISource cache values, which the views' `source` takes
|
|
141
|
+
// so a React Native source works as it is: native reads 'only-if-cached' as
|
|
142
|
+
// 'cacheOnly', and the others as the default, 'immutable'.
|
|
143
|
+
type ReactNativeCache = 'default' | 'reload' | 'force-cache' | 'only-if-cached'
|
|
144
|
+
type ViewSource = {
|
|
145
|
+
...FastImageSource,
|
|
146
|
+
cache?: CacheControls | ReactNativeCache,
|
|
147
|
+
}
|
|
148
|
+
|
|
140
149
|
export type FastImageProps = $ReadOnly<{|
|
|
141
150
|
...ViewProps,
|
|
142
151
|
onError?: ?(event: OnErrorEvent) => void,
|
|
@@ -145,7 +154,7 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
145
154
|
onLoadStart?: ?() => void,
|
|
146
155
|
onProgress?: ?(event: OnProgressEvent) => void,
|
|
147
156
|
|
|
148
|
-
source?: ?(
|
|
157
|
+
source?: ?(ViewSource | Array<ViewSource | number> | number),
|
|
149
158
|
placeholder?: ?(number | string | SourcePlaceholder | PlaceholderSource),
|
|
150
159
|
// Deprecated: use placeholder, which is the same.
|
|
151
160
|
defaultSource?: ?number,
|
package/src/index.tsx
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
placeholderSource,
|
|
24
24
|
} from './placeholders'
|
|
25
25
|
import { composeRefs } from './composeRefs'
|
|
26
|
+
import { type ReactNativeCache, warnIfReload } from './reactNativeCache'
|
|
26
27
|
import { warnIfChildren, warnOnce } from './warnings'
|
|
27
28
|
|
|
28
29
|
/** How the image fits the view: see [`objectFit`](#objectfit). */
|
|
@@ -178,6 +179,14 @@ export type Source = {
|
|
|
178
179
|
scale?: number
|
|
179
180
|
}
|
|
180
181
|
|
|
182
|
+
// What the views' `source` takes: a Source, or one with React Native's cache
|
|
183
|
+
// values (see reactNativeCache.ts), so a React Native `ImageURISource` works as
|
|
184
|
+
// it is. Only the views: preload, getCachePath, writeToCache and placeholder
|
|
185
|
+
// take Fast Image's values.
|
|
186
|
+
type ViewSource =
|
|
187
|
+
| Source
|
|
188
|
+
| (Omit<Source, 'cache'> & { cache?: ReactNativeCache })
|
|
189
|
+
|
|
181
190
|
/** A `thumbhash` or `blurDataUri` as `placeholder`. */
|
|
182
191
|
export type SourcePlaceholder = Pick<Source, 'thumbhash' | 'blurDataUri'>
|
|
183
192
|
|
|
@@ -366,7 +375,10 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
|
366
375
|
* - A `require()`d image can be one of the sizes: its size is the file's.
|
|
367
376
|
* - An array of one is the same as that source.
|
|
368
377
|
*/
|
|
369
|
-
source?:
|
|
378
|
+
source?:
|
|
379
|
+
| ViewSource
|
|
380
|
+
| Array<ViewSource | ImageRequireSource>
|
|
381
|
+
| ImageRequireSource
|
|
370
382
|
/**
|
|
371
383
|
* A placeholder shown while the image loads, and if it fails to load:
|
|
372
384
|
*
|
|
@@ -901,9 +913,11 @@ function FastImageBase({
|
|
|
901
913
|
// only those change. Memoized on source, so an unchanged source reaches
|
|
902
914
|
// the native view as the same object, and React's prop diff stops at it.
|
|
903
915
|
const resolvedSource = useMemo(() => {
|
|
904
|
-
// A blurDataUri that isn't a data uri is ignored, with a warning
|
|
916
|
+
// A blurDataUri that isn't a data uri is ignored, with a warning, and
|
|
917
|
+
// React Native's cache: 'reload' warns (native reads its cache values).
|
|
905
918
|
for (const entry of [source].flat()) {
|
|
906
919
|
dataUri((entry as Source | undefined)?.blurDataUri)
|
|
920
|
+
warnIfReload(entry)
|
|
907
921
|
}
|
|
908
922
|
const single =
|
|
909
923
|
Array.isArray(source) && source.length === 1 ? source[0] : source
|
package/src/index.web.tsx
CHANGED
|
@@ -399,10 +399,13 @@ function assetScale(asset: PackagerAsset): number {
|
|
|
399
399
|
)
|
|
400
400
|
}
|
|
401
401
|
|
|
402
|
+
// What sizedSources reads of a source.
|
|
403
|
+
type SizedSource = Pick<Source, 'uri' | 'width' | 'height' | 'scale'>
|
|
404
|
+
|
|
402
405
|
// One of several sizes: a require()d image in react-native-web's asset
|
|
403
406
|
// registry as a source with its file's size (Expo's web builds make it a
|
|
404
407
|
// { uri, width, height } object already).
|
|
405
|
-
function sizedSource(source:
|
|
408
|
+
function sizedSource(source: SizedSource | number): SizedSource | undefined {
|
|
406
409
|
if (typeof source !== 'number') return source
|
|
407
410
|
const asset: PackagerAsset | undefined = getAssetByID(source)
|
|
408
411
|
return asset
|
|
@@ -422,10 +425,12 @@ function sizedSource(source: Source | number): Source | undefined {
|
|
|
422
425
|
// is laid out at; it only applies to lazy images, and otherwise, or in
|
|
423
426
|
// browsers without it, the next value, 100vw (the viewport's width). src is the largest, for browsers without
|
|
424
427
|
// srcset.
|
|
425
|
-
function sizedSources(entries: (
|
|
428
|
+
function sizedSources(entries: (SizedSource | number)[]) {
|
|
426
429
|
const sources = entries.map(sizedSource)
|
|
427
430
|
const widths = sources
|
|
428
|
-
.filter(
|
|
431
|
+
.filter(
|
|
432
|
+
(source): source is SizedSource => !!source?.uri && !!source.width,
|
|
433
|
+
)
|
|
429
434
|
.map((source) => ({
|
|
430
435
|
uri: source.uri as string,
|
|
431
436
|
width: Math.round((source.width ?? 0) * (source.scale ?? 1)),
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// React Native's `ImageURISource` cache values, which the views' `source`
|
|
2
|
+
// takes so a source typed for React Native's Image works as it is. Not
|
|
3
|
+
// documented: Fast Image's own values are the ones to use. Native reads
|
|
4
|
+
// 'only-if-cached' as 'cacheOnly', and the others as the default, 'immutable'.
|
|
5
|
+
import { warnOnce } from './warnings'
|
|
6
|
+
|
|
7
|
+
declare const __DEV__: boolean | undefined
|
|
8
|
+
|
|
9
|
+
export type ReactNativeCache =
|
|
10
|
+
| 'default'
|
|
11
|
+
| 'reload'
|
|
12
|
+
| 'force-cache'
|
|
13
|
+
| 'only-if-cached'
|
|
14
|
+
|
|
15
|
+
// Warns for a source with 'reload', as the image isn't loaded again each time.
|
|
16
|
+
export function warnIfReload(source: unknown) {
|
|
17
|
+
if (
|
|
18
|
+
typeof __DEV__ !== 'undefined' &&
|
|
19
|
+
__DEV__ &&
|
|
20
|
+
(source as { cache?: unknown } | null | undefined)?.cache === 'reload'
|
|
21
|
+
) {
|
|
22
|
+
warnOnce(
|
|
23
|
+
'cache-reload',
|
|
24
|
+
"react-native-fast-image: `cache: 'reload'` loads the image as the default, `'immutable'`: it isn't loaded again each time. For an image that changes at the same url, use `cache: 'web'` (it follows the server's cache headers), or change its `cacheKey`.",
|
|
25
|
+
'warn',
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
}
|