react-native-fast-image 8.6.31 → 8.6.33
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 +1 -1
- package/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java +32 -13
- package/android/src/main/java/com/dylanvann/fastimage/FastImageShadowNode.java +21 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageSourceSize.java +237 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java +13 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +18 -3
- package/dist/index.cjs.js +137 -198
- package/dist/index.d.ts +15 -16
- package/dist/index.js +112 -189
- package/package.json +18 -23
- package/dist/index.d.ts.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -176,7 +176,7 @@ e.g. `onProgress={e => console.log(e.nativeEvent.loaded / e.nativeEvent.total)}`
|
|
|
176
176
|
|
|
177
177
|
### `onLoad?: (event) => void`
|
|
178
178
|
|
|
179
|
-
Called on a successful image fetch. Called with the width and height of the
|
|
179
|
+
Called on a successful image fetch. Called with the width and height of the image itself, not of the view (on iOS, in points: an `@2x` asset reports half its pixel size).
|
|
180
180
|
|
|
181
181
|
e.g. `onLoad={e => console.log(e.nativeEvent.width, e.nativeEvent.height)}`
|
|
182
182
|
|
|
@@ -4,6 +4,8 @@ import android.graphics.drawable.Drawable;
|
|
|
4
4
|
|
|
5
5
|
import com.bumptech.glide.load.DataSource;
|
|
6
6
|
import com.bumptech.glide.load.engine.GlideException;
|
|
7
|
+
import com.bumptech.glide.load.model.GlideUrl;
|
|
8
|
+
import com.bumptech.glide.request.Request;
|
|
7
9
|
import com.bumptech.glide.request.RequestListener;
|
|
8
10
|
import com.bumptech.glide.request.target.ImageViewTarget;
|
|
9
11
|
import com.bumptech.glide.request.target.Target;
|
|
@@ -17,16 +19,11 @@ public class FastImageRequestListener implements RequestListener<Drawable> {
|
|
|
17
19
|
static final String REACT_ON_LOAD_EVENT = "onFastImageLoad";
|
|
18
20
|
static final String REACT_ON_LOAD_END_EVENT = "onFastImageLoadEnd";
|
|
19
21
|
private final String key;
|
|
22
|
+
private final FastImageSource source;
|
|
20
23
|
|
|
21
|
-
FastImageRequestListener(String key) {
|
|
24
|
+
FastImageRequestListener(String key, FastImageSource source) {
|
|
22
25
|
this.key = key;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
private static WritableMap mapFromResource(Drawable resource) {
|
|
26
|
-
WritableMap resourceData = new WritableNativeMap();
|
|
27
|
-
resourceData.putInt("width", resource.getIntrinsicWidth());
|
|
28
|
-
resourceData.putInt("height", resource.getIntrinsicHeight());
|
|
29
|
-
return resourceData;
|
|
26
|
+
this.source = source;
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
@Override
|
|
@@ -48,19 +45,41 @@ public class FastImageRequestListener implements RequestListener<Drawable> {
|
|
|
48
45
|
}
|
|
49
46
|
|
|
50
47
|
@Override
|
|
51
|
-
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
|
|
48
|
+
public boolean onResourceReady(Drawable resource, Object model, final Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
|
|
52
49
|
if (!(target instanceof ImageViewTarget)) {
|
|
53
50
|
return false;
|
|
54
51
|
}
|
|
55
|
-
FastImageViewWithUrl view = (FastImageViewWithUrl) ((ImageViewTarget) target).getView();
|
|
52
|
+
final FastImageViewWithUrl view = (FastImageViewWithUrl) ((ImageViewTarget) target).getView();
|
|
53
|
+
boolean local = !(model instanceof GlideUrl);
|
|
54
|
+
int[] size = FastImageSourceSize.get(resource, model, local,
|
|
55
|
+
dataSource == DataSource.RESOURCE_DISK_CACHE);
|
|
56
|
+
if (size != null) {
|
|
57
|
+
sendLoad(view, size);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
// A local image whose size isn't known: read it from the image first,
|
|
61
|
+
// unless the view starts another load meanwhile.
|
|
62
|
+
final Request request = target.getRequest();
|
|
63
|
+
FastImageSourceSize.readLocal(view.getContext(), source, resource, model, new FastImageSourceSize.Callback() {
|
|
64
|
+
@Override
|
|
65
|
+
public void onSize(int[] size) {
|
|
66
|
+
if (target.getRequest() == request) sendLoad(view, size);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private static void sendLoad(FastImageViewWithUrl view, int[] size) {
|
|
56
73
|
ReactContext context = FastImageViewManager.getReactContext(view.getContext());
|
|
57
74
|
if (context == null) {
|
|
58
|
-
return
|
|
75
|
+
return;
|
|
59
76
|
}
|
|
60
77
|
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
|
|
61
78
|
int viewId = view.getId();
|
|
62
|
-
|
|
79
|
+
WritableMap event = new WritableNativeMap();
|
|
80
|
+
event.putInt("width", size[0]);
|
|
81
|
+
event.putInt("height", size[1]);
|
|
82
|
+
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_EVENT, event);
|
|
63
83
|
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap());
|
|
64
|
-
return false;
|
|
65
84
|
}
|
|
66
85
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
package com.dylanvann.fastimage;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.uimanager.LayoutShadowNode;
|
|
4
|
+
import com.facebook.react.uimanager.UIViewOperationQueue;
|
|
5
|
+
|
|
6
|
+
// Legacy architecture only. React Native only lays out a view when its frame
|
|
7
|
+
// changes, so a view that stays 0×0 at its parent's origin is never laid out,
|
|
8
|
+
// and Glide waits for its size forever (#865). When the computed size is zero,
|
|
9
|
+
// tell the view, so it can lay itself out and load as it does on the New
|
|
10
|
+
// Architecture. Remove with legacy architecture support.
|
|
11
|
+
public class FastImageShadowNode extends LayoutShadowNode {
|
|
12
|
+
static final Object ZERO_LAYOUT = new Object();
|
|
13
|
+
|
|
14
|
+
@Override
|
|
15
|
+
public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
|
|
16
|
+
super.onCollectExtraUpdates(uiViewOperationQueue);
|
|
17
|
+
if (Math.round(getLayoutWidth()) == 0 && Math.round(getLayoutHeight()) == 0) {
|
|
18
|
+
uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), ZERO_LAYOUT);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
package com.dylanvann.fastimage;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.graphics.BitmapFactory;
|
|
5
|
+
import android.graphics.drawable.Drawable;
|
|
6
|
+
import android.net.Uri;
|
|
7
|
+
import android.os.Handler;
|
|
8
|
+
import android.os.Looper;
|
|
9
|
+
import android.util.Base64;
|
|
10
|
+
import android.util.LruCache;
|
|
11
|
+
import android.widget.ImageView;
|
|
12
|
+
|
|
13
|
+
import androidx.annotation.Nullable;
|
|
14
|
+
|
|
15
|
+
import com.bumptech.glide.Glide;
|
|
16
|
+
import com.bumptech.glide.load.ImageHeaderParserUtils;
|
|
17
|
+
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy;
|
|
18
|
+
import com.bumptech.glide.load.resource.gif.GifDrawable;
|
|
19
|
+
import com.bumptech.glide.request.RequestOptions;
|
|
20
|
+
|
|
21
|
+
import java.io.ByteArrayInputStream;
|
|
22
|
+
import java.io.IOException;
|
|
23
|
+
import java.io.InputStream;
|
|
24
|
+
import java.nio.ByteBuffer;
|
|
25
|
+
import java.util.concurrent.Executor;
|
|
26
|
+
import java.util.concurrent.Executors;
|
|
27
|
+
|
|
28
|
+
// Works out an image's own size for onLoad, as iOS reports it (#608). Glide
|
|
29
|
+
// decodes images to fit the view, so the loaded drawable is usually smaller
|
|
30
|
+
// than the image.
|
|
31
|
+
final class FastImageSourceSize {
|
|
32
|
+
// Sizes of recently decoded images, for loads from the memory cache, which
|
|
33
|
+
// don't decode.
|
|
34
|
+
private static final LruCache<String, int[]> SIZES = new LruCache<>(500);
|
|
35
|
+
private static final Executor EXECUTOR = Executors.newSingleThreadExecutor();
|
|
36
|
+
private static final Handler MAIN = new Handler(Looper.getMainLooper());
|
|
37
|
+
|
|
38
|
+
private FastImageSourceSize() {
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Wraps the downsample strategy Glide would use, to record the image's
|
|
42
|
+
// size (after EXIF rotation) when Glide decodes it. Views loading the same
|
|
43
|
+
// image at the same size share one decode, which uses the first view's
|
|
44
|
+
// options, so the size is recorded by image rather than by request.
|
|
45
|
+
static final class Capture extends DownsampleStrategy {
|
|
46
|
+
private final DownsampleStrategy strategy;
|
|
47
|
+
private final String key;
|
|
48
|
+
private boolean recorded;
|
|
49
|
+
|
|
50
|
+
Capture(DownsampleStrategy strategy, String key) {
|
|
51
|
+
this.strategy = strategy;
|
|
52
|
+
this.key = key;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@Override
|
|
56
|
+
public float getScaleFactor(int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
|
|
57
|
+
// Called again with the sampled size; the first call has the image's.
|
|
58
|
+
if (!recorded) {
|
|
59
|
+
recorded = true;
|
|
60
|
+
SIZES.put(key, new int[]{sourceWidth, sourceHeight});
|
|
61
|
+
}
|
|
62
|
+
return strategy.getScaleFactor(sourceWidth, sourceHeight, requestedWidth, requestedHeight);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Override
|
|
66
|
+
public SampleSizeRounding getSampleSizeRounding(int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
|
|
67
|
+
return strategy.getSampleSizeRounding(sourceWidth, sourceHeight, requestedWidth, requestedHeight);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Glide's memory cache keys include the strategy, so match the wrapped
|
|
71
|
+
// one to keep sharing cached images between views.
|
|
72
|
+
@Override
|
|
73
|
+
public boolean equals(Object o) {
|
|
74
|
+
return o instanceof Capture && ((Capture) o).strategy.equals(strategy);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Override
|
|
78
|
+
public int hashCode() {
|
|
79
|
+
return strategy.hashCode();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// What RequestBuilder.into(ImageView) applies for the view's scale type
|
|
84
|
+
// (it leaves a transformation that's already set alone), with the
|
|
85
|
+
// downsample strategy it implies wrapped by capture.
|
|
86
|
+
static RequestOptions scaleTypeOptions(@Nullable ImageView.ScaleType scaleType, Capture capture) {
|
|
87
|
+
RequestOptions options = new RequestOptions();
|
|
88
|
+
if (scaleType != null) {
|
|
89
|
+
switch (scaleType) {
|
|
90
|
+
case CENTER_CROP:
|
|
91
|
+
options = options.optionalCenterCrop();
|
|
92
|
+
break;
|
|
93
|
+
case CENTER_INSIDE:
|
|
94
|
+
case FIT_XY:
|
|
95
|
+
options = options.optionalCenterInside();
|
|
96
|
+
break;
|
|
97
|
+
case FIT_CENTER:
|
|
98
|
+
case FIT_START:
|
|
99
|
+
case FIT_END:
|
|
100
|
+
options = options.optionalFitCenter();
|
|
101
|
+
break;
|
|
102
|
+
default:
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return options.downsample(capture);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
static Capture capture(@Nullable ImageView.ScaleType scaleType, Object model) {
|
|
110
|
+
return new Capture(strategy(scaleType), String.valueOf(model));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private static DownsampleStrategy strategy(@Nullable ImageView.ScaleType scaleType) {
|
|
114
|
+
if (scaleType == null) return DownsampleStrategy.DEFAULT;
|
|
115
|
+
switch (scaleType) {
|
|
116
|
+
case CENTER_CROP:
|
|
117
|
+
return DownsampleStrategy.CENTER_OUTSIDE;
|
|
118
|
+
case CENTER_INSIDE:
|
|
119
|
+
case FIT_XY:
|
|
120
|
+
return DownsampleStrategy.CENTER_INSIDE;
|
|
121
|
+
case FIT_CENTER:
|
|
122
|
+
case FIT_START:
|
|
123
|
+
case FIT_END:
|
|
124
|
+
return DownsampleStrategy.FIT_CENTER;
|
|
125
|
+
default:
|
|
126
|
+
return DownsampleStrategy.DEFAULT;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// The image's size, or null when it has to be read from a local image's
|
|
131
|
+
// header first (see readLocal).
|
|
132
|
+
@Nullable
|
|
133
|
+
static int[] get(Drawable resource, Object model, boolean local, boolean fromResourceCache) {
|
|
134
|
+
String key = String.valueOf(model);
|
|
135
|
+
if (resource instanceof GifDrawable) {
|
|
136
|
+
// Glide decodes GIFs itself (sampled to fit the view), without the
|
|
137
|
+
// downsample strategy; their header has the size.
|
|
138
|
+
int[] size = gifSize((GifDrawable) resource);
|
|
139
|
+
if (size != null) return size;
|
|
140
|
+
} else if (fromResourceCache) {
|
|
141
|
+
// Glide caches local images already resized for the view, and the
|
|
142
|
+
// decode from there recorded the resized size.
|
|
143
|
+
SIZES.remove(key);
|
|
144
|
+
return null;
|
|
145
|
+
} else {
|
|
146
|
+
int[] size = SIZES.get(key);
|
|
147
|
+
if (size != null) return size;
|
|
148
|
+
if (local) return null;
|
|
149
|
+
}
|
|
150
|
+
return new int[]{resource.getIntrinsicWidth(), resource.getIntrinsicHeight()};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@Nullable
|
|
154
|
+
private static int[] gifSize(GifDrawable gif) {
|
|
155
|
+
ByteBuffer buffer = gif.getBuffer();
|
|
156
|
+
if (buffer == null || buffer.limit() < 10) return null;
|
|
157
|
+
// The logical screen width and height (little-endian) follow the
|
|
158
|
+
// 6-byte signature.
|
|
159
|
+
int width = (buffer.get(6) & 0xff) | (buffer.get(7) & 0xff) << 8;
|
|
160
|
+
int height = (buffer.get(8) & 0xff) | (buffer.get(9) & 0xff) << 8;
|
|
161
|
+
return new int[]{width, height};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
interface Callback {
|
|
165
|
+
void onSize(int[] size);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Reads a local image's size from its header, off the main thread.
|
|
169
|
+
static void readLocal(Context context, final FastImageSource source, final Drawable resource, final Object model, final Callback callback) {
|
|
170
|
+
final Context appContext = context.getApplicationContext();
|
|
171
|
+
EXECUTOR.execute(new Runnable() {
|
|
172
|
+
@Override
|
|
173
|
+
public void run() {
|
|
174
|
+
int[] size = readBounds(appContext, source);
|
|
175
|
+
if (size != null) {
|
|
176
|
+
SIZES.put(String.valueOf(model), size);
|
|
177
|
+
} else {
|
|
178
|
+
size = new int[]{resource.getIntrinsicWidth(), resource.getIntrinsicHeight()};
|
|
179
|
+
}
|
|
180
|
+
final int[] result = size;
|
|
181
|
+
MAIN.post(new Runnable() {
|
|
182
|
+
@Override
|
|
183
|
+
public void run() {
|
|
184
|
+
callback.onSize(result);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@Nullable
|
|
192
|
+
private static int[] readBounds(Context context, FastImageSource source) {
|
|
193
|
+
try {
|
|
194
|
+
BitmapFactory.Options options = new BitmapFactory.Options();
|
|
195
|
+
options.inJustDecodeBounds = true;
|
|
196
|
+
InputStream stream = open(context, source);
|
|
197
|
+
try {
|
|
198
|
+
BitmapFactory.decodeStream(stream, null, options);
|
|
199
|
+
} finally {
|
|
200
|
+
stream.close();
|
|
201
|
+
}
|
|
202
|
+
if (options.outWidth <= 0 || options.outHeight <= 0) return null;
|
|
203
|
+
int orientation;
|
|
204
|
+
stream = open(context, source);
|
|
205
|
+
try {
|
|
206
|
+
Glide glide = Glide.get(context);
|
|
207
|
+
orientation = ImageHeaderParserUtils.getOrientation(
|
|
208
|
+
glide.getRegistry().getImageHeaderParsers(), stream, glide.getArrayPool());
|
|
209
|
+
} finally {
|
|
210
|
+
stream.close();
|
|
211
|
+
}
|
|
212
|
+
// EXIF orientations 5 to 8 rotate by 90 or 270 degrees.
|
|
213
|
+
boolean rotated = orientation >= 5 && orientation <= 8;
|
|
214
|
+
return rotated
|
|
215
|
+
? new int[]{options.outHeight, options.outWidth}
|
|
216
|
+
: new int[]{options.outWidth, options.outHeight};
|
|
217
|
+
} catch (IOException | RuntimeException e) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private static InputStream open(Context context, FastImageSource source) throws IOException {
|
|
223
|
+
if (source.isBase64Resource()) {
|
|
224
|
+
String data = source.getSource();
|
|
225
|
+
byte[] bytes = Base64.decode(data.substring(data.indexOf(',') + 1), Base64.DEFAULT);
|
|
226
|
+
return new ByteArrayInputStream(bytes);
|
|
227
|
+
}
|
|
228
|
+
Uri uri = source.getUri();
|
|
229
|
+
String path = uri.getPath();
|
|
230
|
+
if ("file".equals(uri.getScheme()) && path != null && path.startsWith("/android_asset/")) {
|
|
231
|
+
return context.getAssets().open(path.substring("/android_asset/".length()));
|
|
232
|
+
}
|
|
233
|
+
InputStream stream = context.getContentResolver().openInputStream(uri);
|
|
234
|
+
if (stream == null) throw new IOException("Can't open " + uri);
|
|
235
|
+
return stream;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -18,6 +18,7 @@ import com.facebook.react.bridge.ReadableMap;
|
|
|
18
18
|
import com.facebook.react.bridge.WritableMap;
|
|
19
19
|
import com.facebook.react.bridge.WritableNativeMap;
|
|
20
20
|
import com.facebook.react.common.MapBuilder;
|
|
21
|
+
import com.facebook.react.uimanager.LayoutShadowNode;
|
|
21
22
|
import com.facebook.react.uimanager.SimpleViewManager;
|
|
22
23
|
import com.facebook.react.bridge.ReactContext;
|
|
23
24
|
import com.facebook.react.uimanager.ThemedReactContext;
|
|
@@ -190,6 +191,18 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
|
|
|
190
191
|
|
|
191
192
|
}
|
|
192
193
|
|
|
194
|
+
// Legacy architecture only; the New Architecture doesn't use shadow nodes.
|
|
195
|
+
@NonNull
|
|
196
|
+
@Override
|
|
197
|
+
public LayoutShadowNode createShadowNodeInstance() {
|
|
198
|
+
return new FastImageShadowNode();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@Override
|
|
202
|
+
public void updateExtraData(@NonNull FastImageViewWithUrl view, Object extraData) {
|
|
203
|
+
if (extraData == FastImageShadowNode.ZERO_LAYOUT) view.onZeroLayout();
|
|
204
|
+
}
|
|
205
|
+
|
|
193
206
|
@Override
|
|
194
207
|
protected void onAfterUpdateTransaction(@NonNull FastImageViewWithUrl view) {
|
|
195
208
|
super.onAfterUpdateTransaction(view);
|
|
@@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable;
|
|
|
9
9
|
|
|
10
10
|
import androidx.annotation.Nullable;
|
|
11
11
|
import androidx.appcompat.widget.AppCompatImageView;
|
|
12
|
+
import androidx.core.view.ViewCompat;
|
|
12
13
|
|
|
13
14
|
import com.bumptech.glide.RequestBuilder;
|
|
14
15
|
import com.bumptech.glide.RequestManager;
|
|
@@ -48,6 +49,14 @@ class FastImageViewWithUrl extends AppCompatImageView {
|
|
|
48
49
|
mDefaultSource = source;
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
// Legacy architecture only (see FastImageShadowNode): the view's layout is
|
|
53
|
+
// 0×0 at its parent's origin, which React Native never applies. Lay it out
|
|
54
|
+
// at that size, as the New Architecture does, so Glide stops waiting for a
|
|
55
|
+
// size and loads it (#865).
|
|
56
|
+
void onZeroLayout() {
|
|
57
|
+
if (!ViewCompat.isLaidOut(this)) layout(0, 0, 0, 0);
|
|
58
|
+
}
|
|
59
|
+
|
|
51
60
|
// Glide crops or fits the bitmap for the scale type when it loads, so a
|
|
52
61
|
// new resizeMode needs a reload to take effect (#762).
|
|
53
62
|
public void setResizeMode(ScaleType scaleType) {
|
|
@@ -143,6 +152,9 @@ class FastImageViewWithUrl extends AppCompatImageView {
|
|
|
143
152
|
}
|
|
144
153
|
|
|
145
154
|
if (requestManager != null) {
|
|
155
|
+
// Records the image's own size when Glide decodes it, for onLoad.
|
|
156
|
+
Object model = imageSource == null ? null : imageSource.getSourceForLoad();
|
|
157
|
+
FastImageSourceSize.Capture capture = FastImageSourceSize.capture(getScaleType(), model);
|
|
146
158
|
RequestBuilder<Drawable> builder =
|
|
147
159
|
requestManager
|
|
148
160
|
// This will make this work for remote and local images. e.g.
|
|
@@ -151,14 +163,17 @@ class FastImageViewWithUrl extends AppCompatImageView {
|
|
|
151
163
|
// - res:/
|
|
152
164
|
// - android.resource://
|
|
153
165
|
// - data:image/png;base64
|
|
154
|
-
.load(
|
|
166
|
+
.load(model)
|
|
155
167
|
.apply(FastImageViewConverter
|
|
156
168
|
.getOptions(getContext(), imageSource, mSource)
|
|
157
169
|
.placeholder(mDefaultSource) // show until loaded
|
|
158
|
-
.fallback(mDefaultSource))
|
|
170
|
+
.fallback(mDefaultSource)) // null will not be treated as error
|
|
171
|
+
// What into() would apply for the scale type, with
|
|
172
|
+
// the size capture.
|
|
173
|
+
.apply(FastImageSourceSize.scaleTypeOptions(getScaleType(), capture));
|
|
159
174
|
|
|
160
175
|
if (key != null)
|
|
161
|
-
builder.listener(new FastImageRequestListener(key));
|
|
176
|
+
builder.listener(new FastImageRequestListener(key, imageSource));
|
|
162
177
|
|
|
163
178
|
builder.into(this);
|
|
164
179
|
}
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,214 +1,153 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
//#endregion
|
|
23
|
+
let react = require("react");
|
|
24
|
+
react = __toESM(react);
|
|
25
|
+
let react_native = require("react-native");
|
|
26
|
+
//#region src/index.tsx
|
|
15
27
|
const resizeMode = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
contain: "contain",
|
|
29
|
+
cover: "cover",
|
|
30
|
+
stretch: "stretch",
|
|
31
|
+
center: "center"
|
|
20
32
|
};
|
|
21
33
|
const priority = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
34
|
+
low: "low",
|
|
35
|
+
normal: "normal",
|
|
36
|
+
high: "high"
|
|
25
37
|
};
|
|
26
38
|
const cacheControl = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
immutable: "immutable",
|
|
40
|
+
web: "web",
|
|
41
|
+
cacheOnly: "cacheOnly"
|
|
42
|
+
};
|
|
43
|
+
const resolveDefaultSource = (defaultSource) => {
|
|
44
|
+
if (!defaultSource) return null;
|
|
45
|
+
if (react_native.Platform.OS === "android") {
|
|
46
|
+
const resolved = react_native.Image.resolveAssetSource(defaultSource);
|
|
47
|
+
if (resolved) return resolved.uri;
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return defaultSource;
|
|
33
51
|
};
|
|
34
|
-
|
|
35
|
-
const resolveDefaultSource = defaultSource => {
|
|
36
|
-
if (!defaultSource) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (reactNative.Platform.OS === 'android') {
|
|
41
|
-
// Android receives a URI string, and resolves into a Drawable using RN's methods.
|
|
42
|
-
const resolved = reactNative.Image.resolveAssetSource(defaultSource);
|
|
43
|
-
|
|
44
|
-
if (resolved) {
|
|
45
|
-
return resolved.uri;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return null;
|
|
49
|
-
} // iOS or other number mapped assets
|
|
50
|
-
// In iOS the number is passed, and bridged automatically into a UIImage
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return defaultSource;
|
|
54
|
-
}; // Finds tintColor in a style prop, where the last style that sets it wins, as
|
|
55
|
-
// with StyleSheet.flatten, but without flattening (which allocates a merged
|
|
56
|
-
// object for an array style on every render).
|
|
57
|
-
|
|
58
|
-
|
|
59
52
|
function tintColorFromStyle(style) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return style && typeof style === 'object' ? style.tintColor : undefined;
|
|
76
|
-
} // A copy of the source without `cache`.
|
|
77
|
-
|
|
78
|
-
|
|
53
|
+
if (Array.isArray(style)) {
|
|
54
|
+
for (let i = style.length - 1; i >= 0; i--) {
|
|
55
|
+
const found = tintColorFromStyle(style[i]);
|
|
56
|
+
if (found !== void 0) return found;
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (typeof style === "number") {
|
|
61
|
+
const flattened = react_native.StyleSheet.flatten(style);
|
|
62
|
+
return flattened ? flattened.tintColor : void 0;
|
|
63
|
+
}
|
|
64
|
+
return style && typeof style === "object" ? style.tintColor : void 0;
|
|
65
|
+
}
|
|
79
66
|
function withoutCache(source) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
...rest
|
|
83
|
-
} = source || {};
|
|
84
|
-
return rest;
|
|
67
|
+
const { cache: _cache, ...rest } = source || {};
|
|
68
|
+
return rest;
|
|
85
69
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
ref: forwardedRef
|
|
138
|
-
}), /*#__PURE__*/React__default['default'].createElement(reactNative.Image, _extends__default['default']({}, imageProps, {
|
|
139
|
-
style: [styles.fallbackImage, {
|
|
140
|
-
tintColor: resolvedTintColor
|
|
141
|
-
}],
|
|
142
|
-
source: resolvedSource,
|
|
143
|
-
defaultSource: defaultSource,
|
|
144
|
-
onLoadStart: onLoadStart,
|
|
145
|
-
onProgress: onProgress,
|
|
146
|
-
onLoad: onLoad,
|
|
147
|
-
onError: onError,
|
|
148
|
-
onLoadEnd: onLoadEnd,
|
|
149
|
-
resizeMode: resizeMode
|
|
150
|
-
})), children);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const resolvedSource = reactNative.Image.resolveAssetSource(source);
|
|
154
|
-
const resolvedDefaultSource = resolveDefaultSource(defaultSource);
|
|
155
|
-
return /*#__PURE__*/React__default['default'].createElement(reactNative.View, _extends__default['default']({
|
|
156
|
-
style: [styles.imageContainer, style]
|
|
157
|
-
}, wrapperProps, {
|
|
158
|
-
ref: forwardedRef
|
|
159
|
-
}), /*#__PURE__*/React__default['default'].createElement(FastImageView, _extends__default['default']({}, imageProps, {
|
|
160
|
-
tintColor: resolvedTintColor,
|
|
161
|
-
style: reactNative.StyleSheet.absoluteFill,
|
|
162
|
-
source: resolvedSource,
|
|
163
|
-
defaultSource: resolvedDefaultSource,
|
|
164
|
-
onFastImageLoadStart: onLoadStart,
|
|
165
|
-
onFastImageProgress: onProgress,
|
|
166
|
-
onFastImageLoad: onLoad,
|
|
167
|
-
onFastImageError: onError,
|
|
168
|
-
onFastImageLoadEnd: onLoadEnd,
|
|
169
|
-
resizeMode: resizeMode
|
|
170
|
-
})), children);
|
|
70
|
+
function FastImageBase({ source, defaultSource, tintColor, onLoadStart, onProgress, onLoad, onError, onLoadEnd, style, fallback, children, resizeMode = "cover", forwardedRef, onLayout, pointerEvents, ...viewProps }) {
|
|
71
|
+
const { onClick, ...props } = viewProps;
|
|
72
|
+
const wrapperProps = {
|
|
73
|
+
onLayout,
|
|
74
|
+
onClick,
|
|
75
|
+
pointerEvents
|
|
76
|
+
};
|
|
77
|
+
const imageProps = {
|
|
78
|
+
...props,
|
|
79
|
+
pointerEvents: pointerEvents === "box-none" ? "none" : void 0
|
|
80
|
+
};
|
|
81
|
+
const resolvedTintColor = tintColor != null ? tintColor : tintColorFromStyle(style);
|
|
82
|
+
if (fallback) {
|
|
83
|
+
const cleanedSource = typeof source === "number" ? source : withoutCache(source);
|
|
84
|
+
const resolvedSource = react_native.Image.resolveAssetSource(cleanedSource);
|
|
85
|
+
return /* @__PURE__ */ react.default.createElement(react_native.View, {
|
|
86
|
+
style: [styles.imageContainer, style],
|
|
87
|
+
...wrapperProps,
|
|
88
|
+
ref: forwardedRef
|
|
89
|
+
}, /* @__PURE__ */ react.default.createElement(react_native.Image, {
|
|
90
|
+
...imageProps,
|
|
91
|
+
style: [styles.fallbackImage, { tintColor: resolvedTintColor }],
|
|
92
|
+
source: resolvedSource,
|
|
93
|
+
defaultSource,
|
|
94
|
+
onLoadStart,
|
|
95
|
+
onProgress,
|
|
96
|
+
onLoad,
|
|
97
|
+
onError,
|
|
98
|
+
onLoadEnd,
|
|
99
|
+
resizeMode
|
|
100
|
+
}), children);
|
|
101
|
+
}
|
|
102
|
+
const resolvedSource = react_native.Image.resolveAssetSource(source);
|
|
103
|
+
const resolvedDefaultSource = resolveDefaultSource(defaultSource);
|
|
104
|
+
return /* @__PURE__ */ react.default.createElement(react_native.View, {
|
|
105
|
+
style: [styles.imageContainer, style],
|
|
106
|
+
...wrapperProps,
|
|
107
|
+
ref: forwardedRef
|
|
108
|
+
}, /* @__PURE__ */ react.default.createElement(FastImageView, {
|
|
109
|
+
...imageProps,
|
|
110
|
+
tintColor: resolvedTintColor,
|
|
111
|
+
style: react_native.StyleSheet.absoluteFill,
|
|
112
|
+
source: resolvedSource,
|
|
113
|
+
defaultSource: resolvedDefaultSource,
|
|
114
|
+
onFastImageLoadStart: onLoadStart,
|
|
115
|
+
onFastImageProgress: onProgress,
|
|
116
|
+
onFastImageLoad: onLoad,
|
|
117
|
+
onFastImageError: onError,
|
|
118
|
+
onFastImageLoadEnd: onLoadEnd,
|
|
119
|
+
resizeMode
|
|
120
|
+
}), children);
|
|
171
121
|
}
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
FastImageComponent.displayName =
|
|
122
|
+
const FastImageMemo = (0, react.memo)(FastImageBase);
|
|
123
|
+
const FastImageComponent = (0, react.forwardRef)((props, ref) => /* @__PURE__ */ react.default.createElement(FastImageMemo, {
|
|
124
|
+
forwardedRef: ref,
|
|
125
|
+
...props
|
|
126
|
+
}));
|
|
127
|
+
FastImageComponent.displayName = "FastImage";
|
|
178
128
|
const FastImage = FastImageComponent;
|
|
179
129
|
FastImage.resizeMode = resizeMode;
|
|
180
130
|
FastImage.cacheControl = cacheControl;
|
|
181
131
|
FastImage.priority = priority;
|
|
182
|
-
|
|
183
|
-
FastImage.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
top: 0,
|
|
195
|
-
left: 0,
|
|
196
|
-
width: '100%',
|
|
197
|
-
height: '100%'
|
|
198
|
-
},
|
|
199
|
-
imageContainer: {
|
|
200
|
-
overflow: 'hidden'
|
|
201
|
-
}
|
|
202
|
-
}); // Types of requireNativeComponent are not correct.
|
|
203
|
-
|
|
204
|
-
const FastImageView = reactNative.requireNativeComponent('FastImageView', FastImage, {
|
|
205
|
-
nativeOnly: {
|
|
206
|
-
onFastImageLoadStart: true,
|
|
207
|
-
onFastImageProgress: true,
|
|
208
|
-
onFastImageLoad: true,
|
|
209
|
-
onFastImageError: true,
|
|
210
|
-
onFastImageLoadEnd: true
|
|
211
|
-
}
|
|
132
|
+
FastImage.preload = (sources) => react_native.NativeModules.FastImageView.preload(sources);
|
|
133
|
+
FastImage.clearMemoryCache = () => react_native.NativeModules.FastImageView.clearMemoryCache();
|
|
134
|
+
FastImage.clearDiskCache = () => react_native.NativeModules.FastImageView.clearDiskCache();
|
|
135
|
+
const styles = react_native.StyleSheet.create({
|
|
136
|
+
fallbackImage: {
|
|
137
|
+
position: "absolute",
|
|
138
|
+
top: 0,
|
|
139
|
+
left: 0,
|
|
140
|
+
width: "100%",
|
|
141
|
+
height: "100%"
|
|
142
|
+
},
|
|
143
|
+
imageContainer: { overflow: "hidden" }
|
|
212
144
|
});
|
|
213
|
-
|
|
145
|
+
const FastImageView = (0, react_native.requireNativeComponent)("FastImageView", FastImage, { nativeOnly: {
|
|
146
|
+
onFastImageLoadStart: true,
|
|
147
|
+
onFastImageProgress: true,
|
|
148
|
+
onFastImageLoad: true,
|
|
149
|
+
onFastImageError: true,
|
|
150
|
+
onFastImageLoadEnd: true
|
|
151
|
+
} });
|
|
152
|
+
//#endregion
|
|
214
153
|
module.exports = FastImage;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { LayoutChangeEvent, StyleProp, ViewStyle, ImageRequireSource, AccessibilityProps, ViewProps } from 'react-native';
|
|
3
|
-
|
|
4
|
-
export
|
|
3
|
+
type ColorValue = NonNullable<ViewStyle['backgroundColor']>;
|
|
4
|
+
export type ResizeMode = 'contain' | 'cover' | 'stretch' | 'center';
|
|
5
5
|
declare const resizeMode: {
|
|
6
|
-
readonly contain:
|
|
7
|
-
readonly cover:
|
|
8
|
-
readonly stretch:
|
|
9
|
-
readonly center:
|
|
6
|
+
readonly contain: 'contain';
|
|
7
|
+
readonly cover: 'cover';
|
|
8
|
+
readonly stretch: 'stretch';
|
|
9
|
+
readonly center: 'center';
|
|
10
10
|
};
|
|
11
|
-
export
|
|
11
|
+
export type Priority = 'low' | 'normal' | 'high';
|
|
12
12
|
declare const priority: {
|
|
13
|
-
readonly low:
|
|
14
|
-
readonly normal:
|
|
15
|
-
readonly high:
|
|
13
|
+
readonly low: 'low';
|
|
14
|
+
readonly normal: 'normal';
|
|
15
|
+
readonly high: 'high';
|
|
16
16
|
};
|
|
17
|
-
export
|
|
17
|
+
export type Cache = 'immutable' | 'web' | 'cacheOnly';
|
|
18
18
|
declare const cacheControl: {
|
|
19
|
-
readonly immutable:
|
|
20
|
-
readonly web:
|
|
21
|
-
readonly cacheOnly:
|
|
19
|
+
readonly immutable: 'immutable';
|
|
20
|
+
readonly web: 'web';
|
|
21
|
+
readonly cacheOnly: 'cacheOnly';
|
|
22
22
|
};
|
|
23
|
-
export
|
|
23
|
+
export type Source = {
|
|
24
24
|
uri?: string;
|
|
25
25
|
headers?: {
|
|
26
26
|
[key: string]: string;
|
|
@@ -103,4 +103,3 @@ export interface FastImageStaticProperties {
|
|
|
103
103
|
}
|
|
104
104
|
declare const FastImage: React.ComponentType<FastImageProps> & FastImageStaticProperties;
|
|
105
105
|
export default FastImage;
|
|
106
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,207 +1,130 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// from ViewStyle so the types also work with older React Native types, where
|
|
6
|
-
// it's string.
|
|
7
|
-
|
|
1
|
+
import React, { forwardRef, memo } from "react";
|
|
2
|
+
import { Image, NativeModules, Platform, StyleSheet, View, requireNativeComponent } from "react-native";
|
|
3
|
+
//#region src/index.tsx
|
|
8
4
|
const resizeMode = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
contain: "contain",
|
|
6
|
+
cover: "cover",
|
|
7
|
+
stretch: "stretch",
|
|
8
|
+
center: "center"
|
|
13
9
|
};
|
|
14
10
|
const priority = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
low: "low",
|
|
12
|
+
normal: "normal",
|
|
13
|
+
high: "high"
|
|
18
14
|
};
|
|
19
15
|
const cacheControl = {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
immutable: "immutable",
|
|
17
|
+
web: "web",
|
|
18
|
+
cacheOnly: "cacheOnly"
|
|
19
|
+
};
|
|
20
|
+
const resolveDefaultSource = (defaultSource) => {
|
|
21
|
+
if (!defaultSource) return null;
|
|
22
|
+
if (Platform.OS === "android") {
|
|
23
|
+
const resolved = Image.resolveAssetSource(defaultSource);
|
|
24
|
+
if (resolved) return resolved.uri;
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return defaultSource;
|
|
26
28
|
};
|
|
27
|
-
|
|
28
|
-
const resolveDefaultSource = defaultSource => {
|
|
29
|
-
if (!defaultSource) {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (Platform.OS === 'android') {
|
|
34
|
-
// Android receives a URI string, and resolves into a Drawable using RN's methods.
|
|
35
|
-
const resolved = Image.resolveAssetSource(defaultSource);
|
|
36
|
-
|
|
37
|
-
if (resolved) {
|
|
38
|
-
return resolved.uri;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return null;
|
|
42
|
-
} // iOS or other number mapped assets
|
|
43
|
-
// In iOS the number is passed, and bridged automatically into a UIImage
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return defaultSource;
|
|
47
|
-
}; // Finds tintColor in a style prop, where the last style that sets it wins, as
|
|
48
|
-
// with StyleSheet.flatten, but without flattening (which allocates a merged
|
|
49
|
-
// object for an array style on every render).
|
|
50
|
-
|
|
51
|
-
|
|
52
29
|
function tintColorFromStyle(style) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return style && typeof style === 'object' ? style.tintColor : undefined;
|
|
69
|
-
} // A copy of the source without `cache`.
|
|
70
|
-
|
|
71
|
-
|
|
30
|
+
if (Array.isArray(style)) {
|
|
31
|
+
for (let i = style.length - 1; i >= 0; i--) {
|
|
32
|
+
const found = tintColorFromStyle(style[i]);
|
|
33
|
+
if (found !== void 0) return found;
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (typeof style === "number") {
|
|
38
|
+
const flattened = StyleSheet.flatten(style);
|
|
39
|
+
return flattened ? flattened.tintColor : void 0;
|
|
40
|
+
}
|
|
41
|
+
return style && typeof style === "object" ? style.tintColor : void 0;
|
|
42
|
+
}
|
|
72
43
|
function withoutCache(source) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
...rest
|
|
76
|
-
} = source || {};
|
|
77
|
-
return rest;
|
|
44
|
+
const { cache: _cache, ...rest } = source || {};
|
|
45
|
+
return rest;
|
|
78
46
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
ref: forwardedRef
|
|
131
|
-
}), /*#__PURE__*/React.createElement(Image, _extends({}, imageProps, {
|
|
132
|
-
style: [styles.fallbackImage, {
|
|
133
|
-
tintColor: resolvedTintColor
|
|
134
|
-
}],
|
|
135
|
-
source: resolvedSource,
|
|
136
|
-
defaultSource: defaultSource,
|
|
137
|
-
onLoadStart: onLoadStart,
|
|
138
|
-
onProgress: onProgress,
|
|
139
|
-
onLoad: onLoad,
|
|
140
|
-
onError: onError,
|
|
141
|
-
onLoadEnd: onLoadEnd,
|
|
142
|
-
resizeMode: resizeMode
|
|
143
|
-
})), children);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const resolvedSource = Image.resolveAssetSource(source);
|
|
147
|
-
const resolvedDefaultSource = resolveDefaultSource(defaultSource);
|
|
148
|
-
return /*#__PURE__*/React.createElement(View, _extends({
|
|
149
|
-
style: [styles.imageContainer, style]
|
|
150
|
-
}, wrapperProps, {
|
|
151
|
-
ref: forwardedRef
|
|
152
|
-
}), /*#__PURE__*/React.createElement(FastImageView, _extends({}, imageProps, {
|
|
153
|
-
tintColor: resolvedTintColor,
|
|
154
|
-
style: StyleSheet.absoluteFill,
|
|
155
|
-
source: resolvedSource,
|
|
156
|
-
defaultSource: resolvedDefaultSource,
|
|
157
|
-
onFastImageLoadStart: onLoadStart,
|
|
158
|
-
onFastImageProgress: onProgress,
|
|
159
|
-
onFastImageLoad: onLoad,
|
|
160
|
-
onFastImageError: onError,
|
|
161
|
-
onFastImageLoadEnd: onLoadEnd,
|
|
162
|
-
resizeMode: resizeMode
|
|
163
|
-
})), children);
|
|
47
|
+
function FastImageBase({ source, defaultSource, tintColor, onLoadStart, onProgress, onLoad, onError, onLoadEnd, style, fallback, children, resizeMode = "cover", forwardedRef, onLayout, pointerEvents, ...viewProps }) {
|
|
48
|
+
const { onClick, ...props } = viewProps;
|
|
49
|
+
const wrapperProps = {
|
|
50
|
+
onLayout,
|
|
51
|
+
onClick,
|
|
52
|
+
pointerEvents
|
|
53
|
+
};
|
|
54
|
+
const imageProps = {
|
|
55
|
+
...props,
|
|
56
|
+
pointerEvents: pointerEvents === "box-none" ? "none" : void 0
|
|
57
|
+
};
|
|
58
|
+
const resolvedTintColor = tintColor != null ? tintColor : tintColorFromStyle(style);
|
|
59
|
+
if (fallback) {
|
|
60
|
+
const cleanedSource = typeof source === "number" ? source : withoutCache(source);
|
|
61
|
+
const resolvedSource = Image.resolveAssetSource(cleanedSource);
|
|
62
|
+
return /* @__PURE__ */ React.createElement(View, {
|
|
63
|
+
style: [styles.imageContainer, style],
|
|
64
|
+
...wrapperProps,
|
|
65
|
+
ref: forwardedRef
|
|
66
|
+
}, /* @__PURE__ */ React.createElement(Image, {
|
|
67
|
+
...imageProps,
|
|
68
|
+
style: [styles.fallbackImage, { tintColor: resolvedTintColor }],
|
|
69
|
+
source: resolvedSource,
|
|
70
|
+
defaultSource,
|
|
71
|
+
onLoadStart,
|
|
72
|
+
onProgress,
|
|
73
|
+
onLoad,
|
|
74
|
+
onError,
|
|
75
|
+
onLoadEnd,
|
|
76
|
+
resizeMode
|
|
77
|
+
}), children);
|
|
78
|
+
}
|
|
79
|
+
const resolvedSource = Image.resolveAssetSource(source);
|
|
80
|
+
const resolvedDefaultSource = resolveDefaultSource(defaultSource);
|
|
81
|
+
return /* @__PURE__ */ React.createElement(View, {
|
|
82
|
+
style: [styles.imageContainer, style],
|
|
83
|
+
...wrapperProps,
|
|
84
|
+
ref: forwardedRef
|
|
85
|
+
}, /* @__PURE__ */ React.createElement(FastImageView, {
|
|
86
|
+
...imageProps,
|
|
87
|
+
tintColor: resolvedTintColor,
|
|
88
|
+
style: StyleSheet.absoluteFill,
|
|
89
|
+
source: resolvedSource,
|
|
90
|
+
defaultSource: resolvedDefaultSource,
|
|
91
|
+
onFastImageLoadStart: onLoadStart,
|
|
92
|
+
onFastImageProgress: onProgress,
|
|
93
|
+
onFastImageLoad: onLoad,
|
|
94
|
+
onFastImageError: onError,
|
|
95
|
+
onFastImageLoadEnd: onLoadEnd,
|
|
96
|
+
resizeMode
|
|
97
|
+
}), children);
|
|
164
98
|
}
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
FastImageComponent.displayName =
|
|
99
|
+
const FastImageMemo = memo(FastImageBase);
|
|
100
|
+
const FastImageComponent = forwardRef((props, ref) => /* @__PURE__ */ React.createElement(FastImageMemo, {
|
|
101
|
+
forwardedRef: ref,
|
|
102
|
+
...props
|
|
103
|
+
}));
|
|
104
|
+
FastImageComponent.displayName = "FastImage";
|
|
171
105
|
const FastImage = FastImageComponent;
|
|
172
106
|
FastImage.resizeMode = resizeMode;
|
|
173
107
|
FastImage.cacheControl = cacheControl;
|
|
174
108
|
FastImage.priority = priority;
|
|
175
|
-
|
|
176
|
-
FastImage.preload = sources => NativeModules.FastImageView.preload(sources);
|
|
177
|
-
|
|
109
|
+
FastImage.preload = (sources) => NativeModules.FastImageView.preload(sources);
|
|
178
110
|
FastImage.clearMemoryCache = () => NativeModules.FastImageView.clearMemoryCache();
|
|
179
|
-
|
|
180
111
|
FastImage.clearDiskCache = () => NativeModules.FastImageView.clearDiskCache();
|
|
181
|
-
|
|
182
112
|
const styles = StyleSheet.create({
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
},
|
|
192
|
-
imageContainer: {
|
|
193
|
-
overflow: 'hidden'
|
|
194
|
-
}
|
|
195
|
-
}); // Types of requireNativeComponent are not correct.
|
|
196
|
-
|
|
197
|
-
const FastImageView = requireNativeComponent('FastImageView', FastImage, {
|
|
198
|
-
nativeOnly: {
|
|
199
|
-
onFastImageLoadStart: true,
|
|
200
|
-
onFastImageProgress: true,
|
|
201
|
-
onFastImageLoad: true,
|
|
202
|
-
onFastImageError: true,
|
|
203
|
-
onFastImageLoadEnd: true
|
|
204
|
-
}
|
|
113
|
+
fallbackImage: {
|
|
114
|
+
position: "absolute",
|
|
115
|
+
top: 0,
|
|
116
|
+
left: 0,
|
|
117
|
+
width: "100%",
|
|
118
|
+
height: "100%"
|
|
119
|
+
},
|
|
120
|
+
imageContainer: { overflow: "hidden" }
|
|
205
121
|
});
|
|
206
|
-
|
|
207
|
-
|
|
122
|
+
const FastImageView = requireNativeComponent("FastImageView", FastImage, { nativeOnly: {
|
|
123
|
+
onFastImageLoadStart: true,
|
|
124
|
+
onFastImageProgress: true,
|
|
125
|
+
onFastImageLoad: true,
|
|
126
|
+
onFastImageError: true,
|
|
127
|
+
onFastImageLoadEnd: true
|
|
128
|
+
} });
|
|
129
|
+
//#endregion
|
|
130
|
+
export { FastImage as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-fast-image",
|
|
3
|
-
"version": "8.6.
|
|
3
|
+
"version": "8.6.33",
|
|
4
4
|
"description": "🚩 FastImage, performant React Native image component.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cache",
|
|
@@ -23,48 +23,43 @@
|
|
|
23
23
|
"module": "dist/index.js",
|
|
24
24
|
"typings": "dist/index.d.ts",
|
|
25
25
|
"files": [
|
|
26
|
-
"android",
|
|
27
|
-
"
|
|
28
|
-
"ios",
|
|
29
|
-
"
|
|
26
|
+
"android/build.gradle",
|
|
27
|
+
"android/src/main",
|
|
28
|
+
"ios/FastImage",
|
|
29
|
+
"ios/FastImage.xcodeproj/project.pbxproj",
|
|
30
|
+
"ios/FastImage.xcodeproj/xcshareddata",
|
|
30
31
|
"dist",
|
|
31
32
|
"RNFastImage.podspec"
|
|
32
33
|
],
|
|
33
34
|
"scripts": {
|
|
34
|
-
"build": "
|
|
35
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && rolldown -c && cp src/index.js.flow dist/index.js.flow && cp src/index.js.flow dist/index.cjs.js.flow",
|
|
35
36
|
"lint": "oxlint",
|
|
36
|
-
"release": "
|
|
37
|
-
"test": "
|
|
37
|
+
"release": "semantic-release",
|
|
38
|
+
"test": "bun test",
|
|
39
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
38
40
|
"format": "oxfmt",
|
|
39
41
|
"format:check": "oxfmt --check"
|
|
40
42
|
},
|
|
41
43
|
"packageManager": "bun@1.4.2",
|
|
42
|
-
"jest": {
|
|
43
|
-
"modulePathIgnorePatterns": [
|
|
44
|
-
"ReactNativeFastImageExample*",
|
|
45
|
-
"ReactNativeFastImageExampleServer*"
|
|
46
|
-
],
|
|
47
|
-
"preset": "react-native"
|
|
48
|
-
},
|
|
49
44
|
"resolutions": {
|
|
50
|
-
"@
|
|
45
|
+
"@types/react": "^17.0.14"
|
|
51
46
|
},
|
|
52
47
|
"devDependencies": {
|
|
53
|
-
"@
|
|
54
|
-
"@
|
|
55
|
-
"@types/
|
|
48
|
+
"@semantic-release/changelog": "7.0.0",
|
|
49
|
+
"@semantic-release/git": "11.0.1",
|
|
50
|
+
"@types/bun": "^1.4.2",
|
|
56
51
|
"@types/react": "^17.0.14",
|
|
57
52
|
"@types/react-native": "^0.69.5",
|
|
58
53
|
"@types/react-test-renderer": "^17.0.1",
|
|
59
|
-
"dv-scripts": "^1.6.0",
|
|
60
54
|
"maestro-runner": "1.1.27",
|
|
61
|
-
"metro-react-native-babel-preset": "^0.66.1",
|
|
62
55
|
"oxfmt": "0.70.0",
|
|
63
56
|
"oxlint": "1.85.0",
|
|
57
|
+
"pretty-format": "^30.5.1",
|
|
64
58
|
"react": "17.0.2",
|
|
65
|
-
"react-native": "0.64.2",
|
|
66
59
|
"react-test-renderer": "17.0.2",
|
|
67
|
-
"
|
|
60
|
+
"rolldown": "1.2.11",
|
|
61
|
+
"semantic-release": "25.0.9",
|
|
62
|
+
"typescript": "7.0.2"
|
|
68
63
|
},
|
|
69
64
|
"peerDependencies": {
|
|
70
65
|
"react": "^17 || ^18 || ^19",
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.test.d.ts
DELETED
package/dist/index.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.tsx"],"names":[],"mappings":""}
|