react-native-fast-image 8.25.0 → 8.27.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.
@@ -104,6 +104,9 @@ export type FastImageSource = {
104
104
  cache?: CacheControls,
105
105
  cacheKey?: string,
106
106
  memoryCache?: boolean,
107
+ width?: number,
108
+ height?: number,
109
+ scale?: number,
107
110
  }
108
111
 
109
112
  export type FastImageProps = $ReadOnly<{|
@@ -114,7 +117,7 @@ export type FastImageProps = $ReadOnly<{|
114
117
  onLoadStart?: ?() => void,
115
118
  onProgress?: ?(event: OnProgressEvent) => void,
116
119
 
117
- source?: ?(FastImageSource | number),
120
+ source?: ?(FastImageSource | Array<FastImageSource> | number),
118
121
  defaultSource?: ?number,
119
122
 
120
123
  tintColor?: number | string,
@@ -55,14 +55,27 @@ function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadSta
55
55
  const result = (0, react.useRef)(void 0);
56
56
  const sent = (0, react.useRef)(Promise.resolve());
57
57
  const image = (0, react.useRef)(null);
58
- const uri = typeof source === "object" && source ? source.uri : void 0;
59
- const webSource = typeof source === "number" ? source : uri ? { uri } : void 0;
58
+ const sizes = Array.isArray(source) && source.length > 1 ? source : void 0;
59
+ const single = Array.isArray(source) ? source[0] : source;
60
+ const uri = typeof single === "object" && single ? single.uri : void 0;
61
+ const webSource = typeof single === "number" ? single : uri ? { uri } : void 0;
60
62
  return /* @__PURE__ */ react.default.createElement(react_native.View, {
61
63
  style: [styles.container, style],
62
64
  onLayout,
63
65
  pointerEvents,
64
66
  ref: forwardedRef
65
- }, /* @__PURE__ */ react.default.createElement(react_native.Image, {
67
+ }, sizes ? /* @__PURE__ */ react.default.createElement(react_native.View, {
68
+ ...props,
69
+ style: styles.image
70
+ }, /* @__PURE__ */ react.default.createElement(ImageSizes, {
71
+ sources: sizes,
72
+ resizeMode: mode,
73
+ blurRadius,
74
+ onLoadStart,
75
+ onLoad,
76
+ onError,
77
+ onLoadEnd
78
+ })) : /* @__PURE__ */ react.default.createElement(react_native.Image, {
66
79
  ...props,
67
80
  style: styles.image,
68
81
  source: webSource,
@@ -108,6 +121,68 @@ function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadSta
108
121
  }
109
122
  }), children);
110
123
  }
124
+ function ImageSizes({ sources, resizeMode: mode, blurRadius, onLoadStart, onLoad, onError, onLoadEnd }) {
125
+ var _largest$uri, _sources$find;
126
+ const sized = sources.filter((source) => (source === null || source === void 0 ? void 0 : source.uri) && source.width).map((source) => {
127
+ var _source$width, _source$scale;
128
+ return {
129
+ uri: source.uri,
130
+ width: Math.round(((_source$width = source.width) !== null && _source$width !== void 0 ? _source$width : 0) * ((_source$scale = source.scale) !== null && _source$scale !== void 0 ? _source$scale : 1))
131
+ };
132
+ });
133
+ const largest = sized.reduce((best, source) => !best || source.width > best.width ? source : best, void 0);
134
+ const src = (_largest$uri = largest === null || largest === void 0 ? void 0 : largest.uri) !== null && _largest$uri !== void 0 ? _largest$uri : (_sources$find = sources.find((source) => source === null || source === void 0 ? void 0 : source.uri)) === null || _sources$find === void 0 ? void 0 : _sources$find.uri;
135
+ const srcSet = sized.length > 0 ? sized.map((source) => `${source.uri} ${source.width}w`).join(", ") : void 0;
136
+ (0, react.useEffect)(() => {
137
+ onLoadStart === null || onLoadStart === void 0 || onLoadStart();
138
+ }, [src, srcSet]);
139
+ return /* @__PURE__ */ react.default.createElement("img", {
140
+ src,
141
+ srcSet,
142
+ sizes: srcSet ? "auto, 100vw" : void 0,
143
+ loading: "lazy",
144
+ alt: "",
145
+ draggable: false,
146
+ style: {
147
+ position: "absolute",
148
+ width: "100%",
149
+ height: "100%",
150
+ objectFit: OBJECT_FIT[mode !== null && mode !== void 0 ? mode : "cover"],
151
+ filter: blurRadius ? `blur(${blurRadius}px)` : void 0
152
+ },
153
+ onLoad: (event) => {
154
+ var _picked$width;
155
+ const image = event.currentTarget;
156
+ const picked = srcSet ? sized.find((source) => new URL(source.uri, image.currentSrc).href === image.currentSrc) : void 0;
157
+ const loadedWidth = (_picked$width = picked === null || picked === void 0 ? void 0 : picked.width) !== null && _picked$width !== void 0 ? _picked$width : image.naturalWidth;
158
+ const loadedHeight = picked ? Math.round(image.naturalHeight * picked.width / image.naturalWidth) : image.naturalHeight;
159
+ onLoad === null || onLoad === void 0 || onLoad({ nativeEvent: {
160
+ width: loadedWidth,
161
+ height: loadedHeight
162
+ } });
163
+ onLoadEnd === null || onLoadEnd === void 0 || onLoadEnd({
164
+ ok: true,
165
+ width: loadedWidth,
166
+ height: loadedHeight
167
+ });
168
+ },
169
+ onError: () => {
170
+ const error = "Failed to load the image";
171
+ onError === null || onError === void 0 || onError({ nativeEvent: { error } });
172
+ onLoadEnd === null || onLoadEnd === void 0 || onLoadEnd({
173
+ ok: false,
174
+ error
175
+ });
176
+ }
177
+ });
178
+ }
179
+ const OBJECT_FIT = {
180
+ cover: "cover",
181
+ contain: "contain",
182
+ stretch: "fill",
183
+ center: "scale-down",
184
+ repeat: "cover"
185
+ };
111
186
  const FastImageMemo = (0, react.memo)(FastImageBase);
112
187
  const FastImageComponent = (0, react.forwardRef)((props, ref) => /* @__PURE__ */ react.default.createElement(FastImageMemo, {
113
188
  forwardedRef: ref,
@@ -104,6 +104,9 @@ export type FastImageSource = {
104
104
  cache?: CacheControls,
105
105
  cacheKey?: string,
106
106
  memoryCache?: boolean,
107
+ width?: number,
108
+ height?: number,
109
+ scale?: number,
107
110
  }
108
111
 
109
112
  export type FastImageProps = $ReadOnly<{|
@@ -114,7 +117,7 @@ export type FastImageProps = $ReadOnly<{|
114
117
  onLoadStart?: ?() => void,
115
118
  onProgress?: ?(event: OnProgressEvent) => void,
116
119
 
117
- source?: ?(FastImageSource | number),
120
+ source?: ?(FastImageSource | Array<FastImageSource> | number),
118
121
  defaultSource?: ?number,
119
122
 
120
123
  tintColor?: number | string,
package/dist/index.d.ts CHANGED
@@ -26,6 +26,14 @@ export type Source = {
26
26
  * list scrolled back decodes them again.
27
27
  */
28
28
  memoryCache?: boolean;
29
+ /**
30
+ * With several sources (`source` as an array), the image's size at this
31
+ * uri in pixels (width and height, times `scale` if given): the view
32
+ * loads the source closest to its own size.
33
+ */
34
+ width?: number;
35
+ height?: number;
36
+ scale?: number;
29
37
  };
30
38
  export type Transition = {
31
39
  /**
@@ -116,7 +124,12 @@ export interface ImageStyle extends ViewStyle {
116
124
  opacity?: number;
117
125
  }
118
126
  export interface FastImageProps extends AccessibilityProps, ViewProps {
119
- source?: Source | ImageRequireSource;
127
+ /**
128
+ * The image: a source, a `require()`d image, or several sources of the
129
+ * same image at different sizes (each with its `width` and `height` in
130
+ * pixels), of which the view loads the one closest to its size.
131
+ */
132
+ source?: Source | Source[] | ImageRequireSource;
120
133
  defaultSource?: ImageRequireSource;
121
134
  resizeMode?: ResizeMode;
122
135
  fallback?: boolean;
package/dist/index.js CHANGED
@@ -98,7 +98,7 @@ function FastImageBase({ source, defaultSource, tintColor, onLoadStart, onProgre
98
98
  };
99
99
  const resolvedTintColor = tintColor != null ? tintColor : tintColorFromStyle(style);
100
100
  if (fallback) {
101
- const cleanedSource = typeof source === "number" ? source : withoutCache(source);
101
+ const cleanedSource = typeof source === "number" ? source : Array.isArray(source) ? source.map(withoutCache) : withoutCache(source);
102
102
  const resolvedSource = Image.resolveAssetSource(cleanedSource);
103
103
  return /* @__PURE__ */ React.createElement(View, {
104
104
  style: [styles.imageContainer, style],
@@ -137,7 +137,9 @@ function FastImageBase({ source, defaultSource, tintColor, onLoadStart, onProgre
137
137
  resizeMode
138
138
  }), children);
139
139
  }
140
- const resolvedSource = Image.resolveAssetSource(source);
140
+ const sources = Array.isArray(source) ? source.length === 1 ? void 0 : source : void 0;
141
+ const single = Array.isArray(source) ? source[0] : source;
142
+ const resolvedSource = sources ? void 0 : Image.resolveAssetSource(single);
141
143
  const resolvedDefaultSource = resolveDefaultSource(defaultSource);
142
144
  return /* @__PURE__ */ React.createElement(View, {
143
145
  style: [styles.imageContainer, style],
@@ -150,6 +152,7 @@ function FastImageBase({ source, defaultSource, tintColor, onLoadStart, onProgre
150
152
  ...transitionProps(transition),
151
153
  style: StyleSheet.absoluteFill,
152
154
  source: resolvedSource,
155
+ sources,
153
156
  defaultSource: resolvedDefaultSource,
154
157
  onFastImageLoadStart: onLoadStart,
155
158
  onFastImageProgress: withProgress(onProgress),
@@ -173,8 +176,14 @@ const FastImage = FastImageComponent;
173
176
  FastImage.resizeMode = resizeMode;
174
177
  FastImage.cacheControl = cacheControl;
175
178
  FastImage.priority = priority;
176
- FastImage.preload = (sources) => Promise.resolve(NativeModules.FastImageView.preload(sources.map((s) => s || {}))).then((results) => sources.map((source, i) => {
179
+ const ONE_SOURCE = "Takes one source, not an array of sizes: pass the size to use";
180
+ FastImage.preload = (sources) => Promise.resolve(NativeModules.FastImageView.preload(sources.map((s) => s && !Array.isArray(s) ? s : {}))).then((results) => sources.map((source, i) => {
177
181
  var _results$i;
182
+ if (Array.isArray(source)) return {
183
+ ok: false,
184
+ error: ONE_SOURCE,
185
+ uri: void 0
186
+ };
178
187
  const uri = source ? source.uri : void 0;
179
188
  const result = (_results$i = results === null || results === void 0 ? void 0 : results[i]) !== null && _results$i !== void 0 ? _results$i : noResult;
180
189
  if (!result.ok) return {
@@ -193,8 +202,14 @@ FastImage.preload = (sources) => Promise.resolve(NativeModules.FastImageView.pre
193
202
  FastImage.clearMemoryCache = () => NativeModules.FastImageView.clearMemoryCache();
194
203
  FastImage.clearDiskCache = () => NativeModules.FastImageView.clearDiskCache();
195
204
  FastImage.configureCache = (limits = {}) => Promise.resolve(NativeModules.FastImageView.configureCache(limits));
196
- FastImage.writeToCache = (source, file) => Promise.resolve(NativeModules.FastImageView.writeToCache(source || {}, file));
197
- FastImage.getCachePath = (source) => Promise.resolve(NativeModules.FastImageView.getCachePath(source || {}));
205
+ FastImage.writeToCache = (source, file) => Array.isArray(source) ? Promise.resolve({
206
+ ok: false,
207
+ error: ONE_SOURCE
208
+ }) : Promise.resolve(NativeModules.FastImageView.writeToCache(source || {}, file));
209
+ FastImage.getCachePath = (source) => Array.isArray(source) ? Promise.resolve({
210
+ ok: false,
211
+ error: ONE_SOURCE
212
+ }) : Promise.resolve(NativeModules.FastImageView.getCachePath(source || {}));
198
213
  const styles = StyleSheet.create({
199
214
  fallbackImage: {
200
215
  position: "absolute",
@@ -104,6 +104,9 @@ export type FastImageSource = {
104
104
  cache?: CacheControls,
105
105
  cacheKey?: string,
106
106
  memoryCache?: boolean,
107
+ width?: number,
108
+ height?: number,
109
+ scale?: number,
107
110
  }
108
111
 
109
112
  export type FastImageProps = $ReadOnly<{|
@@ -114,7 +117,7 @@ export type FastImageProps = $ReadOnly<{|
114
117
  onLoadStart?: ?() => void,
115
118
  onProgress?: ?(event: OnProgressEvent) => void,
116
119
 
117
- source?: ?(FastImageSource | number),
120
+ source?: ?(FastImageSource | Array<FastImageSource> | number),
118
121
  defaultSource?: ?number,
119
122
 
120
123
  tintColor?: number | string,
package/dist/index.web.js CHANGED
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, memo, useRef } from "react";
1
+ import React, { forwardRef, memo, useEffect, useRef } from "react";
2
2
  import { Image, StyleSheet, View } from "react-native";
3
3
  //#region src/constants.ts
4
4
  const resizeMode = {
@@ -28,14 +28,27 @@ function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadSta
28
28
  const result = useRef(void 0);
29
29
  const sent = useRef(Promise.resolve());
30
30
  const image = useRef(null);
31
- const uri = typeof source === "object" && source ? source.uri : void 0;
32
- const webSource = typeof source === "number" ? source : uri ? { uri } : void 0;
31
+ const sizes = Array.isArray(source) && source.length > 1 ? source : void 0;
32
+ const single = Array.isArray(source) ? source[0] : source;
33
+ const uri = typeof single === "object" && single ? single.uri : void 0;
34
+ const webSource = typeof single === "number" ? single : uri ? { uri } : void 0;
33
35
  return /* @__PURE__ */ React.createElement(View, {
34
36
  style: [styles.container, style],
35
37
  onLayout,
36
38
  pointerEvents,
37
39
  ref: forwardedRef
38
- }, /* @__PURE__ */ React.createElement(Image, {
40
+ }, sizes ? /* @__PURE__ */ React.createElement(View, {
41
+ ...props,
42
+ style: styles.image
43
+ }, /* @__PURE__ */ React.createElement(ImageSizes, {
44
+ sources: sizes,
45
+ resizeMode: mode,
46
+ blurRadius,
47
+ onLoadStart,
48
+ onLoad,
49
+ onError,
50
+ onLoadEnd
51
+ })) : /* @__PURE__ */ React.createElement(Image, {
39
52
  ...props,
40
53
  style: styles.image,
41
54
  source: webSource,
@@ -81,6 +94,68 @@ function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadSta
81
94
  }
82
95
  }), children);
83
96
  }
97
+ function ImageSizes({ sources, resizeMode: mode, blurRadius, onLoadStart, onLoad, onError, onLoadEnd }) {
98
+ var _largest$uri, _sources$find;
99
+ const sized = sources.filter((source) => (source === null || source === void 0 ? void 0 : source.uri) && source.width).map((source) => {
100
+ var _source$width, _source$scale;
101
+ return {
102
+ uri: source.uri,
103
+ width: Math.round(((_source$width = source.width) !== null && _source$width !== void 0 ? _source$width : 0) * ((_source$scale = source.scale) !== null && _source$scale !== void 0 ? _source$scale : 1))
104
+ };
105
+ });
106
+ const largest = sized.reduce((best, source) => !best || source.width > best.width ? source : best, void 0);
107
+ const src = (_largest$uri = largest === null || largest === void 0 ? void 0 : largest.uri) !== null && _largest$uri !== void 0 ? _largest$uri : (_sources$find = sources.find((source) => source === null || source === void 0 ? void 0 : source.uri)) === null || _sources$find === void 0 ? void 0 : _sources$find.uri;
108
+ const srcSet = sized.length > 0 ? sized.map((source) => `${source.uri} ${source.width}w`).join(", ") : void 0;
109
+ useEffect(() => {
110
+ onLoadStart === null || onLoadStart === void 0 || onLoadStart();
111
+ }, [src, srcSet]);
112
+ return /* @__PURE__ */ React.createElement("img", {
113
+ src,
114
+ srcSet,
115
+ sizes: srcSet ? "auto, 100vw" : void 0,
116
+ loading: "lazy",
117
+ alt: "",
118
+ draggable: false,
119
+ style: {
120
+ position: "absolute",
121
+ width: "100%",
122
+ height: "100%",
123
+ objectFit: OBJECT_FIT[mode !== null && mode !== void 0 ? mode : "cover"],
124
+ filter: blurRadius ? `blur(${blurRadius}px)` : void 0
125
+ },
126
+ onLoad: (event) => {
127
+ var _picked$width;
128
+ const image = event.currentTarget;
129
+ const picked = srcSet ? sized.find((source) => new URL(source.uri, image.currentSrc).href === image.currentSrc) : void 0;
130
+ const loadedWidth = (_picked$width = picked === null || picked === void 0 ? void 0 : picked.width) !== null && _picked$width !== void 0 ? _picked$width : image.naturalWidth;
131
+ const loadedHeight = picked ? Math.round(image.naturalHeight * picked.width / image.naturalWidth) : image.naturalHeight;
132
+ onLoad === null || onLoad === void 0 || onLoad({ nativeEvent: {
133
+ width: loadedWidth,
134
+ height: loadedHeight
135
+ } });
136
+ onLoadEnd === null || onLoadEnd === void 0 || onLoadEnd({
137
+ ok: true,
138
+ width: loadedWidth,
139
+ height: loadedHeight
140
+ });
141
+ },
142
+ onError: () => {
143
+ const error = "Failed to load the image";
144
+ onError === null || onError === void 0 || onError({ nativeEvent: { error } });
145
+ onLoadEnd === null || onLoadEnd === void 0 || onLoadEnd({
146
+ ok: false,
147
+ error
148
+ });
149
+ }
150
+ });
151
+ }
152
+ const OBJECT_FIT = {
153
+ cover: "cover",
154
+ contain: "contain",
155
+ stretch: "fill",
156
+ center: "scale-down",
157
+ repeat: "cover"
158
+ };
84
159
  const FastImageMemo = memo(FastImageBase);
85
160
  const FastImageComponent = forwardRef((props, ref) => /* @__PURE__ */ React.createElement(FastImageMemo, {
86
161
  forwardedRef: ref,
@@ -104,6 +104,9 @@ export type FastImageSource = {
104
104
  cache?: CacheControls,
105
105
  cacheKey?: string,
106
106
  memoryCache?: boolean,
107
+ width?: number,
108
+ height?: number,
109
+ scale?: number,
107
110
  }
108
111
 
109
112
  export type FastImageProps = $ReadOnly<{|
@@ -114,7 +117,7 @@ export type FastImageProps = $ReadOnly<{|
114
117
  onLoadStart?: ?() => void,
115
118
  onProgress?: ?(event: OnProgressEvent) => void,
116
119
 
117
- source?: ?(FastImageSource | number),
120
+ source?: ?(FastImageSource | Array<FastImageSource> | number),
118
121
  defaultSource?: ?number,
119
122
 
120
123
  tintColor?: number | string,
@@ -32,6 +32,9 @@ typedef NS_ENUM(NSInteger, FFFCacheControl) {
32
32
  @property (nonatomic) FFFCacheControl cacheControl;
33
33
  // The key to cache the image under instead of its url, or nil.
34
34
  @property (nonatomic, copy) NSString *cacheKey;
35
+ // With several sources, the image's size at this url in pixels (width ×
36
+ // height × scale²), to pick the one closest to the view's size; 0 if unknown.
37
+ @property (nonatomic) CGFloat pixelCount;
35
38
  // Whether the decoded image is kept in the memory cache (`memoryCache`,
36
39
  // default YES). Without it, it's only kept on disk: views don't leave it in
37
40
  // memory, and preload doesn't decode it.
@@ -61,6 +64,10 @@ typedef NS_ENUM(NSInteger, FFFCacheControl) {
61
64
  // which is decoded at about the view's size.
62
65
  - (BOOL)isPhotoLibrary;
63
66
 
67
+ // The error's message, or for an SVG image that couldn't be decoded because
68
+ // the app doesn't have SDWebImageSVGCoder, what to add.
69
+ - (NSString *)errorMessage:(NSError *)error;
70
+
64
71
  // A photo library image's own size in pixels (for onLoad when it comes from
65
72
  // a cache, where the size it was decoded from isn't recorded), or zero.
66
73
  - (CGSize)photoPixelSize;
@@ -5,8 +5,12 @@
5
5
  #import <SDWebImage/SDWebImageError.h>
6
6
  #import <SDWebImage/SDWebImageDownloaderOperation.h>
7
7
  #import <SDWebImage/NSData+ImageContentType.h>
8
+ #import <SDWebImage/SDImageCodersManager.h>
8
9
  #import <objc/message.h>
9
10
 
11
+ // In FFFastImageView.m.
12
+ FOUNDATION_EXTERN NSString *FFFErrorMessage(NSError *error);
13
+
10
14
  static NSUInteger const FFFWebCacheSize = 50 * 1024 * 1024;
11
15
 
12
16
  // The photo's own size in pixels, looked up by its identifier (the url after
@@ -173,8 +177,41 @@ static CGSize FFFPhotoPixelSize(NSURL *url)
173
177
 
174
178
  @end
175
179
 
180
+ // SDWebImageSVGCoder's coder, when the app has the pod (SVG images): found at
181
+ // runtime, so FastImage doesn't depend on it. Nil otherwise.
182
+ static id<SDImageCoder> FFFSVGCoder;
183
+
176
184
  @implementation FFFastImageSource
177
185
 
186
+ + (void)initialize
187
+ {
188
+ if (self != [FFFastImageSource class]) {
189
+ return;
190
+ }
191
+ // Registered with SDWebImage's coders, as the pod's setup does (unless
192
+ // the app has already): SDWebImage then picks it for SVG data, including
193
+ // for FastImage's animated and downsampled image classes. Giving the
194
+ // coder per load instead (SDWebImageContextImageCoder) would make it the
195
+ // only coder for everything in that load. So the app's own SDWebImage
196
+ // use can decode SVG too, which is what the pod is for.
197
+ Class coderClass = NSClassFromString(@"SDImageSVGCoder");
198
+ SEL shared = NSSelectorFromString(@"sharedCoder");
199
+ if (!coderClass || ![coderClass respondsToSelector:shared]) {
200
+ return;
201
+ }
202
+ id coder = ((id (*)(id, SEL))objc_msgSend)(coderClass, shared);
203
+ if (![coder conformsToProtocol:@protocol(SDImageCoder)]) {
204
+ return;
205
+ }
206
+ FFFSVGCoder = coder;
207
+ for (id<SDImageCoder> registered in SDImageCodersManager.sharedManager.coders) {
208
+ if ([registered isKindOfClass:coderClass]) {
209
+ return;
210
+ }
211
+ }
212
+ [SDImageCodersManager.sharedManager addCoder:coder];
213
+ }
214
+
178
215
  - (instancetype)initWithURL:(NSURL *)url
179
216
  priority:(FFFPriority)priority
180
217
  headers:(NSDictionary *)headers
@@ -216,6 +253,15 @@ static CGSize FFFPhotoPixelSize(NSURL *url)
216
253
  return 0;
217
254
  }
218
255
 
256
+ - (NSString *)errorMessage:(NSError *)error
257
+ {
258
+ if (!FFFSVGCoder && [error.domain isEqualToString:SDWebImageErrorDomain] && error.code == SDWebImageErrorBadImageData &&
259
+ [_url.path.lowercaseString hasSuffix:@".svg"]) {
260
+ return @"SVG images need SDWebImageSVGCoder: add pod 'SDWebImageSVGCoder' to the app's Podfile";
261
+ }
262
+ return FFFErrorMessage(error);
263
+ }
264
+
219
265
  - (BOOL)isPhotoLibrary
220
266
  {
221
267
  return [_url.scheme isEqualToString:@"ph"];
@@ -17,6 +17,9 @@
17
17
  @property (nonatomic, copy) RCTDirectEventBlock onFastImageLoadEnd;
18
18
  @property (nonatomic, assign) RCTResizeMode resizeMode;
19
19
  @property (nonatomic, strong) FFFastImageSource *source;
20
+ // Several sources (2 or more) of the same image at different sizes: the view
21
+ // loads the one whose size is closest to its own.
22
+ @property (nonatomic, copy) NSArray<FFFastImageSource *> *sources;
20
23
  @property (nonatomic, strong) UIImage *defaultSource;
21
24
  @property (nonatomic, strong) UIColor *imageColor;
22
25
  // When it changes, the next image doesn't replace the current one: the view