@depup/react-native-fast-image 8.6.3-depup.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/LICENSE +21 -0
- package/README.md +25 -0
- package/RNFastImage.podspec +21 -0
- package/android/build.gradle +68 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageCacheControl.java +8 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageGlideModule.java +9 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java +176 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageProgressListener.java +14 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java +60 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageSource.java +112 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewConverter.java +153 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java +183 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewModule.java +89 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewPackage.java +25 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +159 -0
- package/changes.json +5 -0
- package/dist/index.cjs.js +144 -0
- package/dist/index.cjs.js.flow +75 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +137 -0
- package/dist/index.js.flow +75 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/ios/FastImage/FFFastImageSource.h +33 -0
- package/ios/FastImage/FFFastImageSource.m +20 -0
- package/ios/FastImage/FFFastImageView.h +24 -0
- package/ios/FastImage/FFFastImageView.m +244 -0
- package/ios/FastImage/FFFastImageViewManager.h +5 -0
- package/ios/FastImage/FFFastImageViewManager.m +52 -0
- package/ios/FastImage/RCTConvert+FFFastImage.h +9 -0
- package/ios/FastImage/RCTConvert+FFFastImage.m +53 -0
- package/ios/FastImage.xcodeproj/project.pbxproj +479 -0
- package/ios/FastImage.xcodeproj/xcshareddata/xcschemes/FastImage-tvOS.xcscheme +80 -0
- package/package.json +98 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
package com.dylanvann.fastimage;
|
|
2
|
+
|
|
3
|
+
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_ERROR_EVENT;
|
|
4
|
+
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_LOAD_END_EVENT;
|
|
5
|
+
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_LOAD_EVENT;
|
|
6
|
+
|
|
7
|
+
import android.app.Activity;
|
|
8
|
+
import android.content.Context;
|
|
9
|
+
import android.content.ContextWrapper;
|
|
10
|
+
import android.graphics.PorterDuff;
|
|
11
|
+
import android.os.Build;
|
|
12
|
+
|
|
13
|
+
import androidx.annotation.NonNull;
|
|
14
|
+
|
|
15
|
+
import com.bumptech.glide.Glide;
|
|
16
|
+
import com.bumptech.glide.RequestManager;
|
|
17
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
18
|
+
import com.facebook.react.bridge.WritableMap;
|
|
19
|
+
import com.facebook.react.bridge.WritableNativeMap;
|
|
20
|
+
import com.facebook.react.common.MapBuilder;
|
|
21
|
+
import com.facebook.react.uimanager.SimpleViewManager;
|
|
22
|
+
import com.facebook.react.uimanager.ThemedReactContext;
|
|
23
|
+
import com.facebook.react.uimanager.annotations.ReactProp;
|
|
24
|
+
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
|
25
|
+
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
|
|
26
|
+
|
|
27
|
+
import java.util.List;
|
|
28
|
+
import java.util.Map;
|
|
29
|
+
import java.util.WeakHashMap;
|
|
30
|
+
|
|
31
|
+
import javax.annotation.Nullable;
|
|
32
|
+
|
|
33
|
+
class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> implements FastImageProgressListener {
|
|
34
|
+
|
|
35
|
+
static final String REACT_CLASS = "FastImageView";
|
|
36
|
+
static final String REACT_ON_LOAD_START_EVENT = "onFastImageLoadStart";
|
|
37
|
+
static final String REACT_ON_PROGRESS_EVENT = "onFastImageProgress";
|
|
38
|
+
private static final Map<String, List<FastImageViewWithUrl>> VIEWS_FOR_URLS = new WeakHashMap<>();
|
|
39
|
+
|
|
40
|
+
@Nullable
|
|
41
|
+
private RequestManager requestManager = null;
|
|
42
|
+
|
|
43
|
+
@NonNull
|
|
44
|
+
@Override
|
|
45
|
+
public String getName() {
|
|
46
|
+
return REACT_CLASS;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@NonNull
|
|
50
|
+
@Override
|
|
51
|
+
protected FastImageViewWithUrl createViewInstance(@NonNull ThemedReactContext reactContext) {
|
|
52
|
+
if (isValidContextForGlide(reactContext)) {
|
|
53
|
+
requestManager = Glide.with(reactContext);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return new FastImageViewWithUrl(reactContext);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@ReactProp(name = "source")
|
|
60
|
+
public void setSource(FastImageViewWithUrl view, @Nullable ReadableMap source) {
|
|
61
|
+
view.setSource(source);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@ReactProp(name = "defaultSource")
|
|
65
|
+
public void setDefaultSource(FastImageViewWithUrl view, @Nullable String source) {
|
|
66
|
+
view.setDefaultSource(
|
|
67
|
+
ResourceDrawableIdHelper.getInstance()
|
|
68
|
+
.getResourceDrawable(view.getContext(), source));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@ReactProp(name = "tintColor", customType = "Color")
|
|
72
|
+
public void setTintColor(FastImageViewWithUrl view, @Nullable Integer color) {
|
|
73
|
+
if (color == null) {
|
|
74
|
+
view.clearColorFilter();
|
|
75
|
+
} else {
|
|
76
|
+
view.setColorFilter(color, PorterDuff.Mode.SRC_IN);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@ReactProp(name = "resizeMode")
|
|
81
|
+
public void setResizeMode(FastImageViewWithUrl view, String resizeMode) {
|
|
82
|
+
final FastImageViewWithUrl.ScaleType scaleType = FastImageViewConverter.getScaleType(resizeMode);
|
|
83
|
+
view.setScaleType(scaleType);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@Override
|
|
87
|
+
public void onDropViewInstance(@NonNull FastImageViewWithUrl view) {
|
|
88
|
+
// This will cancel existing requests.
|
|
89
|
+
view.clearView(requestManager);
|
|
90
|
+
|
|
91
|
+
if (view.glideUrl != null) {
|
|
92
|
+
final String key = view.glideUrl.toString();
|
|
93
|
+
FastImageOkHttpProgressGlideModule.forget(key);
|
|
94
|
+
List<FastImageViewWithUrl> viewsForKey = VIEWS_FOR_URLS.get(key);
|
|
95
|
+
if (viewsForKey != null) {
|
|
96
|
+
viewsForKey.remove(view);
|
|
97
|
+
if (viewsForKey.size() == 0) VIEWS_FOR_URLS.remove(key);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
super.onDropViewInstance(view);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@Override
|
|
105
|
+
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
|
|
106
|
+
return MapBuilder.<String, Object>builder()
|
|
107
|
+
.put(REACT_ON_LOAD_START_EVENT, MapBuilder.of("registrationName", REACT_ON_LOAD_START_EVENT))
|
|
108
|
+
.put(REACT_ON_PROGRESS_EVENT, MapBuilder.of("registrationName", REACT_ON_PROGRESS_EVENT))
|
|
109
|
+
.put(REACT_ON_LOAD_EVENT, MapBuilder.of("registrationName", REACT_ON_LOAD_EVENT))
|
|
110
|
+
.put(REACT_ON_ERROR_EVENT, MapBuilder.of("registrationName", REACT_ON_ERROR_EVENT))
|
|
111
|
+
.put(REACT_ON_LOAD_END_EVENT, MapBuilder.of("registrationName", REACT_ON_LOAD_END_EVENT))
|
|
112
|
+
.build();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@Override
|
|
116
|
+
public void onProgress(String key, long bytesRead, long expectedLength) {
|
|
117
|
+
List<FastImageViewWithUrl> viewsForKey = VIEWS_FOR_URLS.get(key);
|
|
118
|
+
if (viewsForKey != null) {
|
|
119
|
+
for (FastImageViewWithUrl view : viewsForKey) {
|
|
120
|
+
WritableMap event = new WritableNativeMap();
|
|
121
|
+
event.putInt("loaded", (int) bytesRead);
|
|
122
|
+
event.putInt("total", (int) expectedLength);
|
|
123
|
+
ThemedReactContext context = (ThemedReactContext) view.getContext();
|
|
124
|
+
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
|
|
125
|
+
int viewId = view.getId();
|
|
126
|
+
eventEmitter.receiveEvent(viewId, REACT_ON_PROGRESS_EVENT, event);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@Override
|
|
132
|
+
public float getGranularityPercentage() {
|
|
133
|
+
return 0.5f;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private static boolean isValidContextForGlide(final Context context) {
|
|
137
|
+
Activity activity = getActivityFromContext(context);
|
|
138
|
+
|
|
139
|
+
if (activity == null) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return !isActivityDestroyed(activity);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private static Activity getActivityFromContext(final Context context) {
|
|
147
|
+
if (context instanceof Activity) {
|
|
148
|
+
return (Activity) context;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (context instanceof ThemedReactContext) {
|
|
152
|
+
final Context baseContext = ((ThemedReactContext) context).getBaseContext();
|
|
153
|
+
if (baseContext instanceof Activity) {
|
|
154
|
+
return (Activity) baseContext;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (baseContext instanceof ContextWrapper) {
|
|
158
|
+
final ContextWrapper contextWrapper = (ContextWrapper) baseContext;
|
|
159
|
+
final Context wrapperBaseContext = contextWrapper.getBaseContext();
|
|
160
|
+
if (wrapperBaseContext instanceof Activity) {
|
|
161
|
+
return (Activity) wrapperBaseContext;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private static boolean isActivityDestroyed(Activity activity) {
|
|
170
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
|
171
|
+
return activity.isDestroyed() || activity.isFinishing();
|
|
172
|
+
} else {
|
|
173
|
+
return activity.isFinishing() || activity.isChangingConfigurations();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@Override
|
|
179
|
+
protected void onAfterUpdateTransaction(@NonNull FastImageViewWithUrl view) {
|
|
180
|
+
super.onAfterUpdateTransaction(view);
|
|
181
|
+
view.onAfterUpdate(this, requestManager, VIEWS_FOR_URLS);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
package com.dylanvann.fastimage;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
|
|
5
|
+
import androidx.annotation.NonNull;
|
|
6
|
+
|
|
7
|
+
import com.bumptech.glide.Glide;
|
|
8
|
+
import com.bumptech.glide.load.model.GlideUrl;
|
|
9
|
+
import com.facebook.react.bridge.Promise;
|
|
10
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
11
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
12
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
13
|
+
import com.facebook.react.bridge.ReadableArray;
|
|
14
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
15
|
+
import com.facebook.react.views.imagehelper.ImageSource;
|
|
16
|
+
|
|
17
|
+
class FastImageViewModule extends ReactContextBaseJavaModule {
|
|
18
|
+
|
|
19
|
+
private static final String REACT_CLASS = "FastImageView";
|
|
20
|
+
|
|
21
|
+
FastImageViewModule(ReactApplicationContext reactContext) {
|
|
22
|
+
super(reactContext);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@NonNull
|
|
26
|
+
@Override
|
|
27
|
+
public String getName() {
|
|
28
|
+
return REACT_CLASS;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@ReactMethod
|
|
32
|
+
public void preload(final ReadableArray sources) {
|
|
33
|
+
final Activity activity = getCurrentActivity();
|
|
34
|
+
if (activity == null) return;
|
|
35
|
+
activity.runOnUiThread(new Runnable() {
|
|
36
|
+
@Override
|
|
37
|
+
public void run() {
|
|
38
|
+
for (int i = 0; i < sources.size(); i++) {
|
|
39
|
+
final ReadableMap source = sources.getMap(i);
|
|
40
|
+
final FastImageSource imageSource = FastImageViewConverter.getImageSource(activity, source);
|
|
41
|
+
|
|
42
|
+
Glide
|
|
43
|
+
.with(activity.getApplicationContext())
|
|
44
|
+
// This will make this work for remote and local images. e.g.
|
|
45
|
+
// - file:///
|
|
46
|
+
// - content://
|
|
47
|
+
// - res:/
|
|
48
|
+
// - android.resource://
|
|
49
|
+
// - data:image/png;base64
|
|
50
|
+
.load(
|
|
51
|
+
imageSource.isBase64Resource() ? imageSource.getSource() :
|
|
52
|
+
imageSource.isResource() ? imageSource.getUri() : imageSource.getGlideUrl()
|
|
53
|
+
)
|
|
54
|
+
.apply(FastImageViewConverter.getOptions(activity, imageSource, source))
|
|
55
|
+
.preload();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@ReactMethod
|
|
62
|
+
public void clearMemoryCache(final Promise promise) {
|
|
63
|
+
final Activity activity = getCurrentActivity();
|
|
64
|
+
if (activity == null) {
|
|
65
|
+
promise.resolve(null);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
activity.runOnUiThread(new Runnable() {
|
|
70
|
+
@Override
|
|
71
|
+
public void run() {
|
|
72
|
+
Glide.get(activity.getApplicationContext()).clearMemory();
|
|
73
|
+
promise.resolve(null);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@ReactMethod
|
|
79
|
+
public void clearDiskCache(Promise promise) {
|
|
80
|
+
final Activity activity = getCurrentActivity();
|
|
81
|
+
if (activity == null) {
|
|
82
|
+
promise.resolve(null);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Glide.get(activity.getApplicationContext()).clearDiskCache();
|
|
87
|
+
promise.resolve(null);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package com.dylanvann.fastimage;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.ReactPackage;
|
|
6
|
+
import com.facebook.react.bridge.NativeModule;
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
8
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
9
|
+
|
|
10
|
+
import java.util.Collections;
|
|
11
|
+
import java.util.List;
|
|
12
|
+
|
|
13
|
+
public class FastImageViewPackage implements ReactPackage {
|
|
14
|
+
@NonNull
|
|
15
|
+
@Override
|
|
16
|
+
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
|
|
17
|
+
return Collections.<NativeModule>singletonList(new FastImageViewModule(reactContext));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@NonNull
|
|
21
|
+
@Override
|
|
22
|
+
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
|
|
23
|
+
return Collections.<ViewManager>singletonList(new FastImageViewManager());
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
package com.dylanvann.fastimage;
|
|
2
|
+
|
|
3
|
+
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_ERROR_EVENT;
|
|
4
|
+
|
|
5
|
+
import android.annotation.SuppressLint;
|
|
6
|
+
import android.content.Context;
|
|
7
|
+
import android.graphics.drawable.Drawable;
|
|
8
|
+
|
|
9
|
+
import androidx.annotation.Nullable;
|
|
10
|
+
import androidx.appcompat.widget.AppCompatImageView;
|
|
11
|
+
|
|
12
|
+
import com.bumptech.glide.RequestBuilder;
|
|
13
|
+
import com.bumptech.glide.RequestManager;
|
|
14
|
+
import com.bumptech.glide.load.model.GlideUrl;
|
|
15
|
+
import com.bumptech.glide.request.Request;
|
|
16
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
17
|
+
import com.facebook.react.bridge.WritableMap;
|
|
18
|
+
import com.facebook.react.bridge.WritableNativeMap;
|
|
19
|
+
import com.facebook.react.uimanager.ThemedReactContext;
|
|
20
|
+
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
|
21
|
+
|
|
22
|
+
import java.util.ArrayList;
|
|
23
|
+
import java.util.Collections;
|
|
24
|
+
import java.util.List;
|
|
25
|
+
import java.util.Map;
|
|
26
|
+
|
|
27
|
+
import javax.annotation.Nonnull;
|
|
28
|
+
|
|
29
|
+
class FastImageViewWithUrl extends AppCompatImageView {
|
|
30
|
+
private boolean mNeedsReload = false;
|
|
31
|
+
private ReadableMap mSource = null;
|
|
32
|
+
private Drawable mDefaultSource = null;
|
|
33
|
+
|
|
34
|
+
public GlideUrl glideUrl;
|
|
35
|
+
|
|
36
|
+
public FastImageViewWithUrl(Context context) {
|
|
37
|
+
super(context);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public void setSource(@Nullable ReadableMap source) {
|
|
41
|
+
mNeedsReload = true;
|
|
42
|
+
mSource = source;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public void setDefaultSource(@Nullable Drawable source) {
|
|
46
|
+
mNeedsReload = true;
|
|
47
|
+
mDefaultSource = source;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private boolean isNullOrEmpty(final String url) {
|
|
51
|
+
return url == null || url.trim().isEmpty();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@SuppressLint("CheckResult")
|
|
55
|
+
public void onAfterUpdate(
|
|
56
|
+
@Nonnull FastImageViewManager manager,
|
|
57
|
+
@Nullable RequestManager requestManager,
|
|
58
|
+
@Nonnull Map<String, List<FastImageViewWithUrl>> viewsForUrlsMap) {
|
|
59
|
+
if (!mNeedsReload)
|
|
60
|
+
return;
|
|
61
|
+
|
|
62
|
+
if ((mSource == null ||
|
|
63
|
+
!mSource.hasKey("uri") ||
|
|
64
|
+
isNullOrEmpty(mSource.getString("uri"))) &&
|
|
65
|
+
mDefaultSource == null) {
|
|
66
|
+
|
|
67
|
+
// Cancel existing requests.
|
|
68
|
+
clearView(requestManager);
|
|
69
|
+
|
|
70
|
+
if (glideUrl != null) {
|
|
71
|
+
FastImageOkHttpProgressGlideModule.forget(glideUrl.toStringUrl());
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Clear the image.
|
|
75
|
+
setImageDrawable(null);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
//final GlideUrl glideUrl = FastImageViewConverter.getGlideUrl(view.getContext(), mSource);
|
|
80
|
+
final FastImageSource imageSource = FastImageViewConverter.getImageSource(getContext(), mSource);
|
|
81
|
+
|
|
82
|
+
if (imageSource != null && imageSource.getUri().toString().length() == 0) {
|
|
83
|
+
ThemedReactContext context = (ThemedReactContext) getContext();
|
|
84
|
+
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
|
|
85
|
+
int viewId = getId();
|
|
86
|
+
WritableMap event = new WritableNativeMap();
|
|
87
|
+
event.putString("message", "Invalid source prop:" + mSource);
|
|
88
|
+
eventEmitter.receiveEvent(viewId, REACT_ON_ERROR_EVENT, event);
|
|
89
|
+
|
|
90
|
+
// Cancel existing requests.
|
|
91
|
+
clearView(requestManager);
|
|
92
|
+
|
|
93
|
+
if (glideUrl != null) {
|
|
94
|
+
FastImageOkHttpProgressGlideModule.forget(glideUrl.toStringUrl());
|
|
95
|
+
}
|
|
96
|
+
// Clear the image.
|
|
97
|
+
setImageDrawable(null);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// `imageSource` may be null and we still continue, if `defaultSource` is not null
|
|
102
|
+
final GlideUrl glideUrl = imageSource == null ? null : imageSource.getGlideUrl();
|
|
103
|
+
|
|
104
|
+
// Cancel existing request.
|
|
105
|
+
this.glideUrl = glideUrl;
|
|
106
|
+
clearView(requestManager);
|
|
107
|
+
|
|
108
|
+
String key = glideUrl == null ? null : glideUrl.toStringUrl();
|
|
109
|
+
|
|
110
|
+
if (glideUrl != null) {
|
|
111
|
+
FastImageOkHttpProgressGlideModule.expect(key, manager);
|
|
112
|
+
List<FastImageViewWithUrl> viewsForKey = viewsForUrlsMap.get(key);
|
|
113
|
+
if (viewsForKey != null && !viewsForKey.contains(this)) {
|
|
114
|
+
viewsForKey.add(this);
|
|
115
|
+
} else if (viewsForKey == null) {
|
|
116
|
+
List<FastImageViewWithUrl> newViewsForKeys = new ArrayList<>(Collections.singletonList(this));
|
|
117
|
+
viewsForUrlsMap.put(key, newViewsForKeys);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
ThemedReactContext context = (ThemedReactContext) getContext();
|
|
122
|
+
if (imageSource != null) {
|
|
123
|
+
// This is an orphan even without a load/loadend when only loading a placeholder
|
|
124
|
+
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
|
|
125
|
+
int viewId = this.getId();
|
|
126
|
+
|
|
127
|
+
eventEmitter.receiveEvent(viewId,
|
|
128
|
+
FastImageViewManager.REACT_ON_LOAD_START_EVENT,
|
|
129
|
+
new WritableNativeMap());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (requestManager != null) {
|
|
133
|
+
RequestBuilder<Drawable> builder =
|
|
134
|
+
requestManager
|
|
135
|
+
// This will make this work for remote and local images. e.g.
|
|
136
|
+
// - file:///
|
|
137
|
+
// - content://
|
|
138
|
+
// - res:/
|
|
139
|
+
// - android.resource://
|
|
140
|
+
// - data:image/png;base64
|
|
141
|
+
.load(imageSource == null ? null : imageSource.getSourceForLoad())
|
|
142
|
+
.apply(FastImageViewConverter
|
|
143
|
+
.getOptions(context, imageSource, mSource)
|
|
144
|
+
.placeholder(mDefaultSource) // show until loaded
|
|
145
|
+
.fallback(mDefaultSource)); // null will not be treated as error
|
|
146
|
+
|
|
147
|
+
if (key != null)
|
|
148
|
+
builder.listener(new FastImageRequestListener(key));
|
|
149
|
+
|
|
150
|
+
builder.into(this);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public void clearView(@Nullable RequestManager requestManager) {
|
|
155
|
+
if (requestManager != null && getTag() != null && getTag() instanceof Request) {
|
|
156
|
+
requestManager.clear(this);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
package/changes.json
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _extends = require('@babel/runtime/helpers/extends');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var reactNative = require('react-native');
|
|
6
|
+
|
|
7
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
8
|
+
|
|
9
|
+
var _extends__default = /*#__PURE__*/_interopDefaultLegacy(_extends);
|
|
10
|
+
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
11
|
+
|
|
12
|
+
const resizeMode = {
|
|
13
|
+
contain: 'contain',
|
|
14
|
+
cover: 'cover',
|
|
15
|
+
stretch: 'stretch',
|
|
16
|
+
center: 'center'
|
|
17
|
+
};
|
|
18
|
+
const priority = {
|
|
19
|
+
low: 'low',
|
|
20
|
+
normal: 'normal',
|
|
21
|
+
high: 'high'
|
|
22
|
+
};
|
|
23
|
+
const cacheControl = {
|
|
24
|
+
// Ignore headers, use uri as cache key, fetch only if not in cache.
|
|
25
|
+
immutable: 'immutable',
|
|
26
|
+
// Respect http headers, no aggressive caching.
|
|
27
|
+
web: 'web',
|
|
28
|
+
// Only load from cache.
|
|
29
|
+
cacheOnly: 'cacheOnly'
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const resolveDefaultSource = defaultSource => {
|
|
33
|
+
if (!defaultSource) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (reactNative.Platform.OS === 'android') {
|
|
38
|
+
// Android receives a URI string, and resolves into a Drawable using RN's methods.
|
|
39
|
+
const resolved = reactNative.Image.resolveAssetSource(defaultSource);
|
|
40
|
+
|
|
41
|
+
if (resolved) {
|
|
42
|
+
return resolved.uri;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return null;
|
|
46
|
+
} // iOS or other number mapped assets
|
|
47
|
+
// In iOS the number is passed, and bridged automatically into a UIImage
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
return defaultSource;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function FastImageBase({
|
|
54
|
+
source,
|
|
55
|
+
defaultSource,
|
|
56
|
+
tintColor,
|
|
57
|
+
onLoadStart,
|
|
58
|
+
onProgress,
|
|
59
|
+
onLoad,
|
|
60
|
+
onError,
|
|
61
|
+
onLoadEnd,
|
|
62
|
+
style,
|
|
63
|
+
fallback,
|
|
64
|
+
children,
|
|
65
|
+
// eslint-disable-next-line no-shadow
|
|
66
|
+
resizeMode = 'cover',
|
|
67
|
+
forwardedRef,
|
|
68
|
+
...props
|
|
69
|
+
}) {
|
|
70
|
+
if (fallback) {
|
|
71
|
+
const cleanedSource = { ...source
|
|
72
|
+
};
|
|
73
|
+
delete cleanedSource.cache;
|
|
74
|
+
const resolvedSource = reactNative.Image.resolveAssetSource(cleanedSource);
|
|
75
|
+
return /*#__PURE__*/React__default['default'].createElement(reactNative.View, {
|
|
76
|
+
style: [styles.imageContainer, style],
|
|
77
|
+
ref: forwardedRef
|
|
78
|
+
}, /*#__PURE__*/React__default['default'].createElement(reactNative.Image, _extends__default['default']({}, props, {
|
|
79
|
+
style: [reactNative.StyleSheet.absoluteFill, {
|
|
80
|
+
tintColor
|
|
81
|
+
}],
|
|
82
|
+
source: resolvedSource,
|
|
83
|
+
defaultSource: defaultSource,
|
|
84
|
+
onLoadStart: onLoadStart,
|
|
85
|
+
onProgress: onProgress,
|
|
86
|
+
onLoad: onLoad,
|
|
87
|
+
onError: onError,
|
|
88
|
+
onLoadEnd: onLoadEnd,
|
|
89
|
+
resizeMode: resizeMode
|
|
90
|
+
})), children);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const resolvedSource = reactNative.Image.resolveAssetSource(source);
|
|
94
|
+
const resolvedDefaultSource = resolveDefaultSource(defaultSource);
|
|
95
|
+
return /*#__PURE__*/React__default['default'].createElement(reactNative.View, {
|
|
96
|
+
style: [styles.imageContainer, style],
|
|
97
|
+
ref: forwardedRef
|
|
98
|
+
}, /*#__PURE__*/React__default['default'].createElement(FastImageView, _extends__default['default']({}, props, {
|
|
99
|
+
tintColor: tintColor,
|
|
100
|
+
style: reactNative.StyleSheet.absoluteFill,
|
|
101
|
+
source: resolvedSource,
|
|
102
|
+
defaultSource: resolvedDefaultSource,
|
|
103
|
+
onFastImageLoadStart: onLoadStart,
|
|
104
|
+
onFastImageProgress: onProgress,
|
|
105
|
+
onFastImageLoad: onLoad,
|
|
106
|
+
onFastImageError: onError,
|
|
107
|
+
onFastImageLoadEnd: onLoadEnd,
|
|
108
|
+
resizeMode: resizeMode
|
|
109
|
+
})), children);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const FastImageMemo = /*#__PURE__*/React.memo(FastImageBase);
|
|
113
|
+
const FastImageComponent = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React__default['default'].createElement(FastImageMemo, _extends__default['default']({
|
|
114
|
+
forwardedRef: ref
|
|
115
|
+
}, props)));
|
|
116
|
+
FastImageComponent.displayName = 'FastImage';
|
|
117
|
+
const FastImage = FastImageComponent;
|
|
118
|
+
FastImage.resizeMode = resizeMode;
|
|
119
|
+
FastImage.cacheControl = cacheControl;
|
|
120
|
+
FastImage.priority = priority;
|
|
121
|
+
|
|
122
|
+
FastImage.preload = sources => reactNative.NativeModules.FastImageView.preload(sources);
|
|
123
|
+
|
|
124
|
+
FastImage.clearMemoryCache = () => reactNative.NativeModules.FastImageView.clearMemoryCache();
|
|
125
|
+
|
|
126
|
+
FastImage.clearDiskCache = () => reactNative.NativeModules.FastImageView.clearDiskCache();
|
|
127
|
+
|
|
128
|
+
const styles = reactNative.StyleSheet.create({
|
|
129
|
+
imageContainer: {
|
|
130
|
+
overflow: 'hidden'
|
|
131
|
+
}
|
|
132
|
+
}); // Types of requireNativeComponent are not correct.
|
|
133
|
+
|
|
134
|
+
const FastImageView = reactNative.requireNativeComponent('FastImageView', FastImage, {
|
|
135
|
+
nativeOnly: {
|
|
136
|
+
onFastImageLoadStart: true,
|
|
137
|
+
onFastImageProgress: true,
|
|
138
|
+
onFastImageLoad: true,
|
|
139
|
+
onFastImageError: true,
|
|
140
|
+
onFastImageLoadEnd: true
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
module.exports = FastImage;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import type { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes'
|
|
4
|
+
import type { SyntheticEvent } from 'react-native/Libraries/Types/CoreEventTypes'
|
|
5
|
+
|
|
6
|
+
export type OnLoadEvent = SyntheticEvent<
|
|
7
|
+
$ReadOnly<{
|
|
8
|
+
width: number,
|
|
9
|
+
height: number,
|
|
10
|
+
}>,
|
|
11
|
+
>
|
|
12
|
+
|
|
13
|
+
export type OnProgressEvent = SyntheticEvent<
|
|
14
|
+
$ReadOnly<{|
|
|
15
|
+
loaded: number,
|
|
16
|
+
total: number,
|
|
17
|
+
|}>,
|
|
18
|
+
>
|
|
19
|
+
|
|
20
|
+
export type ResizeMode = $ReadOnly<{|
|
|
21
|
+
contain: 'contain',
|
|
22
|
+
cover: 'cover',
|
|
23
|
+
stretch: 'stretch',
|
|
24
|
+
center: 'center',
|
|
25
|
+
|}>
|
|
26
|
+
|
|
27
|
+
export type Priority = $ReadOnly<{|
|
|
28
|
+
low: 'low',
|
|
29
|
+
normal: 'normal',
|
|
30
|
+
high: 'high',
|
|
31
|
+
|}>
|
|
32
|
+
|
|
33
|
+
export type CacheControl = $ReadOnly<{|
|
|
34
|
+
immutable: 'immutable',
|
|
35
|
+
web: 'web',
|
|
36
|
+
cacheOnly: 'cacheOnly',
|
|
37
|
+
|}>
|
|
38
|
+
|
|
39
|
+
export type ResizeModes = $Values<ResizeMode>
|
|
40
|
+
export type Priorities = $Values<Priority>
|
|
41
|
+
export type CacheControls = $Values<CacheControl>
|
|
42
|
+
|
|
43
|
+
export type PreloadFn = (sources: Array<FastImageSource>) => void
|
|
44
|
+
export type FastImageSource = {
|
|
45
|
+
uri?: string,
|
|
46
|
+
headers?: Object,
|
|
47
|
+
priority?: Priorities,
|
|
48
|
+
cache?: CacheControls,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type FastImageProps = $ReadOnly<{|
|
|
52
|
+
...ViewProps,
|
|
53
|
+
onError?: ?() => void,
|
|
54
|
+
onLoad?: ?(event: OnLoadEvent) => void,
|
|
55
|
+
onLoadEnd?: ?() => void,
|
|
56
|
+
onLoadStart?: ?() => void,
|
|
57
|
+
onProgress?: ?(event: OnProgressEvent) => void,
|
|
58
|
+
|
|
59
|
+
source?: ?(FastImageSource | number),
|
|
60
|
+
defaultSource?: ?number,
|
|
61
|
+
|
|
62
|
+
tintColor?: number | string,
|
|
63
|
+
resizeMode?: ?ResizeModes,
|
|
64
|
+
fallback?: ?boolean,
|
|
65
|
+
testID?: ?string,
|
|
66
|
+
|}>
|
|
67
|
+
|
|
68
|
+
declare export default class FastImage extends React$Component<FastImageProps> {
|
|
69
|
+
static resizeMode: ResizeMode;
|
|
70
|
+
static priority: Priority;
|
|
71
|
+
static cacheControl: CacheControl;
|
|
72
|
+
static preload: PreloadFn;
|
|
73
|
+
static clearMemoryCache: () => Promise<void>;
|
|
74
|
+
static clearDiskCache: () => Promise<void>;
|
|
75
|
+
}
|