react-native-fast-image 9.3.7 → 10.0.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.
Files changed (76) hide show
  1. package/README.md +87 -61
  2. package/RNFastImage.podspec +14 -9
  3. package/android/build.gradle +21 -47
  4. package/android/consumer-rules.pro +7 -7
  5. package/android/src/main/AndroidManifest.xml +1 -2
  6. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageAnimatable.java +1 -1
  7. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageAnimated.java +2 -2
  8. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageApng.java +2 -2
  9. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageBlur.java +4 -6
  10. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageCacheControl.java +1 -1
  11. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageCacheLimits.java +4 -1
  12. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageCacheWrite.java +1 -1
  13. package/android/src/main/java/com/fastimage/FastImageCrop.java +92 -0
  14. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageEvent.java +2 -3
  15. package/android/src/main/java/com/fastimage/FastImageEvents.java +74 -0
  16. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageGif.java +1 -1
  17. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageGlide.java +3 -3
  18. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageGlideModule.java +1 -1
  19. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageKeyedGlideUrl.java +1 -1
  20. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageOkHttpProgressGlideModule.java +36 -9
  21. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageRequestListener.java +6 -1
  22. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSharedDownloads.java +1 -1
  23. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSource.java +1 -1
  24. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSourceSize.java +12 -2
  25. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSvg.java +3 -3
  26. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSvgRenderer.java +1 -1
  27. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageUrl.java +1 -1
  28. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewConverter.java +7 -8
  29. package/android/src/main/java/com/fastimage/FastImageViewManager.java +591 -0
  30. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewModule.java +31 -28
  31. package/android/src/main/java/com/fastimage/FastImageViewPackage.java +51 -0
  32. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewWithUrl.java +352 -126
  33. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageWebGlideUrl.java +1 -1
  34. package/app.plugin.js +2 -7
  35. package/dist/index.cjs.js +276 -157
  36. package/dist/index.cjs.js.flow +33 -17
  37. package/dist/index.cjs.web.js +211 -61
  38. package/dist/index.cjs.web.js.flow +33 -17
  39. package/dist/index.d.ts +83 -58
  40. package/dist/index.js +278 -159
  41. package/dist/index.js.flow +33 -17
  42. package/dist/index.web.js +212 -62
  43. package/dist/index.web.js.flow +33 -17
  44. package/dist/objectFit.d.ts +2 -2
  45. package/dist/objectPosition.d.ts +42 -0
  46. package/dist/specs/FastImageViewNativeComponent.d.ts +48 -0
  47. package/dist/specs/NativeFastImageModule.d.ts +11 -0
  48. package/dist/warnings.d.ts +3 -0
  49. package/ios/FastImage/FFFDownsampledImage.h +4 -8
  50. package/ios/FastImage/FFFDownsampledImage.m +0 -34
  51. package/ios/FastImage/FFFastImageModule.h +8 -0
  52. package/ios/FastImage/{FFFastImageViewManager.m → FFFastImageModule.mm} +70 -59
  53. package/ios/FastImage/FFFastImageView.h +36 -7
  54. package/ios/FastImage/FFFastImageView.m +201 -164
  55. package/ios/FastImage/FFFastImageViewComponentView.h +11 -0
  56. package/ios/FastImage/FFFastImageViewComponentView.mm +325 -0
  57. package/ios/FastImage/RCTConvert+FFFastImage.h +1 -1
  58. package/ios/FastImage/RCTConvert+FFFastImage.m +7 -10
  59. package/package.json +28 -9
  60. package/src/constants.ts +28 -0
  61. package/src/index.js.flow +179 -0
  62. package/src/index.tsx +1103 -0
  63. package/src/index.web.tsx +548 -0
  64. package/src/objectFit.ts +48 -0
  65. package/src/objectPosition.ts +243 -0
  66. package/src/specs/FastImageViewNativeComponent.ts +83 -0
  67. package/src/specs/NativeFastImageModule.ts +35 -0
  68. package/src/warnings.ts +37 -0
  69. package/android/src/main/AndroidManifestNew.xml +0 -3
  70. package/android/src/main/java/com/dylanvann/fastimage/FastImageCookieHandler.java +0 -63
  71. package/android/src/main/java/com/dylanvann/fastimage/FastImageEvents.java +0 -49
  72. package/android/src/main/java/com/dylanvann/fastimage/FastImageProgressListener.java +0 -18
  73. package/android/src/main/java/com/dylanvann/fastimage/FastImageShadowNode.java +0 -21
  74. package/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java +0 -379
  75. package/android/src/main/java/com/dylanvann/fastimage/FastImageViewPackage.java +0 -27
  76. package/ios/FastImage/FFFastImageViewManager.h +0 -5
