react-native-fast-image 9.3.7 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -61
- package/RNFastImage.podspec +14 -9
- package/android/build.gradle +21 -47
- package/android/consumer-rules.pro +7 -7
- package/android/src/main/AndroidManifest.xml +1 -2
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageAnimatable.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageAnimated.java +2 -2
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageApng.java +2 -2
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageBlur.java +4 -6
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageCacheControl.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageCacheLimits.java +4 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageCacheWrite.java +1 -1
- package/android/src/main/java/com/fastimage/FastImageCrop.java +92 -0
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageEvent.java +2 -3
- package/android/src/main/java/com/fastimage/FastImageEvents.java +74 -0
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageGif.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageGlide.java +13 -3
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageGlideModule.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageKeyedGlideUrl.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageOkHttpProgressGlideModule.java +49 -14
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageRequestListener.java +6 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSharedDownloads.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSource.java +12 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSourceSize.java +12 -2
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSvg.java +3 -3
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSvgRenderer.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageUrl.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewConverter.java +24 -9
- package/android/src/main/java/com/fastimage/FastImageViewManager.java +591 -0
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewModule.java +50 -28
- package/android/src/main/java/com/fastimage/FastImageViewPackage.java +51 -0
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewWithUrl.java +352 -126
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageWebGlideUrl.java +1 -1
- package/app.plugin.js +2 -7
- package/dist/index.cjs.js +276 -157
- package/dist/index.cjs.js.flow +34 -17
- package/dist/index.cjs.web.js +211 -61
- package/dist/index.cjs.web.js.flow +34 -17
- package/dist/index.d.ts +108 -58
- package/dist/index.js +278 -159
- package/dist/index.js.flow +34 -17
- package/dist/index.web.js +212 -62
- package/dist/index.web.js.flow +34 -17
- package/dist/objectFit.d.ts +2 -2
- package/dist/objectPosition.d.ts +42 -0
- package/dist/specs/FastImageViewNativeComponent.d.ts +48 -0
- package/dist/specs/NativeFastImageModule.d.ts +11 -0
- package/dist/warnings.d.ts +3 -0
- package/ios/FastImage/FFFDownsampledImage.h +4 -8
- package/ios/FastImage/FFFDownsampledImage.m +0 -34
- package/ios/FastImage/FFFastImageModule.h +8 -0
- package/ios/FastImage/{FFFastImageViewManager.m → FFFastImageModule.mm} +89 -59
- package/ios/FastImage/FFFastImageSource.h +4 -0
- package/ios/FastImage/FFFastImageSource.m +5 -1
- package/ios/FastImage/FFFastImageView.h +36 -7
- package/ios/FastImage/FFFastImageView.m +229 -172
- package/ios/FastImage/FFFastImageViewComponentView.h +11 -0
- package/ios/FastImage/FFFastImageViewComponentView.mm +325 -0
- package/ios/FastImage/RCTConvert+FFFastImage.h +1 -1
- package/ios/FastImage/RCTConvert+FFFastImage.m +9 -10
- package/package.json +28 -9
- package/src/constants.ts +28 -0
- package/src/index.js.flow +180 -0
- package/src/index.tsx +1128 -0
- package/src/index.web.tsx +548 -0
- package/src/objectFit.ts +48 -0
- package/src/objectPosition.ts +243 -0
- package/src/specs/FastImageViewNativeComponent.ts +83 -0
- package/src/specs/NativeFastImageModule.ts +35 -0
- package/src/warnings.ts +37 -0
- package/android/src/main/AndroidManifestNew.xml +0 -3
- package/android/src/main/java/com/dylanvann/fastimage/FastImageCookieHandler.java +0 -63
- package/android/src/main/java/com/dylanvann/fastimage/FastImageEvents.java +0 -49
- package/android/src/main/java/com/dylanvann/fastimage/FastImageProgressListener.java +0 -18
- package/android/src/main/java/com/dylanvann/fastimage/FastImageShadowNode.java +0 -21
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java +0 -379
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewPackage.java +0 -27
- package/ios/FastImage/FFFastImageViewManager.h +0 -5
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
#import "FFFastImageViewComponentView.h"
|
|
2
|
+
|
|
3
|
+
#import <React/RCTConversions.h>
|
|
4
|
+
#import <React/RCTConvert.h>
|
|
5
|
+
#import <react/renderer/components/RNFastImageSpec/ComponentDescriptors.h>
|
|
6
|
+
#import <react/renderer/components/RNFastImageSpec/EventEmitters.h>
|
|
7
|
+
#import <react/renderer/components/RNFastImageSpec/Props.h>
|
|
8
|
+
#import <react/renderer/components/RNFastImageSpec/RCTComponentViewHelpers.h>
|
|
9
|
+
|
|
10
|
+
#import "FFFastImageView.h"
|
|
11
|
+
#import "RCTConvert+FFFastImage.h"
|
|
12
|
+
|
|
13
|
+
using namespace facebook::react;
|
|
14
|
+
|
|
15
|
+
// A string prop: nil when it's empty (not set).
|
|
16
|
+
static NSString *_Nullable FFFString(const std::string &value)
|
|
17
|
+
{
|
|
18
|
+
return value.empty() ? nil : [NSString stringWithUTF8String:value.c_str()];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// A value from JS as the objects React Native's converters (RCTConvert) take,
|
|
22
|
+
// with NSNull for null. (React Native's own converters aren't available to a
|
|
23
|
+
// library: React/RCTFollyConvert.h is a private header of React-Core, and
|
|
24
|
+
// react/utils/FollyConvert.h isn't on a library's header search paths when
|
|
25
|
+
// the app builds React Native from source with use_frameworks!, e.g. 0.83's.)
|
|
26
|
+
static id FFFObjectFromDynamic(const folly::dynamic &value)
|
|
27
|
+
{
|
|
28
|
+
switch (value.type()) {
|
|
29
|
+
case folly::dynamic::BOOL:
|
|
30
|
+
return @(value.getBool());
|
|
31
|
+
case folly::dynamic::INT64:
|
|
32
|
+
return @(value.getInt());
|
|
33
|
+
case folly::dynamic::DOUBLE:
|
|
34
|
+
return @(value.getDouble());
|
|
35
|
+
case folly::dynamic::STRING: {
|
|
36
|
+
const std::string &string = value.getString();
|
|
37
|
+
return [[NSString alloc] initWithBytes:string.data() length:string.size() encoding:NSUTF8StringEncoding] ?: @"";
|
|
38
|
+
}
|
|
39
|
+
case folly::dynamic::ARRAY: {
|
|
40
|
+
NSMutableArray *array = [NSMutableArray arrayWithCapacity:value.size()];
|
|
41
|
+
for (const auto &item : value) {
|
|
42
|
+
[array addObject:FFFObjectFromDynamic(item)];
|
|
43
|
+
}
|
|
44
|
+
return array;
|
|
45
|
+
}
|
|
46
|
+
case folly::dynamic::OBJECT: {
|
|
47
|
+
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:value.size()];
|
|
48
|
+
for (const auto &pair : value.items()) {
|
|
49
|
+
if (pair.first.isString()) {
|
|
50
|
+
// As a string value: never nil (dictionary[nil] throws).
|
|
51
|
+
dictionary[FFFObjectFromDynamic(pair.first)] = FFFObjectFromDynamic(pair.second);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return dictionary;
|
|
55
|
+
}
|
|
56
|
+
default:
|
|
57
|
+
return (id)kCFNull;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// An event's payload (dictionaries of strings, numbers and booleans) for the
|
|
62
|
+
// event emitter.
|
|
63
|
+
static folly::dynamic FFFDynamicFromObject(id value)
|
|
64
|
+
{
|
|
65
|
+
if ([value isKindOfClass:NSDictionary.class]) {
|
|
66
|
+
folly::dynamic object = folly::dynamic::object();
|
|
67
|
+
for (id key in (NSDictionary *)value) {
|
|
68
|
+
if ([key isKindOfClass:NSString.class]) {
|
|
69
|
+
object[RCTStringFromNSString(key)] = FFFDynamicFromObject(((NSDictionary *)value)[key]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return object;
|
|
73
|
+
}
|
|
74
|
+
if ([value isKindOfClass:NSArray.class]) {
|
|
75
|
+
folly::dynamic array = folly::dynamic::array();
|
|
76
|
+
for (id item in (NSArray *)value) {
|
|
77
|
+
array.push_back(FFFDynamicFromObject(item));
|
|
78
|
+
}
|
|
79
|
+
return array;
|
|
80
|
+
}
|
|
81
|
+
if ([value isKindOfClass:NSString.class]) {
|
|
82
|
+
// "" when it has no UTF-8 form (UTF8String is NULL for a string with
|
|
83
|
+
// a lone surrogate), as React Native's conversions do.
|
|
84
|
+
return RCTStringFromNSString(value);
|
|
85
|
+
}
|
|
86
|
+
if ([value isKindOfClass:NSNumber.class]) {
|
|
87
|
+
if (CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID()) {
|
|
88
|
+
return (bool)[value boolValue];
|
|
89
|
+
}
|
|
90
|
+
if (CFNumberIsFloatType((__bridge CFNumberRef)value)) {
|
|
91
|
+
return [value doubleValue];
|
|
92
|
+
}
|
|
93
|
+
return (int64_t)[value longLongValue];
|
|
94
|
+
}
|
|
95
|
+
return nullptr;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// A prop of any type (a source, sources, defaultSource) as JSON: nil when
|
|
99
|
+
// it's null (not set).
|
|
100
|
+
static id _Nullable FFFJSON(const folly::dynamic &value)
|
|
101
|
+
{
|
|
102
|
+
return value.isNull() ? nil : FFFObjectFromDynamic(value);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// The events JS has a handler for: the handledEvents prop's bits (see
|
|
106
|
+
// src/specs/FastImageViewNativeComponent.ts).
|
|
107
|
+
typedef NS_OPTIONS(int32_t, FFFEvent) {
|
|
108
|
+
FFFEventLoadStart = 1 << 0,
|
|
109
|
+
FFFEventProgress = 1 << 1,
|
|
110
|
+
FFFEventLoad = 1 << 2,
|
|
111
|
+
FFFEventError = 1 << 3,
|
|
112
|
+
FFFEventLoadEnd = 1 << 4,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
@interface FFFastImageViewComponentView () <RCTFastImageViewViewProtocol>
|
|
116
|
+
@end
|
|
117
|
+
|
|
118
|
+
@implementation FFFastImageViewComponentView {
|
|
119
|
+
FFFastImageView *_imageView;
|
|
120
|
+
// The props aren't on the image view yet (a new one): set them all,
|
|
121
|
+
// rather than the ones that changed.
|
|
122
|
+
BOOL _setsAllProps;
|
|
123
|
+
BOOL _propsChanged;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
127
|
+
{
|
|
128
|
+
return concreteComponentDescriptorProvider<FastImageViewComponentDescriptor>();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
132
|
+
{
|
|
133
|
+
if (self = [super initWithFrame:frame]) {
|
|
134
|
+
static const auto defaultProps = std::make_shared<const FastImageViewProps>();
|
|
135
|
+
_props = defaultProps;
|
|
136
|
+
}
|
|
137
|
+
return self;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// A new image view, made with the view's first props after it's created or
|
|
141
|
+
// recycled (see updateProps), which sends its events through the component's
|
|
142
|
+
// event emitter: the ones JS has a handler for (see sendEvent:).
|
|
143
|
+
- (void)makeImageView
|
|
144
|
+
{
|
|
145
|
+
_imageView = [FFFastImageView new];
|
|
146
|
+
__weak __typeof(self) weakSelf = self;
|
|
147
|
+
_imageView.onFastImageLoadStart = ^(NSDictionary *event) {
|
|
148
|
+
[weakSelf sendEvent:FFFEventLoadStart type:"fastImageLoadStart" payload:event];
|
|
149
|
+
};
|
|
150
|
+
_imageView.onFastImageProgress = ^(NSDictionary *event) {
|
|
151
|
+
[weakSelf sendEvent:FFFEventProgress type:"fastImageProgress" payload:event];
|
|
152
|
+
};
|
|
153
|
+
_imageView.onFastImageLoad = ^(NSDictionary *event) {
|
|
154
|
+
[weakSelf sendEvent:FFFEventLoad type:"fastImageLoad" payload:event];
|
|
155
|
+
};
|
|
156
|
+
_imageView.onFastImageError = ^(NSDictionary *event) {
|
|
157
|
+
[weakSelf sendEvent:FFFEventError type:"fastImageError" payload:event];
|
|
158
|
+
};
|
|
159
|
+
_imageView.onFastImageLoadEnd = ^(NSDictionary *event) {
|
|
160
|
+
[weakSelf sendEvent:FFFEventLoadEnd type:"fastImageLoadEnd" payload:event];
|
|
161
|
+
};
|
|
162
|
+
_setsAllProps = YES;
|
|
163
|
+
self.contentView = _imageView;
|
|
164
|
+
_imageView.frame = RCTCGRectFromRect(_layoutMetrics.getPaddingFrame());
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Padding doesn't inset the image: it fills the view inside its borders (the
|
|
168
|
+
// padding box), as on Android. React Native lays the content view out inside
|
|
169
|
+
// the padding too; its clipping mask for an image view (the padding box's
|
|
170
|
+
// corners, in the image view's bounds) then fits it exactly.
|
|
171
|
+
- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
|
|
172
|
+
oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics
|
|
173
|
+
{
|
|
174
|
+
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
|
175
|
+
_imageView.frame = RCTCGRectFromRect(layoutMetrics.getPaddingFrame());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// The image view's event as it built it (not the generated event structs,
|
|
179
|
+
// which would add the fields an event doesn't have, e.g. width to an error's
|
|
180
|
+
// onLoadEnd), if JS has a handler for it as it happens: most images have
|
|
181
|
+
// none, and each event would be converted and dispatched to JS for nothing.
|
|
182
|
+
- (void)sendEvent:(FFFEvent)event type:(std::string)type payload:(NSDictionary *)payload
|
|
183
|
+
{
|
|
184
|
+
const auto &props = *std::static_pointer_cast<const FastImageViewProps>(_props);
|
|
185
|
+
if (_eventEmitter && (props.handledEvents & event)) {
|
|
186
|
+
_eventEmitter->dispatchEvent(type, FFFDynamicFromObject(payload ?: @{}));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// React Native sets the accessibility props on this view (its
|
|
191
|
+
// accessibilityElement), but RCTViewComponentView answers isAccessibilityElement
|
|
192
|
+
// with its content view's, which is NO for an image view: `accessible` did
|
|
193
|
+
// nothing. (React Native's Image makes its image view the accessibilityElement
|
|
194
|
+
// instead, which doesn't work here: a recycled view gets a new image view,
|
|
195
|
+
// without the props that didn't change.)
|
|
196
|
+
- (BOOL)isAccessibilityElement
|
|
197
|
+
{
|
|
198
|
+
return std::static_pointer_cast<const FastImageViewProps>(_props)->accessible;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
- (void)prepareForRecycle
|
|
202
|
+
{
|
|
203
|
+
[super prepareForRecycle];
|
|
204
|
+
// A recycled view shows another image: it gets a new image view when it's
|
|
205
|
+
// used again (see updateProps), as a new view does, and has none while it
|
|
206
|
+
// waits in React Native's pool, where it may stay unused. The old one
|
|
207
|
+
// stops: removing it autoreleases it, and a load that finished just before
|
|
208
|
+
// can still reach it from the main queue after this view has its next
|
|
209
|
+
// element's event emitter.
|
|
210
|
+
[_imageView stop];
|
|
211
|
+
_imageView = nil;
|
|
212
|
+
self.contentView = nil;
|
|
213
|
+
// A fade from the old image view (see FFFastImageView's fadeIn:).
|
|
214
|
+
[self.layer removeAnimationForKey: kCATransition];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps
|
|
218
|
+
{
|
|
219
|
+
if (!_imageView) {
|
|
220
|
+
[self makeImageView];
|
|
221
|
+
}
|
|
222
|
+
const auto &oldViewProps = *std::static_pointer_cast<const FastImageViewProps>(_props);
|
|
223
|
+
const auto &newViewProps = *std::static_pointer_cast<const FastImageViewProps>(props);
|
|
224
|
+
BOOL all = _setsAllProps;
|
|
225
|
+
_setsAllProps = NO;
|
|
226
|
+
FFFastImageView *view = _imageView;
|
|
227
|
+
|
|
228
|
+
if (all || oldViewProps.source != newViewProps.source) {
|
|
229
|
+
view.source = [RCTConvert FFFastImageSource:FFFJSON(newViewProps.source)];
|
|
230
|
+
}
|
|
231
|
+
if (all || oldViewProps.sources != newViewProps.sources) {
|
|
232
|
+
id sources = FFFJSON(newViewProps.sources);
|
|
233
|
+
view.sources = sources ? [RCTConvert FFFastImageSourceArray:sources] : nil;
|
|
234
|
+
}
|
|
235
|
+
if (all || oldViewProps.defaultSource != newViewProps.defaultSource) {
|
|
236
|
+
id defaultSource = FFFJSON(newViewProps.defaultSource);
|
|
237
|
+
view.defaultSource = defaultSource ? [RCTConvert UIImage:defaultSource] : nil;
|
|
238
|
+
}
|
|
239
|
+
if (all || oldViewProps.objectFit != newViewProps.objectFit) {
|
|
240
|
+
view.objectFit = [RCTConvert FFFObjectFit:FFFString(newViewProps.objectFit)];
|
|
241
|
+
}
|
|
242
|
+
if (all || oldViewProps.objectPositionX != newViewProps.objectPositionX) {
|
|
243
|
+
view.objectPositionX = newViewProps.objectPositionX;
|
|
244
|
+
}
|
|
245
|
+
if (all || oldViewProps.objectPositionXOffset != newViewProps.objectPositionXOffset) {
|
|
246
|
+
view.objectPositionXOffset = newViewProps.objectPositionXOffset;
|
|
247
|
+
}
|
|
248
|
+
if (all || oldViewProps.objectPositionY != newViewProps.objectPositionY) {
|
|
249
|
+
view.objectPositionY = newViewProps.objectPositionY;
|
|
250
|
+
}
|
|
251
|
+
if (all || oldViewProps.objectPositionYOffset != newViewProps.objectPositionYOffset) {
|
|
252
|
+
view.objectPositionYOffset = newViewProps.objectPositionYOffset;
|
|
253
|
+
}
|
|
254
|
+
if (all || oldViewProps.tintColor != newViewProps.tintColor) {
|
|
255
|
+
view.imageColor = RCTUIColorFromSharedColor(newViewProps.tintColor);
|
|
256
|
+
}
|
|
257
|
+
if (all || oldViewProps.recyclingKey != newViewProps.recyclingKey) {
|
|
258
|
+
// As it is, '' included: Codegen sends '' when it isn't set, and an
|
|
259
|
+
// empty key going to another one clears the image, as on Android.
|
|
260
|
+
view.recyclingKey = [NSString stringWithUTF8String:newViewProps.recyclingKey.c_str()];
|
|
261
|
+
}
|
|
262
|
+
if (all || oldViewProps.loopCount != newViewProps.loopCount) {
|
|
263
|
+
view.loopCount = newViewProps.loopCount;
|
|
264
|
+
}
|
|
265
|
+
if (all || oldViewProps.imageRendering != newViewProps.imageRendering) {
|
|
266
|
+
view.imageRendering = FFFString(newViewProps.imageRendering);
|
|
267
|
+
}
|
|
268
|
+
if (all || oldViewProps.paused != newViewProps.paused) {
|
|
269
|
+
view.paused = newViewProps.paused;
|
|
270
|
+
}
|
|
271
|
+
if (all || oldViewProps.transitionDuration != newViewProps.transitionDuration) {
|
|
272
|
+
view.transitionDuration = newViewProps.transitionDuration;
|
|
273
|
+
}
|
|
274
|
+
if (all || oldViewProps.transitionBetweenImages != newViewProps.transitionBetweenImages) {
|
|
275
|
+
view.transitionBetweenImages = newViewProps.transitionBetweenImages;
|
|
276
|
+
}
|
|
277
|
+
if (all || oldViewProps.transitionSkipOnCacheHit != newViewProps.transitionSkipOnCacheHit) {
|
|
278
|
+
view.transitionSkipOnCacheHit = FFFString(newViewProps.transitionSkipOnCacheHit);
|
|
279
|
+
}
|
|
280
|
+
if (all || oldViewProps.downsample != newViewProps.downsample) {
|
|
281
|
+
view.downsample = newViewProps.downsample;
|
|
282
|
+
}
|
|
283
|
+
if (all || oldViewProps.blurRadius != newViewProps.blurRadius) {
|
|
284
|
+
view.blurRadius = newViewProps.blurRadius;
|
|
285
|
+
}
|
|
286
|
+
if (all || oldViewProps.handledEvents != newViewProps.handledEvents) {
|
|
287
|
+
// Progress is tracked from the next load, as a handler added while
|
|
288
|
+
// loading was. (Each event is sent if it has a handler as it happens:
|
|
289
|
+
// see sendEvent:.)
|
|
290
|
+
view.trackProgress = (newViewProps.handledEvents & FFFEventProgress) != 0;
|
|
291
|
+
}
|
|
292
|
+
_propsChanged = YES;
|
|
293
|
+
|
|
294
|
+
[super updateProps:props oldProps:oldProps];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Once the update's props and layout are all set: the image view loads (for
|
|
298
|
+
// its size, which it has by now).
|
|
299
|
+
- (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask
|
|
300
|
+
{
|
|
301
|
+
[super finalizeUpdates:updateMask];
|
|
302
|
+
// RCTViewComponentView masks its image view subviews to the border radius
|
|
303
|
+
// while it clips (invalidateLayer), and leaves the mask when overflow
|
|
304
|
+
// becomes visible.
|
|
305
|
+
if (!std::static_pointer_cast<const FastImageViewProps>(_props)->getClipsContentToBounds()) {
|
|
306
|
+
_imageView.layer.mask = nil;
|
|
307
|
+
}
|
|
308
|
+
// Where objectPosition moved the image view, its mask moves back.
|
|
309
|
+
[_imageView updateMask];
|
|
310
|
+
if (_propsChanged) {
|
|
311
|
+
_propsChanged = NO;
|
|
312
|
+
[_imageView didSetProps];
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// React Native makes the image view's mask again when the appearance changes
|
|
317
|
+
// (as after an update): where objectPosition moved the view, it moves back.
|
|
318
|
+
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
|
|
319
|
+
{
|
|
320
|
+
[super traitCollectionDidChange:previousTraitCollection];
|
|
321
|
+
[_imageView updateMask];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
@end
|
|
325
|
+
|
|
@@ -3,16 +3,13 @@
|
|
|
3
3
|
|
|
4
4
|
@implementation RCTConvert (FFFastImage)
|
|
5
5
|
|
|
6
|
-
RCT_ENUM_CONVERTER(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@"repeat": @(RCTResizeModeRepeat),
|
|
14
|
-
@"none": @(FFFResizeModeNone),
|
|
15
|
-
}), RCTResizeModeCover, integerValue);
|
|
6
|
+
RCT_ENUM_CONVERTER(FFFObjectFit, (@{
|
|
7
|
+
@"cover": @(FFFObjectFitCover),
|
|
8
|
+
@"contain": @(FFFObjectFitContain),
|
|
9
|
+
@"fill": @(FFFObjectFitFill),
|
|
10
|
+
@"scale-down": @(FFFObjectFitScaleDown),
|
|
11
|
+
@"none": @(FFFObjectFitNone),
|
|
12
|
+
}), FFFObjectFitCover, integerValue);
|
|
16
13
|
|
|
17
14
|
RCT_ENUM_CONVERTER(FFFPriority, (@{
|
|
18
15
|
@"low": @(FFFPriorityLow),
|
|
@@ -62,6 +59,8 @@ RCT_ENUM_CONVERTER(FFFCacheControl, (@{
|
|
|
62
59
|
imageSource.cacheKey = cacheKey.length > 0 ? cacheKey : nil;
|
|
63
60
|
id memoryCache = json[@"memoryCache"];
|
|
64
61
|
imageSource.memoryCache = memoryCache && memoryCache != (id)kCFNull ? [self BOOL:memoryCache] : YES;
|
|
62
|
+
id diskCache = json[@"diskCache"];
|
|
63
|
+
imageSource.diskCache = diskCache && diskCache != (id)kCFNull ? [self BOOL:diskCache] : YES;
|
|
65
64
|
CGFloat scale = json[@"scale"] && json[@"scale"] != (id)kCFNull ? [self CGFloat:json[@"scale"]] : 1;
|
|
66
65
|
imageSource.pixelCount = [self CGFloat:json[@"width"]] * [self CGFloat:json[@"height"]] * scale * scale;
|
|
67
66
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-fast-image",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.1.0",
|
|
4
4
|
"description": "FastImage, performant React Native image component.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cache",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"author": "Dylan Vann <dylan@dylanvann.com> (https://dylanvann.com)",
|
|
22
22
|
"main": "dist/index.cjs",
|
|
23
23
|
"module": "dist/index",
|
|
24
|
-
"react-native": "
|
|
24
|
+
"react-native": "src/index",
|
|
25
25
|
"browser": "dist/index.web.js",
|
|
26
26
|
"typings": "dist/index.d.ts",
|
|
27
27
|
"files": [
|
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
"android/src/main",
|
|
31
31
|
"ios/FastImage",
|
|
32
32
|
"app.plugin.js",
|
|
33
|
+
"src",
|
|
34
|
+
"!src/**/*.test.tsx",
|
|
35
|
+
"!src/__snapshots__",
|
|
33
36
|
"dist",
|
|
34
37
|
"RNFastImage.podspec"
|
|
35
38
|
],
|
|
@@ -46,29 +49,45 @@
|
|
|
46
49
|
},
|
|
47
50
|
"packageManager": "bun@1.4.2",
|
|
48
51
|
"resolutions": {
|
|
49
|
-
"@types/react": "^
|
|
52
|
+
"@types/react": "^19.2.0"
|
|
50
53
|
},
|
|
51
54
|
"devDependencies": {
|
|
52
55
|
"@semantic-release/changelog": "7.0.0",
|
|
53
56
|
"@semantic-release/git": "11.0.1",
|
|
54
57
|
"@types/bun": "^1.4.2",
|
|
55
|
-
"@types/react": "^
|
|
56
|
-
"@types/react-
|
|
57
|
-
"@types/react-test-renderer": "^17.0.1",
|
|
58
|
+
"@types/react": "^19.2.0",
|
|
59
|
+
"@types/react-test-renderer": "^19.1.0",
|
|
58
60
|
"conventional-changelog-angular": "8.3.1",
|
|
59
61
|
"maestro-runner": "1.1.27",
|
|
60
62
|
"odiff-bin": "^4.5.0",
|
|
61
63
|
"oxfmt": "0.70.0",
|
|
62
64
|
"oxlint": "1.85.0",
|
|
63
65
|
"pretty-format": "^30.5.1",
|
|
64
|
-
"react": "
|
|
65
|
-
"react-
|
|
66
|
+
"react": "19.2.0",
|
|
67
|
+
"react-native": "0.83.10",
|
|
68
|
+
"react-test-renderer": "19.2.0",
|
|
66
69
|
"rolldown": "1.2.11",
|
|
67
70
|
"semantic-release": "25.0.9",
|
|
68
71
|
"typescript": "7.0.2"
|
|
69
72
|
},
|
|
70
73
|
"peerDependencies": {
|
|
71
|
-
"react": "
|
|
74
|
+
"react": "*",
|
|
72
75
|
"react-native": "*"
|
|
76
|
+
},
|
|
77
|
+
"codegenConfig": {
|
|
78
|
+
"name": "RNFastImageSpec",
|
|
79
|
+
"type": "all",
|
|
80
|
+
"jsSrcsDir": "src/specs",
|
|
81
|
+
"android": {
|
|
82
|
+
"javaPackageName": "com.fastimage"
|
|
83
|
+
},
|
|
84
|
+
"ios": {
|
|
85
|
+
"componentProvider": {
|
|
86
|
+
"FastImageView": "FFFastImageViewComponentView"
|
|
87
|
+
},
|
|
88
|
+
"modulesProvider": {
|
|
89
|
+
"FastImageModule": "FFFastImageModule"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
73
92
|
}
|
|
74
93
|
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// FastImage.resizeMode, priority and cacheControl (deprecated), shared by the
|
|
2
|
+
// native (index.tsx) and web (index.web.tsx) versions.
|
|
3
|
+
|
|
4
|
+
// resizeMode was removed in 10.0 and is ignored (objectFit instead): its
|
|
5
|
+
// values are kept so code that reads them, e.g. FastImage.resizeMode.contain,
|
|
6
|
+
// doesn't throw.
|
|
7
|
+
export const resizeMode = {
|
|
8
|
+
contain: 'contain',
|
|
9
|
+
cover: 'cover',
|
|
10
|
+
stretch: 'stretch',
|
|
11
|
+
center: 'center',
|
|
12
|
+
repeat: 'repeat',
|
|
13
|
+
} as const
|
|
14
|
+
|
|
15
|
+
export const priority = {
|
|
16
|
+
low: 'low',
|
|
17
|
+
normal: 'normal',
|
|
18
|
+
high: 'high',
|
|
19
|
+
} as const
|
|
20
|
+
|
|
21
|
+
export const cacheControl = {
|
|
22
|
+
// Ignore headers, use uri as cache key, fetch only if not in cache.
|
|
23
|
+
immutable: 'immutable',
|
|
24
|
+
// Respect http headers, no aggressive caching.
|
|
25
|
+
web: 'web',
|
|
26
|
+
// Only load from cache.
|
|
27
|
+
cacheOnly: 'cacheOnly',
|
|
28
|
+
} as const
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import type { NativeSyntheticEvent, ViewProps } from 'react-native'
|
|
4
|
+
|
|
5
|
+
export type OnLoadEvent = NativeSyntheticEvent<
|
|
6
|
+
$ReadOnly<{
|
|
7
|
+
width: number,
|
|
8
|
+
height: number,
|
|
9
|
+
// The view's React tag (on iOS and Android; not on the web).
|
|
10
|
+
target?: number,
|
|
11
|
+
}>,
|
|
12
|
+
>
|
|
13
|
+
|
|
14
|
+
export type OnErrorEvent = NativeSyntheticEvent<
|
|
15
|
+
$ReadOnly<{|
|
|
16
|
+
error: string,
|
|
17
|
+
|}>,
|
|
18
|
+
>
|
|
19
|
+
|
|
20
|
+
export type OnProgressEvent = NativeSyntheticEvent<
|
|
21
|
+
$ReadOnly<{|
|
|
22
|
+
loaded: number,
|
|
23
|
+
total: number,
|
|
24
|
+
// loaded / total, from 0 to 1.
|
|
25
|
+
progress: number,
|
|
26
|
+
|}>,
|
|
27
|
+
>
|
|
28
|
+
|
|
29
|
+
export type Priority = $ReadOnly<{|
|
|
30
|
+
low: 'low',
|
|
31
|
+
normal: 'normal',
|
|
32
|
+
high: 'high',
|
|
33
|
+
|}>
|
|
34
|
+
|
|
35
|
+
export type ResizeMode = $ReadOnly<{|
|
|
36
|
+
contain: 'contain',
|
|
37
|
+
cover: 'cover',
|
|
38
|
+
stretch: 'stretch',
|
|
39
|
+
center: 'center',
|
|
40
|
+
repeat: 'repeat',
|
|
41
|
+
|}>
|
|
42
|
+
|
|
43
|
+
export type CacheControl = $ReadOnly<{|
|
|
44
|
+
immutable: 'immutable',
|
|
45
|
+
web: 'web',
|
|
46
|
+
cacheOnly: 'cacheOnly',
|
|
47
|
+
|}>
|
|
48
|
+
|
|
49
|
+
export type ObjectFit = 'fill' | 'contain' | 'cover' | 'none' | 'scale-down'
|
|
50
|
+
export type PositionLength = number | string
|
|
51
|
+
export type ObjectPosition =
|
|
52
|
+
| string
|
|
53
|
+
| $ReadOnly<{|
|
|
54
|
+
top?: PositionLength,
|
|
55
|
+
bottom?: PositionLength,
|
|
56
|
+
left?: PositionLength,
|
|
57
|
+
right?: PositionLength,
|
|
58
|
+
|}>
|
|
59
|
+
export type Priorities = $Values<Priority>
|
|
60
|
+
export type CacheControls = $Values<CacheControl>
|
|
61
|
+
|
|
62
|
+
export type LoadResult =
|
|
63
|
+
| {| ok: true, width: number, height: number |}
|
|
64
|
+
| {| ok: false, error: string |}
|
|
65
|
+
|
|
66
|
+
export type CacheLimits = {|
|
|
67
|
+
maxDiskSize?: ?number,
|
|
68
|
+
maxDiskAge?: ?number,
|
|
69
|
+
maxMemorySize?: ?number,
|
|
70
|
+
|}
|
|
71
|
+
|
|
72
|
+
export type CacheState = {|
|
|
73
|
+
maxDiskSize?: number,
|
|
74
|
+
maxDiskAge?: number,
|
|
75
|
+
maxMemorySize?: number,
|
|
76
|
+
diskSize?: number,
|
|
77
|
+
|}
|
|
78
|
+
|
|
79
|
+
export type CachePathResult =
|
|
80
|
+
| {| ok: true, path: string |}
|
|
81
|
+
| {| ok: false, error: string |}
|
|
82
|
+
|
|
83
|
+
export type ClearCacheResult = {| ok: true |} | {| ok: false, error: string |}
|
|
84
|
+
|
|
85
|
+
export type Transition = $ReadOnly<{|
|
|
86
|
+
duration?: ?number,
|
|
87
|
+
betweenImages?: ?boolean,
|
|
88
|
+
skipOnCacheHit?: ?('none' | 'memory' | 'all'),
|
|
89
|
+
|}>
|
|
90
|
+
|
|
91
|
+
export type FastImageDefaults = $ReadOnly<{|
|
|
92
|
+
transition?: ?(boolean | number | Transition),
|
|
93
|
+
|}>
|
|
94
|
+
|
|
95
|
+
export type PreloadSuccess = {|
|
|
96
|
+
uri: string,
|
|
97
|
+
ok: true,
|
|
98
|
+
width: number,
|
|
99
|
+
height: number,
|
|
100
|
+
|}
|
|
101
|
+
|
|
102
|
+
export type PreloadFailure = {|
|
|
103
|
+
uri?: string,
|
|
104
|
+
ok: false,
|
|
105
|
+
error: string,
|
|
106
|
+
|}
|
|
107
|
+
|
|
108
|
+
export type PreloadResult = PreloadSuccess | PreloadFailure
|
|
109
|
+
|
|
110
|
+
export type PreloadFn = (
|
|
111
|
+
sources: Array<FastImageSource>,
|
|
112
|
+
) => Promise<Array<PreloadResult>>
|
|
113
|
+
export type FastImageSource = {
|
|
114
|
+
uri?: string,
|
|
115
|
+
headers?: Object,
|
|
116
|
+
priority?: Priorities,
|
|
117
|
+
cache?: CacheControls,
|
|
118
|
+
cacheKey?: string,
|
|
119
|
+
memoryCache?: boolean,
|
|
120
|
+
diskCache?: boolean,
|
|
121
|
+
width?: number,
|
|
122
|
+
height?: number,
|
|
123
|
+
scale?: number,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type FastImageProps = $ReadOnly<{|
|
|
127
|
+
...ViewProps,
|
|
128
|
+
onError?: ?(event: OnErrorEvent) => void,
|
|
129
|
+
onLoad?: ?(event: OnLoadEvent) => void,
|
|
130
|
+
onLoadEnd?: ?(result: LoadResult) => void,
|
|
131
|
+
onLoadStart?: ?() => void,
|
|
132
|
+
onProgress?: ?(event: OnProgressEvent) => void,
|
|
133
|
+
|
|
134
|
+
source?: ?(FastImageSource | Array<FastImageSource> | number),
|
|
135
|
+
defaultSource?: ?number,
|
|
136
|
+
|
|
137
|
+
tintColor?: number | string,
|
|
138
|
+
objectFit?: ?ObjectFit,
|
|
139
|
+
objectPosition?: ?ObjectPosition,
|
|
140
|
+
recyclingKey?: ?string,
|
|
141
|
+
loop?: ?(boolean | number),
|
|
142
|
+
imageRendering?: ?('auto' | 'smooth' | 'pixelated'),
|
|
143
|
+
paused?: ?boolean,
|
|
144
|
+
transition?: ?(boolean | number | Transition),
|
|
145
|
+
downsample?: ?boolean,
|
|
146
|
+
blurRadius?: ?number,
|
|
147
|
+
testID?: ?string,
|
|
148
|
+
// FastImage is a single native view, which doesn't take children (since
|
|
149
|
+
// 10.0): use FastImageBackground.
|
|
150
|
+
children?: "FastImage doesn't take children: use FastImageBackground for content over an image",
|
|
151
|
+
|}>
|
|
152
|
+
|
|
153
|
+
export type FastImageBackgroundProps = $ReadOnly<{|
|
|
154
|
+
...FastImageProps,
|
|
155
|
+
imageStyle?: $PropertyType<ViewProps, 'style'>,
|
|
156
|
+
imageRef?: React$Ref<any>,
|
|
157
|
+
children?: React$Node,
|
|
158
|
+
|}>
|
|
159
|
+
|
|
160
|
+
declare export var FastImageBackground: React$ComponentType<FastImageBackgroundProps>
|
|
161
|
+
|
|
162
|
+
declare export default class FastImage extends React$Component<FastImageProps> {
|
|
163
|
+
// Deprecated: removed in 10.0, and a resizeMode prop is ignored; use
|
|
164
|
+
// objectFit. Kept so code that reads it doesn't throw.
|
|
165
|
+
static resizeMode: ResizeMode;
|
|
166
|
+
// Deprecated: use the strings instead, e.g. priority: 'high'.
|
|
167
|
+
static priority: Priority;
|
|
168
|
+
// Deprecated: use the strings instead, e.g. cache: 'immutable'.
|
|
169
|
+
static cacheControl: CacheControl;
|
|
170
|
+
static preload: PreloadFn;
|
|
171
|
+
static clearMemoryCache: () => Promise<ClearCacheResult>;
|
|
172
|
+
static clearDiskCache: () => Promise<ClearCacheResult>;
|
|
173
|
+
static getCachePath: (source: FastImageSource) => Promise<CachePathResult>;
|
|
174
|
+
static configureCache: (limits?: CacheLimits) => Promise<CacheState>;
|
|
175
|
+
static setDefaults: (defaults: FastImageDefaults) => void;
|
|
176
|
+
static writeToCache: (
|
|
177
|
+
source: FastImageSource,
|
|
178
|
+
file: string,
|
|
179
|
+
) => Promise<CachePathResult>;
|
|
180
|
+
}
|