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.
@@ -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 propValue;
194
- try {
195
- propValue = source != null ? source.getString(propName) : null;
196
- } catch (NoSuchKeyException e) {
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()) dataUri(entry === null || entry === void 0 ? void 0 : entry.blurDataUri);
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]);
@@ -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?: ?(FastImageSource | Array<FastImageSource | number> | number),
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?: ?(FastImageSource | Array<FastImageSource | number> | number),
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?: Source | Array<Source | ImageRequireSource> | ImageRequireSource;
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()) dataUri(entry === null || entry === void 0 ? void 0 : entry.blurDataUri);
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]);
@@ -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?: ?(FastImageSource | Array<FastImageSource | number> | number),
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?: ?(FastImageSource | Array<FastImageSource | number> | number),
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,
@@ -0,0 +1,2 @@
1
+ export type ReactNativeCache = 'default' | 'reload' | 'force-cache' | 'only-if-cached';
2
+ export declare function warnIfReload(source: unknown): void;
@@ -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
  }
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-fast-image",
3
- "version": "10.7.0",
3
+ "version": "10.8.1",
4
4
  "description": "FastImage, performant React Native image component.",
5
5
  "keywords": [
6
6
  "cache",
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?: ?(FastImageSource | Array<FastImageSource | number> | number),
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?: Source | Array<Source | ImageRequireSource> | ImageRequireSource
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: Source | number): Source | undefined {
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: (Source | number)[]) {
428
+ function sizedSources(entries: (SizedSource | number)[]) {
426
429
  const sources = entries.map(sizedSource)
427
430
  const widths = sources
428
- .filter((source): source is Source => !!source?.uri && !!source.width)
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
+ }