package/src/index.tsx ADDED
@@ -0,0 +1,1103 @@
1
+ import React, { forwardRef, memo } from 'react'
2
+ import {
3
+ ColorValue,
4
+ View,
5
+ Image,
6
+ StyleSheet,
7
+ StyleProp,
8
+ ViewStyle,
9
+ ImageRequireSource,
10
+ Platform,
11
+ AccessibilityProps,
12
+ ViewProps,
13
+ } from 'react-native'
14
+ import { cacheControl, priority, resizeMode } from './constants'
15
+ import { fromStyle, resolveObjectFit } from './objectFit'
16
+ import { objectPositionOrCentered, type ObjectPosition } from './objectPosition'
17
+ import FastImageView from './specs/FastImageViewNativeComponent'
18
+ import NativeFastImageModule from './specs/NativeFastImageModule'
19
+ import { warnIfChildren, warnOnce } from './warnings'
20
+
21
+ /** How the image fits the view: see [`objectFit`](#objectfit). */
22
+ export type ObjectFit = 'fill' | 'contain' | 'cover' | 'none' | 'scale-down'
23
+
24
+ export type { ObjectPosition, PositionLength } from './objectPosition'
25
+
26
+ /** An image's load priority: see [`source.priority`](#sourcepriority). */
27
+ export type Priority = 'low' | 'normal' | 'high'
28
+
29
+ /** How fresh an image must be: see [`source.cache`](#sourcecache). */
30
+ export type Cache = 'immutable' | 'web' | 'cacheOnly'
31
+
32
+ /** A remote image to load, and how to load and cache it. */
33
+ export type Source = {
34
+ /**
35
+ * Remote url to load the image from. e.g. `'https://example.com/image.jpg'`.
36
+ *
37
+ * Also loads local files (`file://`, and on Android `content://`), images
38
+ * in the app by name (e.g. `'my_image'`: in its asset catalog on iOS, a
39
+ * drawable on Android), photo library images on iOS and tvOS (`ph://`,
40
+ * see [Photo library images](#photo-library-images-ios-and-tvos)) and SVG
41
+ * images (see [SVG images](docs/formats.md#svg-images)).
42
+ */
43
+ uri?: string
44
+ /** Headers to load the image with. e.g. `{ Authorization: 'someAuthToken' }`. */
45
+ headers?: { [key: string]: string }
46
+ /**
47
+ * A hint for which images to start loading first when several are
48
+ * waiting: `'high'` ones before `'normal'` ones, and `'low'` ones after.
49
+ * It's best effort, not an order: several images load at once, and when
50
+ * each finishes depends on its size and the network.
51
+ *
52
+ * - `'low'`: e.g. images further down a list.
53
+ * - `'normal'`: the default.
54
+ * - `'high'`: e.g. the image the screen is about.
55
+ *
56
+ * @default 'normal'
57
+ */
58
+ priority?: Priority
59
+ /**
60
+ * How fresh the image must be. See
61
+ * [how caching is handled](docs/how-is-caching-handled.md) for how the
62
+ * options fit together.
63
+ *
64
+ * - `'immutable'`: loads the image once, then shows the cached copy until
65
+ * its url (or `cacheKey`) changes.
66
+ * - `'web'`: follows the server's HTTP cache headers, as a browser does,
67
+ * checking with the server when it loads. These responses are kept in
68
+ * their own HTTP cache (50 MB on each platform), which `clearDiskCache`
69
+ * also clears.
70
+ * - `'cacheOnly'`: only shows a cached image, without making a request.
71
+ *
72
+ * @default 'immutable'
73
+ */
74
+ cache?: Cache
75
+ /**
76
+ * The key the image is cached under, instead of its uri. Use it for urls
77
+ * that change while the image stays the same, e.g. signed urls with a
78
+ * token or an expiry. Use something that identifies the image, and change
79
+ * it when the image changes (e.g. include a version or the time it was
80
+ * updated), or the old image keeps showing.
81
+ *
82
+ * Not used with `cache: 'web'`, which follows the HTTP cache (keyed by
83
+ * url).
84
+ *
85
+ * @example
86
+ * ```jsx
87
+ * <FastImage
88
+ * source={{
89
+ * uri: signedUrl,
90
+ * cacheKey: `avatar-${user.id}-${user.avatarUpdatedAt}`,
91
+ * }}
92
+ * />
93
+ * ```
94
+ */
95
+ cacheKey?: string
96
+ /**
97
+ * Whether the decoded image is kept in the memory cache. With `false`
98
+ * it's only kept on disk: a view doesn't leave it in memory once it stops
99
+ * showing it, and `FastImage.preload` downloads it without decoding it.
100
+ * Use it for large images that are shown once or rarely, like a
101
+ * full-screen photo: a decoded photo can take tens of MB of memory. Images
102
+ * shown again, like a list scrolled back, are decoded from disk again.
103
+ *
104
+ * @default true
105
+ */
106
+ memoryCache?: boolean
107
+ /**
108
+ * With several sources (`source` as an array), the image's width at this
109
+ * uri, in pixels (times `scale`, if given).
110
+ */
111
+ width?: number
112
+ /**
113
+ * With several sources (`source` as an array), the image's height at this
114
+ * uri, in pixels (times `scale`, if given).
115
+ */
116
+ height?: number
117
+ /** With several sources, the scale `width` and `height` are multiplied by. */
118
+ scale?: number
119
+ }
120
+
121
+ /** How the image fades in: see [`transition`](#transition). */
122
+ export type Transition = {
123
+ /**
124
+ * How long the fade takes, in milliseconds; 0 means no fade. Defaults to
125
+ * the default transition's (see `FastImage.setDefaults`) if it fades,
126
+ * otherwise the platform's usual length: 300 ms on Android, 250 ms on iOS.
127
+ */
128
+ duration?: number
129
+ /**
130
+ * Also fades between images: a new `source` replacing an image that's
131
+ * showing cross-dissolves from it. Otherwise only an image that appears
132
+ * over nothing (or over `defaultSource`) fades in, and a new source
133
+ * replaces the image showing at once, once it has loaded.
134
+ * @default false
135
+ */
136
+ betweenImages?: boolean
137
+ /**
138
+ * Skips the fade for an image from a cache, so images already loaded
139
+ * show at once, e.g. in a list scrolled back up or a reused row.
140
+ *
141
+ * - `'memory'`: skips it for images from the memory cache.
142
+ * - `'all'`: skips it for images from the memory or disk cache too, so
143
+ * images that download fade in, and local files and bundled images
144
+ * usually only the first time (the disk cache usually keeps them too).
145
+ * - `'none'`: always fades.
146
+ *
147
+ * On Android, a `cache: 'web'` image from its HTTP cache, without a request,
148
+ * counts as one from the memory cache (these images aren't kept there).
149
+ * @default 'memory'
150
+ */
151
+ skipOnCacheHit?: 'none' | 'memory' | 'all' | null
152
+ }
153
+
154
+ /**
155
+ * `onLoad`'s event: `nativeEvent` has the image's own `width` and `height`
156
+ * (see `onLoad`), and `target`, the view's React tag (on iOS and Android; not
157
+ * on the web).
158
+ */
159
+ export interface OnLoadEvent {
160
+ nativeEvent: {
161
+ width: number
162
+ height: number
163
+ target?: number
164
+ }
165
+ }
166
+
167
+ /**
168
+ * `onError`'s event: `nativeEvent.error` says what went wrong, e.g. an HTTP
169
+ * status code or an image that can't be decoded.
170
+ */
171
+ export interface OnErrorEvent {
172
+ nativeEvent: {
173
+ error: string
174
+ }
175
+ }
176
+
177
+ /**
178
+ * A load's result, as `onLoadEnd` gets it: `ok` with the image's size, or not
179
+ * `ok` with the error (as `onLoad` and `onError` get them).
180
+ */
181
+ export type LoadResult =
182
+ | { ok: true; width: number; height: number }
183
+ | { ok: false; error: string }
184
+
185
+ /**
186
+ * `configureCache`'s changes: a number (0 for no limit), or `null` to go back
187
+ * to the app's native config (Info.plist, AndroidManifest.xml) or the
188
+ * platform's default. Leave one out to keep it. Changes are saved, and used on
189
+ * the next launches too. Android only has `maxDiskSize`.
190
+ */
191
+ export interface CacheLimits {
192
+ /**
193
+ * The most bytes of images kept on disk. When it's over, the least
194
+ * recently used are removed (on iOS, until it's half this size). Default:
195
+ * no limit on iOS, 250 MB on Android.
196
+ */
197
+ maxDiskSize?: number | null
198
+ /**
199
+ * iOS: the seconds an image is kept on disk after it was last used, or 0
200
+ * to keep them until `maxDiskSize` removes them. Default: 1 week.
201
+ */
202
+ maxDiskAge?: number | null
203
+ /**
204
+ * iOS: the most bytes of decoded images kept in memory. Default: no limit
205
+ * (they're removed when the system is low on memory).
206
+ */
207
+ maxMemorySize?: number | null
208
+ }
209
+
210
+ /**
211
+ * `configureCache`'s result: the limits in effect (0 for no limit), and the
212
+ * bytes the disk cache uses now. Android only has `maxDiskSize` and
213
+ * `diskSize`, and neither if the app has its own `AppGlideModule` (or
214
+ * expo-image, which has one).
215
+ */
216
+ export interface CacheState {
217
+ maxDiskSize?: number
218
+ maxDiskAge?: number
219
+ maxMemorySize?: number
220
+ /** The bytes the disk cache uses now. */
221
+ diskSize?: number
222
+ }
223
+
224
+ /**
225
+ * `getCachePath`'s and `writeToCache`'s result: `ok` with the file's path, or
226
+ * not `ok` with the error.
227
+ */
228
+ export type CachePathResult =
229
+ | { ok: true; path: string }
230
+ | { ok: false; error: string }
231
+
232
+ /**
233
+ * `clearMemoryCache`'s and `clearDiskCache`'s result: `ok` once the cache is
234
+ * cleared, or not `ok` with the error.
235
+ */
236
+ export type ClearCacheResult = { ok: true } | { ok: false; error: string }
237
+
238
+ /**
239
+ * `onProgress`'s event: `nativeEvent` has the bytes `loaded` and the `total`,
240
+ * and `progress`, `loaded / total` from 0 to 1.
241
+ */
242
+ export interface OnProgressEvent {
243
+ nativeEvent: {
244
+ loaded: number
245
+ total: number
246
+ progress: number
247
+ }
248
+ }
249
+
250
+ // Extends ViewStyle rather than FlexStyle/TransformsStyle/ShadowStyleIOS, which
251
+ // React Native's Strict TypeScript API (types_generated: opt-in from 0.80, the
252
+ // default from 0.87) doesn't export. Only the image's own keys are added; the
253
+ // rest (radii, opacity, colors) come from ViewStyle, so they're the app's
254
+ // React Native types (e.g. string radii, Animated values).
255
+ export interface ImageStyle extends ViewStyle {
256
+ tintColor?: ColorValue
257
+ objectFit?: ObjectFit
258
+ }
259
+
260
+ export interface FastImageProps extends AccessibilityProps, ViewProps {
261
+ /**
262
+ * Source for the remote image to load: a `Source`, a `require()`d image,
263
+ * or an array of the same image at different sizes.
264
+ *
265
+ * When `source` changes, the image that's showing stays until the new one
266
+ * has loaded. In views that get reused for other content, such as rows in
267
+ * FlashList or recyclerlistview, set `recyclingKey` so a reused row
268
+ * doesn't show the previous row's image.
269
+ *
270
+ * **Several sizes of an image.** `source` can also be an array of the
271
+ * same image at different sizes, each with its `width` and `height` in
272
+ * pixels (times `scale`, if it has one). The view loads the one whose size
273
+ * is closest to its own, in pixels, so a small view downloads a small
274
+ * image:
275
+ *
276
+ * ```jsx
277
+ * <FastImage
278
+ * style={{ width: 120, height: 120 }}
279
+ * source={[
280
+ * { uri: 'https://example.com/photo-200.jpg', width: 200, height: 200 },
281
+ * { uri: 'https://example.com/photo-800.jpg', width: 800, height: 800 },
282
+ * ]}
283
+ * />
284
+ * ```
285
+ *
286
+ * - The view picks once it has been laid out. One that has no size (e.g.
287
+ * sized from `onLoad`) loads the largest.
288
+ * - When the view's size changes so that another size fits better, it
289
+ * loads that one (with its load events), and keeps showing the current
290
+ * image until then, without a `transition`.
291
+ * - Each size is cached separately. A `cacheKey` applies to its own entry:
292
+ * give each size its own key (or none), or the sizes would replace each
293
+ * other in the cache.
294
+ * - `FastImage.preload`, `getCachePath` and `writeToCache` take one size:
295
+ * pass the one a view will show. An array fails with
296
+ * `{ ok: false, error }`.
297
+ * - An array of one is the same as that source.
298
+ */
299
+ source?: Source | Source[] | ImageRequireSource
300
+ /**
301
+ * An asset loaded with `require(...)`, shown while the first image loads,
302
+ * and if an image fails to load. When `source` changes, the previous image
303
+ * shows while the new one loads instead (see `source` and
304
+ * `recyclingKey`).
305
+ *
306
+ * On Android, `defaultSource` doesn't show in debug builds: there the dev
307
+ * server serves `require()`d images, and `defaultSource` is only loaded
308
+ * from the app's resources.
309
+ */
310
+ defaultSource?: ImageRequireSource
311
+ /**
312
+ * How the image fits the view, as CSS's `object-fit` does.
313
+ *
314
+ * - `'cover'`: scales it uniformly (keeping its aspect ratio) so it covers
315
+ * the view (inside its borders), cropping what doesn't fit.
316
+ * - `'contain'`: scales it uniformly (keeping its aspect ratio) so all of
317
+ * it fits in the view (inside its borders).
318
+ * - `'fill'`: scales its width and height separately to fill the view,
319
+ * which can change its aspect ratio.
320
+ * - `'none'`: shows it at its own size, centered, cropped if it's larger
321
+ * than the view.
322
+ * - `'scale-down'`: shows it at its own size, centered, or scaled down
323
+ * uniformly to fit if it's larger than the view (the smaller of `'none'`
324
+ * and `'contain'`).
325
+ *
326
+ * An image's own size is its size in pixels, as points (dp on Android),
327
+ * as CSS counts an image's pixels: a 300 × 200 image is 300 × 200 points.
328
+ * A bundled image (`require()`) is its size in points.
329
+ *
330
+ * It can also be set in `style`; the prop wins.
331
+ *
332
+ * @default 'cover'
333
+ */
334
+ objectFit?: ObjectFit
335
+ /**
336
+ * Where the image is in the view, as CSS's `object-position` places it:
337
+ * where its edges are when it doesn't fill the view exactly (`contain`,
338
+ * `cover`, `none` and `scale-down`; with `fill` it always fills it).
339
+ *
340
+ * - A string in CSS's syntax: one or two keywords (`'top'`,
341
+ * `'left bottom'`), lengths in points (`'10px 20px'`, with `px`) or
342
+ * percentages (`'25% 75%'`), or offsets from an edge
343
+ * (`'right 10px bottom 20%'`). A percentage puts that point of the image
344
+ * at that point of the view: `'0% 0%'` is the top-left corner,
345
+ * `'100% 100%'` the bottom-right.
346
+ * - An object with one of `top` and `bottom` and one of `left` and
347
+ * `right`, each in points (a number) or a percentage (`'25%'`), e.g.
348
+ * `{ right: 10, bottom: '20%' }`. A missing axis is centered.
349
+ *
350
+ * Points are dp on Android. An invalid value is ignored (the image is
351
+ * centered), with a warning in development. `defaultSource` is placed
352
+ * the same way.
353
+ *
354
+ * @default '50% 50%'
355
+ * @example
356
+ * ```jsx
357
+ * <FastImage source={{ uri }} objectFit="cover" objectPosition="top" />
358
+ * ```
359
+ */
360
+ objectPosition?: ObjectPosition
361
+ /**
362
+ * For views that get reused for other content, such as rows in FlashList
363
+ * or recyclerlistview. Set it to something that identifies the content,
364
+ * e.g. the item's id. When it changes, the image is cleared right away (to
365
+ * `defaultSource`, or blank) instead of staying until the new one has
366
+ * loaded. Unlike changing `key`, this keeps the view, which is what list
367
+ * recycling is for.
368
+ *
369
+ * @example
370
+ * ```jsx
371
+ * <FastImage recyclingKey={item.id} source={{ uri: item.imageUrl }} />
372
+ * ```
373
+ */
374
+ recyclingKey?: string | null
375
+ /**
376
+ * How many times an animated image (GIF, animated WebP, APNG, or animated
377
+ * AVIF) plays:
378
+ *
379
+ * - Not set: as many times as the file says (like a browser).
380
+ * - `true`: forever.
381
+ * - `false`: once.
382
+ * - A number: that many times.
383
+ *
384
+ * Changing it restarts the animation.
385
+ */
386
+ loop?: boolean | number
387
+ /**
388
+ * How the image is filtered when it's drawn smaller or larger than its
389
+ * size (like CSS's `image-rendering`):
390
+ *
391
+ * - `'auto'`: the platform's usual filtering.
392
+ * - `'smooth'`: iOS only. Keeps a large image drawn much smaller than its
393
+ * size (e.g. a big photo as a thumbnail, or fine lines and text) from
394
+ * looking jagged or noisy. Such an image is already decoded at about the
395
+ * view's size by default (see `downsample`), so this is for images shown
396
+ * at less than half their size with `downsample={false}`, or a little
397
+ * smaller than their size. **It uses more memory:** the image is also
398
+ * kept at smaller sizes for drawing, about a third more than the decoded
399
+ * image. On Android it's the same as `'auto'`.
400
+ * - `'pixelated'`: sharp pixels, without smoothing, e.g. for pixel art
401
+ * drawn larger than its size. On Android, animated images are still
402
+ * smoothed.
403
+ *
404
+ * @default 'auto'
405
+ */
406
+ imageRendering?: 'auto' | 'smooth' | 'pixelated'
407
+ /**
408
+ * Pauses an animated image (GIF, animated WebP, APNG, or animated AVIF) on
409
+ * the frame it's showing; `false` plays it again from there
410
+ * (on Android, an animated WebP or AVIF plays again from its first frame:
411
+ * Android can't resume one). Each image animates on its own, so pausing
412
+ * one doesn't pause others showing the same file.
413
+ */
414
+ paused?: boolean
415
+ /**
416
+ * Fades the image in when it loads. `true` fades for the default's
417
+ * duration (see `FastImage.setDefaults`), or the platform's usual length
418
+ * if the default doesn't fade; a number is the duration in milliseconds,
419
+ * `false` (or 0) is no fade, or pass a `Transition`.
420
+ *
421
+ * By default, images fade in on Android (300 ms), and not on iOS.
422
+ * `FastImage.setDefaults` changes that for every image; an image's own
423
+ * `transition` wins, and the settings it leaves out come from the
424
+ * default. `null` is the default.
425
+ *
426
+ * Downloads, local files (`file://`, `content://`) and bundled images
427
+ * (`require()`) fade in. In lists that reuse views (e.g. FlashList), set
428
+ * `recyclingKey`, so a reused view starts empty and its image fades in,
429
+ * instead of showing the previous item's image until it loads.
430
+ *
431
+ * @example
432
+ * ```jsx
433
+ * <FastImage source={{ uri }} transition />
434
+ * <FastImage source={{ uri }} transition={500} />
435
+ * <FastImage source={{ uri }} transition={{ betweenImages: true, skipOnCacheHit: 'none' }} />
436
+ * ```
437
+ */
438
+ transition?: boolean | number | Transition | null
439
+ /**
440
+ * Decodes a large image at about the size it's shown at, instead of at
441
+ * full size, so it takes much less memory.
442
+ *
443
+ * - `true`: an image at least twice the size its view needs is decoded
444
+ * at about the view's size (on Android, any image larger than its
445
+ * view). If the view grows, the image is decoded again for its new size
446
+ * (from the disk cache).
447
+ * - `false`: images are decoded at full size, e.g. for an image that's
448
+ * zoomed in on with a transform (a pinch-to-zoom viewer), which would
449
+ * otherwise show the smaller copy enlarged. **It uses more memory:** a
450
+ * 12-megapixel photo takes about 48 MB decoded, however small it's
451
+ * shown. On Android, an image over 16 megapixels (4096 × 4096) is
452
+ * decoded at the largest size under that, which Android can still
453
+ * draw.
454
+ *
455
+ * It doesn't change `onLoad`'s width and height (the image's own size) or
456
+ * the cached file. Photo library images are always decoded at about the
457
+ * view's size. Android 16 and later decode animated WebP at full size
458
+ * either way, and scale it as they draw it.
459
+ *
460
+ * If you control the images, serve them at the size they're shown
461
+ * (resized on your server or by an image CDN), which also saves
462
+ * bandwidth.
463
+ *
464
+ * @default true
465
+ */
466
+ downsample?: boolean
467
+ // The radius is converted from points to the bitmap's pixels on both
468
+ // platforms, so it matches React Native's Image.
469
+ /**
470
+ * Blurs the image by this radius, in points (the same radius looks about
471
+ * the same on iOS and Android). `0` is no blur.
472
+ *
473
+ * It's for still images, or a radius that changes now and then (e.g.
474
+ * blurring a photo behind a sheet). Each change blurs the image again on
475
+ * the CPU, so don't animate it.
476
+ *
477
+ * - Only the loaded image is blurred, not `defaultSource`.
478
+ * - An animated image (GIF, animated WebP, APNG or AVIF) shows its first frame,
479
+ * blurred, and doesn't animate.
480
+ * - With `tintColor`, the blurred image is tinted.
481
+ * - The image is blurred at about the size it's shown at, off the main
482
+ * thread. The cached file stays the original image, so `getCachePath`
483
+ * and other views of it aren't affected.
484
+ * - Changing it blurs the image that's showing again, without sending the
485
+ * load events again.
486
+ *
487
+ * To animate a blur, or to blur an animated image, use React Native's
488
+ * `filter` style instead, which the GPU draws:
489
+ * `style={{ filter: [{ blur: 6 }] }}`. React Native's docs list `blur` for Android 12+ only; on iOS it's behind
490
+ * an experimental React Native feature flag (`enableSwiftUIBasedFilters`,
491
+ * SwiftUI-based filters).
492
+ *
493
+ * @default 0
494
+ */
495
+ blurRadius?: number
496
+
497
+ /** Called when the image starts to load. */
498
+ onLoadStart?(): void
499
+
500
+ /**
501
+ * Called while the image downloads, with the bytes `loaded` so far, the
502
+ * `total`, and `progress` (`loaded / total`, from 0 to 1; the last event
503
+ * has 1). Not called while the total is unknown (a response without a
504
+ * `Content-Length`).
505
+ *
506
+ * @example
507
+ * ```jsx
508
+ * onProgress={e => console.log(e.nativeEvent.progress)}
509
+ * ```
510
+ */
511
+ onProgress?(event: OnProgressEvent): void
512
+
513
+ /**
514
+ * Called on a successful image fetch. Called with the width and height of
515
+ * the image itself, not of the view (on iOS, in points: an `@2x` asset
516
+ * reports half its pixel size).
517
+ *
518
+ * @example
519
+ * ```jsx
520
+ * onLoad={e => console.log(e.nativeEvent.width, e.nativeEvent.height)}
521
+ * ```
522
+ */
523
+ onLoad?(event: OnLoadEvent): void
524
+
525
+ /**
526
+ * Called on an image fetching error, with a message describing it (e.g.
527
+ * the HTTP status code). On iOS and Android, a download that gets nothing
528
+ * from the server for 15 seconds fails too (on Android, unless the app's
529
+ * OkHttp client has timeouts of its own).
530
+ *
531
+ * @example
532
+ * ```jsx
533
+ * onError={e => console.log(e.nativeEvent.error)}
534
+ * ```
535
+ */
536
+ onError?(event: OnErrorEvent): void
537
+
538
+ /**
539
+ * Called when the image finishes loading, whether it was successful or an
540
+ * error, with the result: `{ ok: true, width, height }` (the image's size,
541
+ * as `onLoad` gets it) or `{ ok: false, error }` (as `onError` gets it).
542
+ * TypeScript makes you check `ok` before reading the size or the error.
543
+ *
544
+ * @example
545
+ * ```jsx
546
+ * <FastImage
547
+ * source={{ uri }}
548
+ * onLoadEnd={(result) => {
549
+ * if (result.ok) setAspectRatio(result.width / result.height)
550
+ * else setFailed(result.error)
551
+ * }}
552
+ * />
553
+ * ```
554
+ */
555
+ onLoadEnd?(result: LoadResult): void
556
+
557
+ /**
558
+ * The image's style: View's style props (`borderRadius` clips the image,
559
+ * and borders inset it, but padding doesn't), and `tintColor`, as with
560
+ * React Native's `Image` (the `tintColor` prop wins).
561
+ */
562
+ style?: StyleProp<ImageStyle>
563
+
564
+ /**
565
+ * If supplied, changes the color of all the non-transparent pixels to the
566
+ * given color.
567
+ */
568
+ tintColor?: ColorValue
569
+
570
+ /**
571
+ * FastImage is a single native view, which doesn't take children (since
572
+ * 10.0): use `FastImageBackground` for content over an image.
573
+ *
574
+ * @hidden
575
+ */
576
+ children?: "FastImage doesn't take children: use FastImageBackground for content over an image"
577
+ }
578
+
579
+ const resolveDefaultSource = (
580
+ defaultSource?: ImageRequireSource,
581
+ ): string | object | null => {
582
+ if (!defaultSource) {
583
+ return null
584
+ }
585
+ const resolved = Image.resolveAssetSource(defaultSource)
586
+ if (!resolved) {
587
+ return null
588
+ }
589
+ // Android takes the uri, and loads it as a Drawable with React Native's
590
+ // helpers; iOS the image source ({ uri, width, height, scale }).
591
+ return Platform.OS === 'android' ? resolved.uri : resolved
592
+ }
593
+
594
+ // The native loopCount: -1 for the file's own, 0 for forever, or a number of
595
+ // plays. Always sent, since native would reset a removed prop to 0 (forever).
596
+ function loopCount(loop: boolean | number | undefined) {
597
+ if (loop === undefined) return -1
598
+ if (loop === true) return 0
599
+ if (loop === false) return 1
600
+ return Number.isFinite(loop) ? Math.max(1, Math.floor(loop)) : 0
601
+ }
602
+
603
+ // onLoadEnd's result from the native event (ok with the image's size, or not
604
+ // ok with the error).
605
+ function loadResult(event: {
606
+ ok?: boolean
607
+ width?: number
608
+ height?: number
609
+ error?: string
610
+ }): LoadResult {
611
+ return event.ok
612
+ ? { ok: true, width: event.width ?? 0, height: event.height ?? 0 }
613
+ : { ok: false, error: event.error ?? 'Failed to load the image' }
614
+ }
615
+
616
+ // The native view's handledEvents (see specs/FastImageViewNativeComponent.ts):
617
+ // the events with a handler, which are the only ones it sends.
618
+ function handledEvents({
619
+ onLoadStart,
620
+ onProgress,
621
+ onLoad,
622
+ onError,
623
+ onLoadEnd,
624
+ }: FastImageProps) {
625
+ return (
626
+ (onLoadStart ? 1 : 0) |
627
+ (onProgress ? 2 : 0) |
628
+ (onLoad ? 4 : 0) |
629
+ (onError ? 8 : 0) |
630
+ (onLoadEnd ? 16 : 0)
631
+ )
632
+ }
633
+
634
+ // The platform's usual fade length: Glide's and React Native's Image's on
635
+ // Android, Core Animation's default on iOS.
636
+ function usualFadeMs() {
637
+ return Platform.OS === 'ios' ? 250 : 300
638
+ }
639
+
640
+ // The native transition props. Always sent, so removing `transition` goes
641
+ // back to the default.
642
+ type TransitionProps = {
643
+ transitionDuration: number
644
+ transitionBetweenImages: boolean
645
+ transitionSkipOnCacheHit: string
646
+ }
647
+
648
+ const NO_TRANSITION: TransitionProps = {
649
+ transitionDuration: 0,
650
+ transitionBetweenImages: false,
651
+ transitionSkipOnCacheHit: 'memory',
652
+ }
653
+
654
+ // A transition (an image's, or setDefaults') over `base`: the settings it
655
+ // leaves out come from base, and null (or undefined) is base. true, or a
656
+ // missing duration, is base's duration if it fades, otherwise the platform's
657
+ // usual one.
658
+ function resolveTransition(
659
+ transition: FastImageProps['transition'],
660
+ base: TransitionProps,
661
+ ): TransitionProps {
662
+ if (transition == null) return base
663
+ if (transition === false) return { ...base, transitionDuration: 0 }
664
+ const usual =
665
+ base.transitionDuration > 0 ? base.transitionDuration : usualFadeMs()
666
+ if (transition === true) return { ...base, transitionDuration: usual }
667
+ const { duration, betweenImages, skipOnCacheHit }: Transition =
668
+ typeof transition === 'number' ? { duration: transition } : transition
669
+ const resolvedDuration = duration ?? usual
670
+ return {
671
+ transitionDuration:
672
+ typeof resolvedDuration === 'number' && resolvedDuration > 0
673
+ ? resolvedDuration
674
+ : 0,
675
+ transitionBetweenImages: betweenImages ?? base.transitionBetweenImages,
676
+ transitionSkipOnCacheHit:
677
+ skipOnCacheHit ?? base.transitionSkipOnCacheHit,
678
+ }
679
+ }
680
+
681
+ // The built-in default: Android fades images in, as React Native's Image does;
682
+ // iOS doesn't, as UIImageView and SDWebImage don't.
683
+ function builtInTransition() {
684
+ return resolveTransition(Platform.OS === 'android', NO_TRANSITION)
685
+ }
686
+
687
+ // The default transition set with FastImage.setDefaults (undefined: the
688
+ // built-in one).
689
+ let defaultTransition: TransitionProps | undefined
690
+
691
+ // React Native's global: true in development (Metro and React Native's Jest
692
+ // preset set it), false in release builds.
693
+ declare const __DEV__: boolean
694
+
695
+ function FastImageBase({
696
+ source,
697
+ defaultSource,
698
+ tintColor,
699
+ onLoadStart,
700
+ onProgress,
701
+ onLoad,
702
+ onError,
703
+ onLoadEnd,
704
+ style,
705
+ children,
706
+ objectFit,
707
+ objectPosition,
708
+ loop,
709
+ transition,
710
+ forwardedRef,
711
+ ...viewProps
712
+ }: FastImageProps & { forwardedRef: React.Ref<any> }) {
713
+ const {
714
+ fallback,
715
+ resizeMode: _resizeMode,
716
+ ...props
717
+ } = viewProps as typeof viewProps & {
718
+ // Removed in 10.0 (React Native's Image instead of FastImage's view).
719
+ fallback?: unknown
720
+ // Removed in 10.0 (objectFit instead): ignored, so it reaches no
721
+ // native prop.
722
+ resizeMode?: unknown
723
+ }
724
+ if (__DEV__ && fallback !== undefined) {
725
+ warnOnce(
726
+ 'fallback',
727
+ "react-native-fast-image: `fallback` was removed in 10.0, and does nothing: images always load natively. Remove it, or use React Native's Image for those images.",
728
+ 'warn',
729
+ )
730
+ }
731
+ // FastImage is a single native view: children aren't rendered (as with
732
+ // React Native's Image).
733
+ if (__DEV__) {
734
+ warnIfChildren(children)
735
+ }
736
+ // tintColor can also be set in style, as with React Native's Image. The
737
+ // prop wins.
738
+ const resolvedTintColor =
739
+ tintColor != null ? tintColor : fromStyle(style, 'tintColor')
740
+ const fit = resolveObjectFit(objectFit, style)
741
+ const position = objectPositionOrCentered(objectPosition)
742
+
743
+ // Several sources are picked from natively, for the view's size; one in
744
+ // an array is a plain source.
745
+ const sources = Array.isArray(source)
746
+ ? source.length === 1
747
+ ? undefined
748
+ : source
749
+ : undefined
750
+ const single = Array.isArray(source) ? source[0] : source
751
+ const resolvedSource = sources
752
+ ? undefined
753
+ : Image.resolveAssetSource(single as any)
754
+ const resolvedDefaultSource = resolveDefaultSource(defaultSource)
755
+
756
+ return (
757
+ <FastImageView
758
+ {...props}
759
+ ref={forwardedRef}
760
+ // Clips the image to borderRadius by default, as React Native's
761
+ // Image does.
762
+ style={[styles.image, style]}
763
+ // null (no key) as undefined, which the native prop takes.
764
+ recyclingKey={props.recyclingKey ?? undefined}
765
+ loopCount={loopCount(loop)}
766
+ {...resolveTransition(
767
+ transition,
768
+ defaultTransition ?? builtInTransition(),
769
+ )}
770
+ source={resolvedSource}
771
+ sources={sources}
772
+ defaultSource={resolvedDefaultSource}
773
+ onFastImageLoadStart={onLoadStart}
774
+ onFastImageProgress={onProgress}
775
+ // The native views only send the events with a handler (and only
776
+ // track progress with onProgress).
777
+ handledEvents={handledEvents({
778
+ onLoadStart,
779
+ onProgress,
780
+ onLoad,
781
+ onError,
782
+ onLoadEnd,
783
+ })}
784
+ onFastImageLoad={onLoad}
785
+ onFastImageError={onError}
786
+ onFastImageLoadEnd={
787
+ onLoadEnd &&
788
+ ((event: { nativeEvent: any }) =>
789
+ onLoadEnd(loadResult(event.nativeEvent)))
790
+ }
791
+ // After style, whose tintColor and objectFit reach the native
792
+ // view's props too: these win.
793
+ tintColor={resolvedTintColor}
794
+ objectFit={fit}
795
+ objectPositionX={position.x.fraction}
796
+ objectPositionXOffset={position.x.offset}
797
+ objectPositionY={position.y.fraction}
798
+ objectPositionYOffset={position.y.offset}
799
+ />
800
+ )
801
+ }
802
+
803
+ const FastImageMemo = memo(FastImageBase)
804
+
805
+ // What a ref to FastImage or FastImageBackground gets: FastImage's native
806
+ // view, or FastImageBackground's view. Typed as a View's (they have the same
807
+ // methods, e.g. measure). ComponentRef, as
808
+ // React Native's types declare View as a class, and its Strict TypeScript API
809
+ // (opt-in from 0.80, the default from 0.87) as a function component that takes
810
+ // a ref.
811
+ type ViewRef = React.ComponentRef<typeof View>
812
+
813
+ const FastImageComponent: React.ForwardRefExoticComponent<
814
+ FastImageProps & React.RefAttributes<ViewRef>
815
+ > = forwardRef((props: FastImageProps, ref: React.Ref<ViewRef>) => (
816
+ <FastImageMemo forwardedRef={ref} {...props} />
817
+ ))
818
+
819
+ FastImageComponent.displayName = 'FastImage'
820
+
821
+ /** A preloaded source that loaded (and is now cached). */
822
+ export interface PreloadSuccess {
823
+ /** The source's uri. */
824
+ uri: string
825
+ ok: true
826
+ /** The image's width (as in `onLoad`). */
827
+ width: number
828
+ /** The image's height (as in `onLoad`). */
829
+ height: number
830
+ }
831
+
832
+ /** A preloaded source that failed to load. */
833
+ export interface PreloadFailure {
834
+ /** The source's uri (none for a source without one, e.g. `null`). */
835
+ uri?: string
836
+ ok: false
837
+ /** What went wrong. */
838
+ error: string
839
+ }
840
+
841
+ /**
842
+ * `preload`'s result for a source. Check `ok` to tell which it is: e.g.
843
+ * `if (result.ok)` narrows it to a `PreloadSuccess`, with its size. Reading
844
+ * `width` or `error` without checking is a type error.
845
+ */
846
+ export type PreloadResult = PreloadSuccess | PreloadFailure
847
+
848
+ // A result as native sends it (without the uri).
849
+ type NativePreloadResult =
850
+ | Omit<PreloadSuccess, 'uri'>
851
+ | Omit<PreloadFailure, 'uri'>
852
+
853
+ const noResult: NativePreloadResult = { ok: false, error: 'No result' }
854
+
855
+ // The native module, or an error saying why it's missing, thrown when a
856
+ // function is called (as React Native's own optional modules do), so
857
+ // importing FastImage works without it (Jest, Expo Go).
858
+ function nativeModule(): NonNullable<typeof NativeFastImageModule> {
859
+ if (!NativeFastImageModule) {
860
+ throw new Error(
861
+ "react-native-fast-image: the native module FastImageModule isn't " +
862
+ 'in this app. Rebuild the app after installing the package ' +
863
+ '(run pod install on iOS). It needs the New Architecture, and ' +
864
+ "isn't in Expo Go: use a development build. In Jest, mock the " +
865
+ 'function (e.g. jest.spyOn(FastImage, "preload")).',
866
+ )
867
+ }
868
+ return NativeFastImageModule
869
+ }
870
+
871
+ export interface FastImageStaticProperties {
872
+ /**
873
+ * @deprecated `resizeMode` was removed in 10.0, and a `resizeMode` prop
874
+ * is ignored: use `objectFit` instead (`stretch` is `fill`, `center` is
875
+ * `scale-down`). Kept so code that reads it doesn't throw.
876
+ */
877
+ resizeMode: typeof resizeMode
878
+ /**
879
+ * @deprecated Use the strings instead, e.g. `priority: 'high'`.
880
+ */
881
+ priority: typeof priority
882
+ /**
883
+ * @deprecated Use the strings instead, e.g. `cache: 'immutable'`.
884
+ */
885
+ cacheControl: typeof cacheControl
886
+ preload: (sources: Source[]) => Promise<PreloadResult[]>
887
+ /**
888
+ * Removes every image from the memory cache. Resolves once it's done, or
889
+ * not `ok` with the error if it couldn't (Android: Glide failed to
890
+ * start). Never rejects.
891
+ */
892
+ clearMemoryCache: () => Promise<ClearCacheResult>
893
+ /**
894
+ * Removes every image from the disk cache, and the HTTP cache of
895
+ * `cache: 'web'` images. Resolves once it's done, or not `ok` with the
896
+ * error if it couldn't (Android: Glide failed to start). Never rejects.
897
+ */
898
+ clearDiskCache: () => Promise<ClearCacheResult>
899
+ /**
900
+ * The path of the source's downloaded file in the disk cache, downloading
901
+ * it first if it isn't there (without decoding it). With
902
+ * `cache: 'cacheOnly'`, or a `cacheKey` without a `uri`, it doesn't
903
+ * download. Never rejects.
904
+ */
905
+ getCachePath: (source: Source) => Promise<CachePathResult>
906
+ /**
907
+ * Stores a local image file (a `file://` uri or a path, or on Android a
908
+ * `content://` uri) as the source's image in the disk cache, so views and
909
+ * preloads of the source show it without downloading it. A source with a
910
+ * `cacheKey` doesn't need a `uri`. Resolves with its cached file. Doesn't
911
+ * replace an image that's already cached, and isn't for `cache: 'web'`
912
+ * sources. Never rejects.
913
+ */
914
+ writeToCache: (source: Source, file: string) => Promise<CachePathResult>
915
+ /**
916
+ * Changes the cache's limits at runtime (only those given; 0 for no
917
+ * limit, null to go back to the app's native config) and saves them, then
918
+ * resolves with the limits in effect and the disk cache's size; without
919
+ * limits, it only resolves with them. iOS applies changes at once.
920
+ * Android only has `maxDiskSize`, applied when Glide starts: at once if
921
+ * it hasn't started yet in this launch, otherwise from the next launch.
922
+ * FastImage starts it when its first view is created (before the view has
923
+ * a source), and for `preload`, `getCachePath`, `writeToCache`,
924
+ * `clearDiskCache` and `configureCache` itself, even without limits (e.g.
925
+ * to read the disk cache's size).
926
+ */
927
+ configureCache: (limits?: CacheLimits) => Promise<CacheState>
928
+ /**
929
+ * Changes the defaults of every image (only those given; null goes back
930
+ * to the built-in one). An image already showing uses a change once one
931
+ * of its props changes, for its next load. Not saved: call it before
932
+ * images render, e.g. in index.js.
933
+ */
934
+ setDefaults: (defaults: FastImageDefaults) => void
935
+ }
936
+
937
+ /** `FastImage.setDefaults`'s settings. */
938
+ export interface FastImageDefaults {
939
+ /**
940
+ * The `transition` of images that don't set one (an image's own settings
941
+ * win, and those it leaves out come from this). `null` goes back to the
942
+ * built-in default (see `transition`).
943
+ */
944
+ transition?: boolean | number | Transition | null
945
+ }
946
+
947
+ const FastImage: React.ForwardRefExoticComponent<
948
+ FastImageProps & React.RefAttributes<ViewRef>
949
+ > &
950
+ FastImageStaticProperties = FastImageComponent as any
951
+
952
+ FastImage.cacheControl = cacheControl
953
+
954
+ FastImage.priority = priority
955
+
956
+ FastImage.resizeMode = resizeMode
957
+
958
+ FastImage.setDefaults = ({ transition }: FastImageDefaults = {}) => {
959
+ if (transition === null) {
960
+ defaultTransition = undefined
961
+ } else if (transition !== undefined) {
962
+ defaultTransition = resolveTransition(transition, builtInTransition())
963
+ }
964
+ }
965
+
966
+ // preload, getCachePath and writeToCache take one source per image, not
967
+ // several sizes of one: pick the size (e.g. the one a view will show).
968
+ const ONE_SOURCE =
969
+ 'Takes one source, not an array of sizes: pass the size to use'
970
+
971
+ // Whether a source is an object, which native can read. Other values (null, a
972
+ // url string, a number, an array) are sent as {}, which fails as a source
973
+ // without a uri: native would crash reading them as a source.
974
+ const isSourceObject = (source: unknown): source is Source =>
975
+ typeof source === 'object' && source !== null && !Array.isArray(source)
976
+
977
+ FastImage.preload = (sources: Source[]) =>
978
+ // Entries that aren't source objects are sent as {}, so native results
979
+ // line up with the sources (iOS drops null entries). Arrays' results are
980
+ // replaced.
981
+ Promise.resolve(
982
+ nativeModule().preload(
983
+ sources.map((s) => (isSourceObject(s) ? s : {})),
984
+ ) as Promise<NativePreloadResult[]>,
985
+ ).then((results?: NativePreloadResult[]) =>
986
+ sources.map((source, i): PreloadResult => {
987
+ if (Array.isArray(source)) {
988
+ return { ok: false, error: ONE_SOURCE, uri: undefined }
989
+ }
990
+ const uri = isSourceObject(source) ? source.uri : undefined
991
+ const result = results?.[i] ?? noResult
992
+ if (!result.ok) return { ...result, uri }
993
+ // Native already fails a source without a uri.
994
+ return typeof uri === 'string'
995
+ ? { ...result, uri }
996
+ : { ok: false, error: 'Invalid source: no uri', uri }
997
+ }),
998
+ )
999
+
1000
+ FastImage.clearMemoryCache = (): Promise<ClearCacheResult> =>
1001
+ nativeModule().clearMemoryCache() as Promise<ClearCacheResult>
1002
+
1003
+ FastImage.clearDiskCache = (): Promise<ClearCacheResult> =>
1004
+ nativeModule().clearDiskCache() as Promise<ClearCacheResult>
1005
+
1006
+ FastImage.configureCache = (limits?: CacheLimits): Promise<CacheState> => {
1007
+ // Sent as the limits to set (numbers) and the names of those to reset
1008
+ // (null), never as null values: iOS's TurboModule leaves those out of the
1009
+ // object it passes, so both platforms read resets from `reset`.
1010
+ const set: { [name: string]: number } = {}
1011
+ const reset: string[] = []
1012
+ for (const [name, value] of Object.entries(limits ?? {})) {
1013
+ if (value === null) reset.push(name)
1014
+ else if (typeof value === 'number') set[name] = value
1015
+ }
1016
+ return nativeModule().configureCache({
1017
+ ...set,
1018
+ reset,
1019
+ }) as Promise<CacheState>
1020
+ }
1021
+
1022
+ FastImage.writeToCache = (
1023
+ source: Source,
1024
+ file: string,
1025
+ ): Promise<CachePathResult> =>
1026
+ Array.isArray(source)
1027
+ ? Promise.resolve({ ok: false, error: ONE_SOURCE })
1028
+ : Promise.resolve(
1029
+ nativeModule().writeToCache(
1030
+ isSourceObject(source) ? source : {},
1031
+ file,
1032
+ ) as Promise<CachePathResult>,
1033
+ )
1034
+
1035
+ FastImage.getCachePath = (source: Source): Promise<CachePathResult> =>
1036
+ Array.isArray(source)
1037
+ ? Promise.resolve({ ok: false, error: ONE_SOURCE })
1038
+ : Promise.resolve(
1039
+ nativeModule().getCachePath(
1040
+ isSourceObject(source) ? source : {},
1041
+ ) as Promise<CachePathResult>,
1042
+ )
1043
+
1044
+ const styles = StyleSheet.create({
1045
+ image: {
1046
+ overflow: 'hidden',
1047
+ },
1048
+ })
1049
+
1050
+ export interface FastImageBackgroundProps extends Omit<
1051
+ FastImageProps,
1052
+ 'style' | 'children'
1053
+ > {
1054
+ /** The container's style; the image fills it. */
1055
+ style?: StyleProp<ViewStyle>
1056
+ /** The image's style. */
1057
+ imageStyle?: StyleProp<ImageStyle>
1058
+ /** A ref to the image (the FastImage inside). */
1059
+ imageRef?: React.Ref<any>
1060
+ /** Content shown on top of the image. */
1061
+ children?: React.ReactNode
1062
+ }
1063
+
1064
+ /**
1065
+ * An image with content on top of it, like React Native's `ImageBackground`: a
1066
+ * view that the image fills, with the children on top (`FastImage` is a single
1067
+ * native view, which doesn't take children). The other props go to the image;
1068
+ * the ref is the view's.
1069
+ */
1070
+ export const FastImageBackground: React.ForwardRefExoticComponent<
1071
+ FastImageBackgroundProps & React.RefAttributes<ViewRef>
1072
+ > = forwardRef(
1073
+ (
1074
+ {
1075
+ style,
1076
+ imageStyle,
1077
+ imageRef,
1078
+ children,
1079
+ importantForAccessibility,
1080
+ ...props
1081
+ }: FastImageBackgroundProps,
1082
+ ref: React.Ref<ViewRef>,
1083
+ ) => (
1084
+ <View
1085
+ accessibilityIgnoresInvertColors
1086
+ importantForAccessibility={importantForAccessibility}
1087
+ style={style}
1088
+ ref={ref}
1089
+ >
1090
+ <FastImageComponent
1091
+ {...props}
1092
+ importantForAccessibility={importantForAccessibility}
1093
+ style={[StyleSheet.absoluteFill, imageStyle]}
1094
+ ref={imageRef}
1095
+ />
1096
+ {children}
1097
+ </View>
1098
+ ),
1099
+ )
1100
+
1101
+ FastImageBackground.displayName = 'FastImageBackground'
1102
+
1103
+ export default FastImage