react-native-nitro-image-pipeline 0.3.3 → 0.3.5
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 +2 -2
- package/android/src/main/java/com/margelo/nitro/nitroimagepipeline/HybridNitroImagePipeline.kt +59 -34
- package/ios/HybridNitroImagePipeline.swift +76 -16
- package/lib/commonjs/index.js +32 -11
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +33 -12
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/nitro-image-toolkit.nitro.d.ts +3 -4
- package/lib/typescript/src/specs/nitro-image-toolkit.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JCacheOption.hpp +1 -1
- package/nitrogen/generated/android/c++/JHybridNitroImagePipelineSpec.cpp +18 -7
- package/nitrogen/generated/android/c++/JHybridNitroImagePipelineSpec.hpp +1 -1
- package/nitrogen/generated/android/c++/JOptions.hpp +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroimagepipeline/HybridNitroImagePipelineSpec.kt +3 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroimagepipeline/Options.kt +17 -0
- package/nitrogen/generated/ios/NitroImagePipeline-Swift-Cxx-Bridge.hpp +0 -9
- package/nitrogen/generated/ios/c++/HybridNitroImagePipelineSpecSwift.hpp +3 -1
- package/nitrogen/generated/ios/swift/HybridNitroImagePipelineSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridNitroImagePipelineSpec_cxx.swift +12 -4
- package/nitrogen/generated/shared/c++/HybridNitroImagePipelineSpec.hpp +1 -1
- package/package.json +10 -10
- package/src/index.ts +32 -5
- package/src/specs/nitro-image-toolkit.nitro.ts +7 -3
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ await NitroImagePipeline.preLoadImages([
|
|
|
85
85
|
const blurred = await NitroImagePipeline.gaussianBlur(image, 10);
|
|
86
86
|
|
|
87
87
|
// Clear the image cache
|
|
88
|
-
NitroImagePipeline.clearCache();
|
|
88
|
+
await NitroImagePipeline.clearCache();
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
## API Reference
|
|
@@ -114,7 +114,7 @@ Applies a Gaussian blur to an existing `Image` object. Returns `Promise<Image>`.
|
|
|
114
114
|
|
|
115
115
|
### `clearCache()`
|
|
116
116
|
|
|
117
|
-
Removes all cached images from memory and disk.
|
|
117
|
+
Removes all cached images from memory and disk. Returns `Promise<void>` that resolves once both caches are cleared.
|
|
118
118
|
|
|
119
119
|
## Credits
|
|
120
120
|
|
package/android/src/main/java/com/margelo/nitro/nitroimagepipeline/HybridNitroImagePipeline.kt
CHANGED
|
@@ -22,6 +22,11 @@ import com.margelo.nitro.core.Promise
|
|
|
22
22
|
import com.margelo.nitro.image.HybridImage
|
|
23
23
|
import com.margelo.nitro.image.HybridImageSpec
|
|
24
24
|
import com.margelo.nitro.nitroimagepipeline.transform.BlurTransformation
|
|
25
|
+
import kotlinx.coroutines.Dispatchers
|
|
26
|
+
import kotlinx.coroutines.async
|
|
27
|
+
import kotlinx.coroutines.awaitAll
|
|
28
|
+
import kotlinx.coroutines.coroutineScope
|
|
29
|
+
import kotlinx.coroutines.withContext
|
|
25
30
|
import okhttp3.OkHttpClient
|
|
26
31
|
import okio.Path.Companion.toOkioPath
|
|
27
32
|
import org.chromium.net.CronetEngine
|
|
@@ -30,29 +35,8 @@ class HybridNitroImagePipeline : HybridNitroImagePipelineSpec() {
|
|
|
30
35
|
private val context
|
|
31
36
|
get() = NitroModules.applicationContext as Context
|
|
32
37
|
|
|
33
|
-
private val
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
val cronetEngine = CronetEngine.Builder(context).build()
|
|
37
|
-
builder.addInterceptor(CronetInterceptor.newBuilder(cronetEngine).build())
|
|
38
|
-
} catch (_: Exception) {
|
|
39
|
-
// Cronet unavailable (no GMS or unsupported device) — use plain OkHttp
|
|
40
|
-
}
|
|
41
|
-
builder.build()
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
private val imageLoader: ImageLoader by lazy {
|
|
45
|
-
ImageLoader.Builder(context)
|
|
46
|
-
.components { add(OkHttpNetworkFetcherFactory(callFactory = okHttpClient)) }
|
|
47
|
-
.memoryCache { MemoryCache.Builder().maxSizePercent(context, 0.25).build() }
|
|
48
|
-
.diskCache {
|
|
49
|
-
DiskCache.Builder()
|
|
50
|
-
.directory(context.cacheDir.resolve("nitro_image_cache").toOkioPath())
|
|
51
|
-
.maxSizeBytes(256L * 1024 * 1024)
|
|
52
|
-
.build()
|
|
53
|
-
}
|
|
54
|
-
.build()
|
|
55
|
-
}
|
|
38
|
+
private val imageLoader: ImageLoader
|
|
39
|
+
get() = getOrCreateImageLoader(context)
|
|
56
40
|
|
|
57
41
|
override fun loadImage(url: String, options: Options?): Promise<HybridImageSpec> = Promise.async {
|
|
58
42
|
val blur = options?.blur?.toFloat() ?: 0f
|
|
@@ -61,11 +45,6 @@ class HybridNitroImagePipeline : HybridNitroImagePipelineSpec() {
|
|
|
61
45
|
if (blur > 0f) add(BlurTransformation(context, blur))
|
|
62
46
|
if (cornerRadius > 0f) add(RoundedCornersTransformation(cornerRadius))
|
|
63
47
|
}
|
|
64
|
-
val memoryCacheKey = buildString {
|
|
65
|
-
append(url)
|
|
66
|
-
if (blur > 0f) append("_blur$blur")
|
|
67
|
-
if (cornerRadius > 0f) append("_corner$cornerRadius")
|
|
68
|
-
}
|
|
69
48
|
val request =
|
|
70
49
|
ImageRequest.Builder(context)
|
|
71
50
|
.data(url)
|
|
@@ -87,7 +66,6 @@ class HybridNitroImagePipeline : HybridNitroImagePipelineSpec() {
|
|
|
87
66
|
}
|
|
88
67
|
}
|
|
89
68
|
.allowHardware(true)
|
|
90
|
-
.memoryCacheKey(memoryCacheKey)
|
|
91
69
|
.transformations(transformations)
|
|
92
70
|
.build()
|
|
93
71
|
|
|
@@ -111,10 +89,17 @@ class HybridNitroImagePipeline : HybridNitroImagePipelineSpec() {
|
|
|
111
89
|
}
|
|
112
90
|
|
|
113
91
|
override fun preLoadImages(urls: Array<String>): Promise<Unit> = Promise.async {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
92
|
+
coroutineScope {
|
|
93
|
+
urls
|
|
94
|
+
.map { url ->
|
|
95
|
+
async {
|
|
96
|
+
val request = ImageRequest.Builder(context).data(url).build()
|
|
97
|
+
imageLoader.execute(request)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
.awaitAll()
|
|
117
101
|
}
|
|
102
|
+
Unit
|
|
118
103
|
}
|
|
119
104
|
|
|
120
105
|
override fun gaussianBlur(image: HybridImageSpec, radius: Double): Promise<HybridImageSpec> =
|
|
@@ -126,8 +111,48 @@ class HybridNitroImagePipeline : HybridNitroImagePipelineSpec() {
|
|
|
126
111
|
HybridImage(blurred)
|
|
127
112
|
}
|
|
128
113
|
|
|
129
|
-
override fun clearCache() {
|
|
114
|
+
override fun clearCache(): Promise<Unit> = Promise.async {
|
|
130
115
|
imageLoader.memoryCache?.clear()
|
|
131
|
-
|
|
116
|
+
// DiskCache.clear() does file I/O; keep it off the JS thread but await
|
|
117
|
+
// completion so callers can rely on the cache being empty, and so an
|
|
118
|
+
// IOException rejects the promise instead of crashing the process.
|
|
119
|
+
withContext(Dispatchers.IO) { imageLoader.diskCache?.clear() }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
companion object {
|
|
123
|
+
// Coil requires a single DiskCache instance per directory, so the loader
|
|
124
|
+
// must be shared across all HybridNitroImagePipeline instances.
|
|
125
|
+
@Volatile private var sharedImageLoader: ImageLoader? = null
|
|
126
|
+
|
|
127
|
+
private fun getOrCreateImageLoader(context: Context): ImageLoader =
|
|
128
|
+
sharedImageLoader
|
|
129
|
+
?: synchronized(this) {
|
|
130
|
+
sharedImageLoader ?: createImageLoader(context).also { sharedImageLoader = it }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private fun createImageLoader(context: Context): ImageLoader {
|
|
134
|
+
val okHttpClient =
|
|
135
|
+
OkHttpClient.Builder()
|
|
136
|
+
.apply {
|
|
137
|
+
try {
|
|
138
|
+
val cronetEngine = CronetEngine.Builder(context).build()
|
|
139
|
+
addInterceptor(CronetInterceptor.newBuilder(cronetEngine).build())
|
|
140
|
+
} catch (_: Exception) {
|
|
141
|
+
// Cronet unavailable (no GMS or unsupported device) — use plain OkHttp
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
.build()
|
|
145
|
+
|
|
146
|
+
return ImageLoader.Builder(context)
|
|
147
|
+
.components { add(OkHttpNetworkFetcherFactory(callFactory = okHttpClient)) }
|
|
148
|
+
.memoryCache { MemoryCache.Builder().maxSizePercent(context, 0.25).build() }
|
|
149
|
+
.diskCache {
|
|
150
|
+
DiskCache.Builder()
|
|
151
|
+
.directory(context.cacheDir.resolve("nitro_image_cache").toOkioPath())
|
|
152
|
+
.maxSizeBytes(256L * 1024 * 1024)
|
|
153
|
+
.build()
|
|
154
|
+
}
|
|
155
|
+
.build()
|
|
156
|
+
}
|
|
132
157
|
}
|
|
133
158
|
}
|
|
@@ -16,9 +16,44 @@ import CoreImage
|
|
|
16
16
|
private class HybridImage: HybridImageSpec, NativeImage {
|
|
17
17
|
let uiImage: UIImage
|
|
18
18
|
|
|
19
|
+
// PNG encoding is expensive; encode once and reuse for both
|
|
20
|
+
// toArrayBuffer() and toBase64(). Guarded by a lock (lazy var is not
|
|
21
|
+
// thread-safe) and dropped on memory pressure so a JS-retained image
|
|
22
|
+
// doesn't pin its encoding forever.
|
|
23
|
+
private let pngLock = NSLock()
|
|
24
|
+
private var cachedPngData: Data?
|
|
25
|
+
private var memoryWarningObserver: (any NSObjectProtocol)?
|
|
26
|
+
|
|
19
27
|
init(uiImage: UIImage) {
|
|
20
28
|
self.uiImage = uiImage
|
|
21
29
|
super.init()
|
|
30
|
+
memoryWarningObserver = NotificationCenter.default.addObserver(
|
|
31
|
+
forName: UIApplication.didReceiveMemoryWarningNotification,
|
|
32
|
+
object: nil,
|
|
33
|
+
queue: nil
|
|
34
|
+
) { [weak self] _ in
|
|
35
|
+
guard let self else { return }
|
|
36
|
+
self.pngLock.lock()
|
|
37
|
+
self.cachedPngData = nil
|
|
38
|
+
self.pngLock.unlock()
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
deinit {
|
|
43
|
+
if let memoryWarningObserver {
|
|
44
|
+
NotificationCenter.default.removeObserver(memoryWarningObserver)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private func pngData() -> Data? {
|
|
49
|
+
pngLock.lock()
|
|
50
|
+
defer { pngLock.unlock() }
|
|
51
|
+
if let cachedPngData {
|
|
52
|
+
return cachedPngData
|
|
53
|
+
}
|
|
54
|
+
let data = uiImage.pngData()
|
|
55
|
+
cachedPngData = data
|
|
56
|
+
return data
|
|
22
57
|
}
|
|
23
58
|
|
|
24
59
|
var width: Double {
|
|
@@ -30,14 +65,14 @@ private class HybridImage: HybridImageSpec, NativeImage {
|
|
|
30
65
|
}
|
|
31
66
|
|
|
32
67
|
func toArrayBuffer() throws -> ArrayBuffer {
|
|
33
|
-
guard let data =
|
|
68
|
+
guard let data = pngData() else {
|
|
34
69
|
throw RuntimeError.error(withMessage: "Failed to encode image to PNG")
|
|
35
70
|
}
|
|
36
71
|
return try ArrayBuffer.copy(data: data)
|
|
37
72
|
}
|
|
38
73
|
|
|
39
74
|
func toBase64() throws -> String {
|
|
40
|
-
guard let data =
|
|
75
|
+
guard let data = pngData() else {
|
|
41
76
|
throw RuntimeError.error(withMessage: "Failed to encode image")
|
|
42
77
|
}
|
|
43
78
|
|
|
@@ -50,14 +85,34 @@ private class HybridImage: HybridImageSpec, NativeImage {
|
|
|
50
85
|
}
|
|
51
86
|
|
|
52
87
|
class HybridNitroImagePipeline: HybridNitroImagePipelineSpec {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
88
|
+
// One shared pipeline (instead of mutating ImagePipeline.shared) so the
|
|
89
|
+
// prefetcher and loadImage read/write the exact same caches, other Nuke
|
|
90
|
+
// users in the host app are left untouched, and repeated instantiation
|
|
91
|
+
// (e.g. Metro reloads) never puts two DataCache instances on the same
|
|
92
|
+
// directory.
|
|
93
|
+
private static let sharedPipeline: ImagePipeline = {
|
|
94
|
+
var configuration = ImagePipeline.Configuration.withDataCache
|
|
95
|
+
// Store processed (blurred/rounded) variants on disk in addition to
|
|
96
|
+
// the original download, so they survive memory eviction and
|
|
97
|
+
// restarts without dropping the original for other variants.
|
|
98
|
+
configuration.dataCachePolicy = .storeAll
|
|
99
|
+
return ImagePipeline(configuration: configuration)
|
|
100
|
+
}()
|
|
101
|
+
|
|
102
|
+
private static let sharedPrefetcher = ImagePrefetcher(pipeline: sharedPipeline)
|
|
103
|
+
|
|
104
|
+
// CIContext is expensive to create; Apple recommends reusing one.
|
|
105
|
+
private static let ciContext = CIContext()
|
|
106
|
+
|
|
107
|
+
private var pipeline: ImagePipeline { Self.sharedPipeline }
|
|
108
|
+
private var prefetcher: ImagePrefetcher { Self.sharedPrefetcher }
|
|
58
109
|
|
|
59
110
|
func loadImage(url: String, options: Options?) throws -> Promise<any HybridImageSpec> {
|
|
60
111
|
return Promise.async {
|
|
112
|
+
guard let imageUrl = URL(string: url) else {
|
|
113
|
+
throw RuntimeError.error(withMessage: "Invalid URL: \(url)")
|
|
114
|
+
}
|
|
115
|
+
|
|
61
116
|
let cacheOptions: ImageRequest.Options = switch options?.cache {
|
|
62
117
|
case .memory: [.disableDiskCache]
|
|
63
118
|
case .disk: [.disableMemoryCache]
|
|
@@ -74,12 +129,12 @@ class HybridNitroImagePipeline: HybridNitroImagePipelineSpec {
|
|
|
74
129
|
}
|
|
75
130
|
|
|
76
131
|
let imgRequest = ImageRequest(
|
|
77
|
-
url:
|
|
132
|
+
url: imageUrl,
|
|
78
133
|
processors: processors,
|
|
79
134
|
options: cacheOptions
|
|
80
135
|
)
|
|
81
136
|
|
|
82
|
-
let image = try await
|
|
137
|
+
let image = try await self.pipeline.image(for: imgRequest)
|
|
83
138
|
return HybridImage(uiImage: image)
|
|
84
139
|
}
|
|
85
140
|
}
|
|
@@ -101,8 +156,10 @@ class HybridNitroImagePipeline: HybridNitroImagePipelineSpec {
|
|
|
101
156
|
}
|
|
102
157
|
}
|
|
103
158
|
|
|
104
|
-
func clearCache() throws {
|
|
105
|
-
|
|
159
|
+
func clearCache() throws -> Promise<Void> {
|
|
160
|
+
return Promise.async {
|
|
161
|
+
self.pipeline.cache.removeAll()
|
|
162
|
+
}
|
|
106
163
|
}
|
|
107
164
|
|
|
108
165
|
func gaussianBlur(image: any HybridImageSpec, radius: Double) throws -> Promise<any HybridImageSpec> {
|
|
@@ -113,17 +170,20 @@ class HybridNitroImagePipeline: HybridNitroImagePipelineSpec {
|
|
|
113
170
|
|
|
114
171
|
let uiImage = nativeImage.uiImage
|
|
115
172
|
|
|
116
|
-
let ciImage = CIImage(image: uiImage)
|
|
173
|
+
guard let ciImage = CIImage(image: uiImage) else {
|
|
174
|
+
throw RuntimeError.error(withMessage: "Failed to read image data")
|
|
175
|
+
}
|
|
117
176
|
|
|
118
177
|
let filter = CIFilter(name: "CIGaussianBlur")!
|
|
119
178
|
filter.setValue(ciImage, forKey: kCIInputImageKey)
|
|
120
179
|
filter.setValue(radius, forKey: kCIInputRadiusKey)
|
|
121
180
|
|
|
122
|
-
let
|
|
123
|
-
|
|
124
|
-
|
|
181
|
+
guard let output = filter.outputImage,
|
|
182
|
+
let cgImage = Self.ciContext.createCGImage(output, from: ciImage.extent) else {
|
|
183
|
+
throw RuntimeError.error(withMessage: "Failed to apply gaussian blur")
|
|
184
|
+
}
|
|
125
185
|
|
|
126
|
-
return HybridImage(uiImage: UIImage(cgImage: cgImage
|
|
186
|
+
return HybridImage(uiImage: UIImage(cgImage: cgImage))
|
|
127
187
|
}
|
|
128
188
|
}
|
|
129
189
|
|
package/lib/commonjs/index.js
CHANGED
|
@@ -19,32 +19,53 @@ const NitroImagePipeline = exports.NitroImagePipeline = _reactNativeNitroModules
|
|
|
19
19
|
function useImage({
|
|
20
20
|
url,
|
|
21
21
|
blur = 0,
|
|
22
|
-
cornerRadius = 0
|
|
22
|
+
cornerRadius = 0,
|
|
23
|
+
cache
|
|
23
24
|
}) {
|
|
24
25
|
const [image, setImage] = (0, _react.useState)({
|
|
25
26
|
image: undefined,
|
|
26
27
|
error: undefined
|
|
27
28
|
});
|
|
29
|
+
const loadedUrlRef = (0, _react.useRef)(url);
|
|
28
30
|
(0, _react.useEffect)(() => {
|
|
31
|
+
let cancelled = false;
|
|
32
|
+
// Only reset to the loading state when the URL changes; for same-URL
|
|
33
|
+
// param tweaks (blur/cornerRadius/cache) keep showing the current image
|
|
34
|
+
// until the new variant resolves, to avoid flashing empty.
|
|
35
|
+
if (loadedUrlRef.current !== url) {
|
|
36
|
+
loadedUrlRef.current = url;
|
|
37
|
+
setImage({
|
|
38
|
+
image: undefined,
|
|
39
|
+
error: undefined
|
|
40
|
+
});
|
|
41
|
+
}
|
|
29
42
|
(async () => {
|
|
30
43
|
try {
|
|
31
44
|
const result = await NitroImagePipeline.loadImage(url, {
|
|
32
45
|
blur,
|
|
33
|
-
cornerRadius
|
|
34
|
-
|
|
35
|
-
setImage({
|
|
36
|
-
image: result,
|
|
37
|
-
error: undefined
|
|
46
|
+
cornerRadius,
|
|
47
|
+
cache
|
|
38
48
|
});
|
|
49
|
+
if (!cancelled) {
|
|
50
|
+
setImage({
|
|
51
|
+
image: result,
|
|
52
|
+
error: undefined
|
|
53
|
+
});
|
|
54
|
+
}
|
|
39
55
|
} catch (e) {
|
|
40
56
|
const error = e instanceof Error ? e : new Error(`${e}`);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
57
|
+
if (!cancelled) {
|
|
58
|
+
setImage({
|
|
59
|
+
image: undefined,
|
|
60
|
+
error: error
|
|
61
|
+
});
|
|
62
|
+
}
|
|
45
63
|
}
|
|
46
64
|
})();
|
|
47
|
-
|
|
65
|
+
return () => {
|
|
66
|
+
cancelled = true;
|
|
67
|
+
};
|
|
68
|
+
}, [url, blur, cornerRadius, cache]);
|
|
48
69
|
return image;
|
|
49
70
|
}
|
|
50
71
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","require","_reactNativeNitroModules","NitroImagePipeline","exports","NitroModules","createHybridObject","useImage","url","blur","cornerRadius","image","setImage","useState","undefined","error","useEffect","result","loadImage","e","Error"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAEA,IAAAC,wBAAA,GAAAD,OAAA;
|
|
1
|
+
{"version":3,"names":["_react","require","_reactNativeNitroModules","NitroImagePipeline","exports","NitroModules","createHybridObject","useImage","url","blur","cornerRadius","cache","image","setImage","useState","undefined","error","loadedUrlRef","useRef","useEffect","cancelled","current","result","loadImage","e","Error"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAEA,IAAAC,wBAAA,GAAAD,OAAA;AAUO,MAAME,kBAAkB,GAAAC,OAAA,CAAAD,kBAAA,GAC7BE,qCAAY,CAACC,kBAAkB,CAAyB,oBAAoB,CAAC;AAmB/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,QAAQA,CAAC;EACvBC,GAAG;EACHC,IAAI,GAAG,CAAC;EACRC,YAAY,GAAG,CAAC;EAChBC;AAMF,CAAC,EAAU;EACT,MAAM,CAACC,KAAK,EAAEC,QAAQ,CAAC,GAAG,IAAAC,eAAQ,EAAS;IACzCF,KAAK,EAAEG,SAAS;IAChBC,KAAK,EAAED;EACT,CAAC,CAAC;EACF,MAAME,YAAY,GAAG,IAAAC,aAAM,EAACV,GAAG,CAAC;EAEhC,IAAAW,gBAAS,EAAC,MAAM;IACd,IAAIC,SAAS,GAAG,KAAK;IACrB;IACA;IACA;IACA,IAAIH,YAAY,CAACI,OAAO,KAAKb,GAAG,EAAE;MAChCS,YAAY,CAACI,OAAO,GAAGb,GAAG;MAC1BK,QAAQ,CAAC;QAAED,KAAK,EAAEG,SAAS;QAAEC,KAAK,EAAED;MAAU,CAAC,CAAC;IAClD;IAEA,CAAC,YAAY;MACX,IAAI;QACF,MAAMO,MAAM,GAAG,MAAMnB,kBAAkB,CAACoB,SAAS,CAACf,GAAG,EAAE;UACrDC,IAAI;UACJC,YAAY;UACZC;QACF,CAAC,CAAC;QAEF,IAAI,CAACS,SAAS,EAAE;UACdP,QAAQ,CAAC;YAAED,KAAK,EAAEU,MAAM;YAAEN,KAAK,EAAED;UAAU,CAAC,CAAC;QAC/C;MACF,CAAC,CAAC,OAAOS,CAAC,EAAE;QACV,MAAMR,KAAK,GAAGQ,CAAC,YAAYC,KAAK,GAAGD,CAAC,GAAG,IAAIC,KAAK,CAAC,GAAGD,CAAC,EAAE,CAAC;QACxD,IAAI,CAACJ,SAAS,EAAE;UACdP,QAAQ,CAAC;YAAED,KAAK,EAAEG,SAAS;YAAEC,KAAK,EAAEA;UAAM,CAAC,CAAC;QAC9C;MACF;IACF,CAAC,EAAE,CAAC;IAEJ,OAAO,MAAM;MACXI,SAAS,GAAG,IAAI;IAClB,CAAC;EACH,CAAC,EAAE,CAACZ,GAAG,EAAEC,IAAI,EAAEC,YAAY,EAAEC,KAAK,CAAC,CAAC;EAEpC,OAAOC,KAAK;AACd","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
3
|
+
import { useEffect, useRef, useState } from 'react';
|
|
4
4
|
import { NitroModules } from 'react-native-nitro-modules';
|
|
5
5
|
export const NitroImagePipeline = NitroModules.createHybridObject('NitroImagePipeline');
|
|
6
6
|
/**
|
|
@@ -14,32 +14,53 @@ export const NitroImagePipeline = NitroModules.createHybridObject('NitroImagePip
|
|
|
14
14
|
export function useImage({
|
|
15
15
|
url,
|
|
16
16
|
blur = 0,
|
|
17
|
-
cornerRadius = 0
|
|
17
|
+
cornerRadius = 0,
|
|
18
|
+
cache
|
|
18
19
|
}) {
|
|
19
20
|
const [image, setImage] = useState({
|
|
20
21
|
image: undefined,
|
|
21
22
|
error: undefined
|
|
22
23
|
});
|
|
24
|
+
const loadedUrlRef = useRef(url);
|
|
23
25
|
useEffect(() => {
|
|
26
|
+
let cancelled = false;
|
|
27
|
+
// Only reset to the loading state when the URL changes; for same-URL
|
|
28
|
+
// param tweaks (blur/cornerRadius/cache) keep showing the current image
|
|
29
|
+
// until the new variant resolves, to avoid flashing empty.
|
|
30
|
+
if (loadedUrlRef.current !== url) {
|
|
31
|
+
loadedUrlRef.current = url;
|
|
32
|
+
setImage({
|
|
33
|
+
image: undefined,
|
|
34
|
+
error: undefined
|
|
35
|
+
});
|
|
36
|
+
}
|
|
24
37
|
(async () => {
|
|
25
38
|
try {
|
|
26
39
|
const result = await NitroImagePipeline.loadImage(url, {
|
|
27
40
|
blur,
|
|
28
|
-
cornerRadius
|
|
29
|
-
|
|
30
|
-
setImage({
|
|
31
|
-
image: result,
|
|
32
|
-
error: undefined
|
|
41
|
+
cornerRadius,
|
|
42
|
+
cache
|
|
33
43
|
});
|
|
44
|
+
if (!cancelled) {
|
|
45
|
+
setImage({
|
|
46
|
+
image: result,
|
|
47
|
+
error: undefined
|
|
48
|
+
});
|
|
49
|
+
}
|
|
34
50
|
} catch (e) {
|
|
35
51
|
const error = e instanceof Error ? e : new Error(`${e}`);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
52
|
+
if (!cancelled) {
|
|
53
|
+
setImage({
|
|
54
|
+
image: undefined,
|
|
55
|
+
error: error
|
|
56
|
+
});
|
|
57
|
+
}
|
|
40
58
|
}
|
|
41
59
|
})();
|
|
42
|
-
|
|
60
|
+
return () => {
|
|
61
|
+
cancelled = true;
|
|
62
|
+
};
|
|
63
|
+
}, [url, blur, cornerRadius, cache]);
|
|
43
64
|
return image;
|
|
44
65
|
}
|
|
45
66
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useEffect","useState","NitroModules","NitroImagePipeline","createHybridObject","useImage","url","blur","cornerRadius","image","setImage","undefined","error","result","loadImage","e","Error"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,SAAS,EAAEC,QAAQ,QAAQ,OAAO;
|
|
1
|
+
{"version":3,"names":["useEffect","useRef","useState","NitroModules","NitroImagePipeline","createHybridObject","useImage","url","blur","cornerRadius","cache","image","setImage","undefined","error","loadedUrlRef","cancelled","current","result","loadImage","e","Error"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,OAAO;AAEnD,SAASC,YAAY,QAAQ,4BAA4B;AAUzD,OAAO,MAAMC,kBAAkB,GAC7BD,YAAY,CAACE,kBAAkB,CAAyB,oBAAoB,CAAC;AAmB/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,QAAQA,CAAC;EACvBC,GAAG;EACHC,IAAI,GAAG,CAAC;EACRC,YAAY,GAAG,CAAC;EAChBC;AAMF,CAAC,EAAU;EACT,MAAM,CAACC,KAAK,EAAEC,QAAQ,CAAC,GAAGV,QAAQ,CAAS;IACzCS,KAAK,EAAEE,SAAS;IAChBC,KAAK,EAAED;EACT,CAAC,CAAC;EACF,MAAME,YAAY,GAAGd,MAAM,CAACM,GAAG,CAAC;EAEhCP,SAAS,CAAC,MAAM;IACd,IAAIgB,SAAS,GAAG,KAAK;IACrB;IACA;IACA;IACA,IAAID,YAAY,CAACE,OAAO,KAAKV,GAAG,EAAE;MAChCQ,YAAY,CAACE,OAAO,GAAGV,GAAG;MAC1BK,QAAQ,CAAC;QAAED,KAAK,EAAEE,SAAS;QAAEC,KAAK,EAAED;MAAU,CAAC,CAAC;IAClD;IAEA,CAAC,YAAY;MACX,IAAI;QACF,MAAMK,MAAM,GAAG,MAAMd,kBAAkB,CAACe,SAAS,CAACZ,GAAG,EAAE;UACrDC,IAAI;UACJC,YAAY;UACZC;QACF,CAAC,CAAC;QAEF,IAAI,CAACM,SAAS,EAAE;UACdJ,QAAQ,CAAC;YAAED,KAAK,EAAEO,MAAM;YAAEJ,KAAK,EAAED;UAAU,CAAC,CAAC;QAC/C;MACF,CAAC,CAAC,OAAOO,CAAC,EAAE;QACV,MAAMN,KAAK,GAAGM,CAAC,YAAYC,KAAK,GAAGD,CAAC,GAAG,IAAIC,KAAK,CAAC,GAAGD,CAAC,EAAE,CAAC;QACxD,IAAI,CAACJ,SAAS,EAAE;UACdJ,QAAQ,CAAC;YAAED,KAAK,EAAEE,SAAS;YAAEC,KAAK,EAAEA;UAAM,CAAC,CAAC;QAC9C;MACF;IACF,CAAC,EAAE,CAAC;IAEJ,OAAO,MAAM;MACXE,SAAS,GAAG,IAAI;IAClB,CAAC;EACH,CAAC,EAAE,CAACT,GAAG,EAAEC,IAAI,EAAEC,YAAY,EAAEC,KAAK,CAAC,CAAC;EAEpC,OAAOC,KAAK;AACd","ignoreList":[]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Image } from 'react-native-nitro-image';
|
|
2
|
-
import type { NitroImagePipeline as NitroImagePipelineSpec } from './specs/nitro-image-toolkit.nitro';
|
|
2
|
+
import type { CacheOption, NitroImagePipeline as NitroImagePipelineSpec, Options } from './specs/nitro-image-toolkit.nitro';
|
|
3
|
+
export type { CacheOption, Options };
|
|
3
4
|
export declare const NitroImagePipeline: NitroImagePipelineSpec;
|
|
4
5
|
type Result = {
|
|
5
6
|
image: undefined;
|
|
@@ -19,10 +20,10 @@ type Result = {
|
|
|
19
20
|
* const { image, error } = useImage({ filePath: '/tmp/image.jpg' })
|
|
20
21
|
* ```
|
|
21
22
|
*/
|
|
22
|
-
export declare function useImage({ url, blur, cornerRadius, }: {
|
|
23
|
+
export declare function useImage({ url, blur, cornerRadius, cache, }: {
|
|
23
24
|
url: string;
|
|
24
25
|
blur?: number;
|
|
25
26
|
cornerRadius?: number;
|
|
27
|
+
cache?: CacheOption;
|
|
26
28
|
}): Result;
|
|
27
|
-
export {};
|
|
28
29
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAGtD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAGtD,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,IAAI,sBAAsB,EAC5C,OAAO,EACR,MAAM,mCAAmC,CAAC;AAE3C,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AAErC,eAAO,MAAM,kBAAkB,wBACgD,CAAC;AAEhF,KAAK,MAAM,GAEP;IACE,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;CAClB,GAED;IACE,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;CAClB,GAED;IACE,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEN;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,IAAQ,EACR,YAAgB,EAChB,KAAK,GACN,EAAE;IACD,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,MAAM,CA0CT"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Image } from 'react-native-nitro-image';
|
|
2
2
|
import type { HybridObject } from 'react-native-nitro-modules';
|
|
3
|
-
type CacheOption = 'memory' | 'disk' | 'none';
|
|
4
|
-
type Options = {
|
|
3
|
+
export type CacheOption = 'memory' | 'disk' | 'none';
|
|
4
|
+
export type Options = {
|
|
5
5
|
blur?: number;
|
|
6
6
|
cache?: CacheOption;
|
|
7
7
|
cornerRadius?: number;
|
|
@@ -14,7 +14,6 @@ export interface NitroImagePipeline extends HybridObject<{
|
|
|
14
14
|
preLoadImage(url: string): Promise<void>;
|
|
15
15
|
preLoadImages(urls: string[]): Promise<void>;
|
|
16
16
|
gaussianBlur(image: Image, radius: number): Promise<Image>;
|
|
17
|
-
clearCache(): void
|
|
17
|
+
clearCache(): Promise<void>;
|
|
18
18
|
}
|
|
19
|
-
export {};
|
|
20
19
|
//# sourceMappingURL=nitro-image-toolkit.nitro.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nitro-image-toolkit.nitro.d.ts","sourceRoot":"","sources":["../../../../src/specs/nitro-image-toolkit.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,
|
|
1
|
+
{"version":3,"file":"nitro-image-toolkit.nitro.d.ts","sourceRoot":"","sources":["../../../../src/specs/nitro-image-toolkit.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AACrD,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,WAAW,kBACf,SAAQ,YAAY,CAAC;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;IACzD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1D,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAG3D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B"}
|
|
@@ -15,7 +15,7 @@ namespace margelo::nitro::nitroimagepipeline {
|
|
|
15
15
|
using namespace facebook;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* The C++ JNI bridge between the C++ enum "CacheOption" and the
|
|
18
|
+
* The C++ JNI bridge between the C++ enum "CacheOption" and the Kotlin enum "CacheOption".
|
|
19
19
|
*/
|
|
20
20
|
struct JCacheOption final: public jni::JavaClass<JCacheOption> {
|
|
21
21
|
public:
|
|
@@ -94,16 +94,16 @@ namespace margelo::nitro::nitroimagepipeline {
|
|
|
94
94
|
}
|
|
95
95
|
std::shared_ptr<Promise<void>> JHybridNitroImagePipelineSpec::preLoadImages(const std::vector<std::string>& urls) {
|
|
96
96
|
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JArrayClass<jni::JString>> /* urls */)>("preLoadImages");
|
|
97
|
-
auto __result = method(_javaPart, [&]() {
|
|
98
|
-
size_t __size =
|
|
97
|
+
auto __result = method(_javaPart, [&](auto&& __input) {
|
|
98
|
+
size_t __size = __input.size();
|
|
99
99
|
jni::local_ref<jni::JArrayClass<jni::JString>> __array = jni::JArrayClass<jni::JString>::newArray(__size);
|
|
100
100
|
for (size_t __i = 0; __i < __size; __i++) {
|
|
101
|
-
const auto& __element =
|
|
101
|
+
const auto& __element = __input[__i];
|
|
102
102
|
auto __elementJni = jni::make_jstring(__element);
|
|
103
103
|
__array->setElement(__i, *__elementJni);
|
|
104
104
|
}
|
|
105
105
|
return __array;
|
|
106
|
-
}());
|
|
106
|
+
}(urls));
|
|
107
107
|
return [&]() {
|
|
108
108
|
auto __promise = Promise<void>::create();
|
|
109
109
|
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& /* unit */) {
|
|
@@ -132,9 +132,20 @@ namespace margelo::nitro::nitroimagepipeline {
|
|
|
132
132
|
return __promise;
|
|
133
133
|
}();
|
|
134
134
|
}
|
|
135
|
-
void JHybridNitroImagePipelineSpec::clearCache() {
|
|
136
|
-
static const auto method = _javaPart->javaClassStatic()->getMethod<
|
|
137
|
-
method(_javaPart);
|
|
135
|
+
std::shared_ptr<Promise<void>> JHybridNitroImagePipelineSpec::clearCache() {
|
|
136
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>()>("clearCache");
|
|
137
|
+
auto __result = method(_javaPart);
|
|
138
|
+
return [&]() {
|
|
139
|
+
auto __promise = Promise<void>::create();
|
|
140
|
+
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& /* unit */) {
|
|
141
|
+
__promise->resolve();
|
|
142
|
+
});
|
|
143
|
+
__result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
|
|
144
|
+
jni::JniException __jniError(__throwable);
|
|
145
|
+
__promise->reject(std::make_exception_ptr(__jniError));
|
|
146
|
+
});
|
|
147
|
+
return __promise;
|
|
148
|
+
}();
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
} // namespace margelo::nitro::nitroimagepipeline
|
|
@@ -58,7 +58,7 @@ namespace margelo::nitro::nitroimagepipeline {
|
|
|
58
58
|
std::shared_ptr<Promise<void>> preLoadImage(const std::string& url) override;
|
|
59
59
|
std::shared_ptr<Promise<void>> preLoadImages(const std::vector<std::string>& urls) override;
|
|
60
60
|
std::shared_ptr<Promise<std::shared_ptr<margelo::nitro::image::HybridImageSpec>>> gaussianBlur(const std::shared_ptr<margelo::nitro::image::HybridImageSpec>& image, double radius) override;
|
|
61
|
-
void clearCache() override;
|
|
61
|
+
std::shared_ptr<Promise<void>> clearCache() override;
|
|
62
62
|
|
|
63
63
|
private:
|
|
64
64
|
jni::global_ref<JHybridNitroImagePipelineSpec::JavaPart> _javaPart;
|
|
@@ -19,7 +19,7 @@ namespace margelo::nitro::nitroimagepipeline {
|
|
|
19
19
|
using namespace facebook;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* The C++ JNI bridge between the C++ struct "Options" and the
|
|
22
|
+
* The C++ JNI bridge between the C++ struct "Options" and the Kotlin data class "Options".
|
|
23
23
|
*/
|
|
24
24
|
struct JOptions final: public jni::JavaClass<JOptions> {
|
|
25
25
|
public:
|
|
@@ -10,6 +10,7 @@ package com.margelo.nitro.nitroimagepipeline
|
|
|
10
10
|
import androidx.annotation.Keep
|
|
11
11
|
import com.facebook.jni.HybridData
|
|
12
12
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
13
|
+
import dalvik.annotation.optimization.FastNative
|
|
13
14
|
import com.margelo.nitro.image.HybridImageSpec
|
|
14
15
|
import com.margelo.nitro.core.Promise
|
|
15
16
|
import com.margelo.nitro.core.HybridObject
|
|
@@ -48,7 +49,7 @@ abstract class HybridNitroImagePipelineSpec: HybridObject() {
|
|
|
48
49
|
|
|
49
50
|
@DoNotStrip
|
|
50
51
|
@Keep
|
|
51
|
-
abstract fun clearCache(): Unit
|
|
52
|
+
abstract fun clearCache(): Promise<Unit>
|
|
52
53
|
|
|
53
54
|
// Default implementation of `HybridObject.toString()`
|
|
54
55
|
override fun toString(): String {
|
|
@@ -60,6 +61,7 @@ abstract class HybridNitroImagePipelineSpec: HybridObject() {
|
|
|
60
61
|
@Keep
|
|
61
62
|
protected open class CxxPart(javaPart: HybridNitroImagePipelineSpec): HybridObject.CxxPart(javaPart) {
|
|
62
63
|
// C++ JHybridNitroImagePipelineSpec::CxxPart::initHybrid(...)
|
|
64
|
+
@FastNative
|
|
63
65
|
external override fun initHybrid(): HybridData
|
|
64
66
|
}
|
|
65
67
|
override fun createCxxPart(): CxxPart {
|
|
@@ -9,6 +9,7 @@ package com.margelo.nitro.nitroimagepipeline
|
|
|
9
9
|
|
|
10
10
|
import androidx.annotation.Keep
|
|
11
11
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
12
|
+
import java.util.Objects
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -29,6 +30,22 @@ data class Options(
|
|
|
29
30
|
) {
|
|
30
31
|
/* primary constructor */
|
|
31
32
|
|
|
33
|
+
override fun equals(other: Any?): Boolean {
|
|
34
|
+
if (this === other) return true
|
|
35
|
+
if (other !is Options) return false
|
|
36
|
+
return Objects.deepEquals(this.blur, other.blur)
|
|
37
|
+
&& Objects.deepEquals(this.cache, other.cache)
|
|
38
|
+
&& Objects.deepEquals(this.cornerRadius, other.cornerRadius)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
override fun hashCode(): Int {
|
|
42
|
+
return arrayOf<Any?>(
|
|
43
|
+
blur,
|
|
44
|
+
cache,
|
|
45
|
+
cornerRadius
|
|
46
|
+
).contentDeepHashCode()
|
|
47
|
+
}
|
|
48
|
+
|
|
32
49
|
companion object {
|
|
33
50
|
/**
|
|
34
51
|
* Constructor called from C++
|
|
@@ -231,14 +231,5 @@ namespace margelo::nitro::nitroimagepipeline::bridge::swift {
|
|
|
231
231
|
inline Result_std__shared_ptr_Promise_void___ create_Result_std__shared_ptr_Promise_void___(const std::exception_ptr& error) noexcept {
|
|
232
232
|
return Result<std::shared_ptr<Promise<void>>>::withError(error);
|
|
233
233
|
}
|
|
234
|
-
|
|
235
|
-
// pragma MARK: Result<void>
|
|
236
|
-
using Result_void_ = Result<void>;
|
|
237
|
-
inline Result_void_ create_Result_void_() noexcept {
|
|
238
|
-
return Result<void>::withValue();
|
|
239
|
-
}
|
|
240
|
-
inline Result_void_ create_Result_void_(const std::exception_ptr& error) noexcept {
|
|
241
|
-
return Result<void>::withError(error);
|
|
242
|
-
}
|
|
243
234
|
|
|
244
235
|
} // namespace margelo::nitro::nitroimagepipeline::bridge::swift
|
|
@@ -110,11 +110,13 @@ namespace margelo::nitro::nitroimagepipeline {
|
|
|
110
110
|
auto __value = std::move(__result.value());
|
|
111
111
|
return __value;
|
|
112
112
|
}
|
|
113
|
-
inline void clearCache() override {
|
|
113
|
+
inline std::shared_ptr<Promise<void>> clearCache() override {
|
|
114
114
|
auto __result = _swiftPart.clearCache();
|
|
115
115
|
if (__result.hasError()) [[unlikely]] {
|
|
116
116
|
std::rethrow_exception(__result.error());
|
|
117
117
|
}
|
|
118
|
+
auto __value = std::move(__result.value());
|
|
119
|
+
return __value;
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
private:
|
|
@@ -18,7 +18,7 @@ public protocol HybridNitroImagePipelineSpec_protocol: HybridObject {
|
|
|
18
18
|
func preLoadImage(url: String) throws -> Promise<Void>
|
|
19
19
|
func preLoadImages(urls: [String]) throws -> Promise<Void>
|
|
20
20
|
func gaussianBlur(image: (any HybridImageSpec), radius: Double) throws -> Promise<(any HybridImageSpec)>
|
|
21
|
-
func clearCache() throws -> Void
|
|
21
|
+
func clearCache() throws -> Promise<Void>
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
public extension HybridNitroImagePipelineSpec_protocol {
|
|
@@ -212,13 +212,21 @@ open class HybridNitroImagePipelineSpec_cxx {
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
@inline(__always)
|
|
215
|
-
public final func clearCache() -> bridge.
|
|
215
|
+
public final func clearCache() -> bridge.Result_std__shared_ptr_Promise_void___ {
|
|
216
216
|
do {
|
|
217
|
-
try self.__implementation.clearCache()
|
|
218
|
-
|
|
217
|
+
let __result = try self.__implementation.clearCache()
|
|
218
|
+
let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in
|
|
219
|
+
let __promise = bridge.create_std__shared_ptr_Promise_void__()
|
|
220
|
+
let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise)
|
|
221
|
+
__result
|
|
222
|
+
.then({ __result in __promiseHolder.resolve() })
|
|
223
|
+
.catch({ __error in __promiseHolder.reject(__error.toCpp()) })
|
|
224
|
+
return __promise
|
|
225
|
+
}()
|
|
226
|
+
return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp)
|
|
219
227
|
} catch (let __error) {
|
|
220
228
|
let __exceptionPtr = __error.toCpp()
|
|
221
|
-
return bridge.
|
|
229
|
+
return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr)
|
|
222
230
|
}
|
|
223
231
|
}
|
|
224
232
|
}
|
|
@@ -61,7 +61,7 @@ namespace margelo::nitro::nitroimagepipeline {
|
|
|
61
61
|
virtual std::shared_ptr<Promise<void>> preLoadImage(const std::string& url) = 0;
|
|
62
62
|
virtual std::shared_ptr<Promise<void>> preLoadImages(const std::vector<std::string>& urls) = 0;
|
|
63
63
|
virtual std::shared_ptr<Promise<std::shared_ptr<margelo::nitro::image::HybridImageSpec>>> gaussianBlur(const std::shared_ptr<margelo::nitro::image::HybridImageSpec>& image, double radius) = 0;
|
|
64
|
-
virtual void clearCache() = 0;
|
|
64
|
+
virtual std::shared_ptr<Promise<void>> clearCache() = 0;
|
|
65
65
|
|
|
66
66
|
protected:
|
|
67
67
|
// Hybrid Setup
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-image-pipeline",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "High-performance image loading, caching, and processing for React Native, built with Nitro Modules",
|
|
5
5
|
"main": "./lib/commonjs/index.js",
|
|
6
6
|
"module": "./lib/module/index.js",
|
|
@@ -59,19 +59,19 @@
|
|
|
59
59
|
"registry": "https://registry.npmjs.org/"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@biomejs/biome": "^2.
|
|
63
|
-
"@semantic-release/changelog": "^
|
|
64
|
-
"@semantic-release/git": "^
|
|
65
|
-
"@types/bun": "^1.3.
|
|
62
|
+
"@biomejs/biome": "^2.5.6",
|
|
63
|
+
"@semantic-release/changelog": "^7.0.0",
|
|
64
|
+
"@semantic-release/git": "^11.0.1",
|
|
65
|
+
"@types/bun": "^1.3.14",
|
|
66
66
|
"@types/jest": "^30.0.0",
|
|
67
|
-
"@types/react": "19.2.
|
|
68
|
-
"conventional-changelog-conventionalcommits": "^
|
|
69
|
-
"nitrogen": "0.36.
|
|
67
|
+
"@types/react": "19.2.18",
|
|
68
|
+
"conventional-changelog-conventionalcommits": "^10.2.1",
|
|
69
|
+
"nitrogen": "0.36.5",
|
|
70
70
|
"react": "19.2.3",
|
|
71
71
|
"react-native": "0.84.1",
|
|
72
72
|
"react-native-builder-bob": "^0.43.0",
|
|
73
|
-
"react-native-nitro-modules": "0.36.
|
|
74
|
-
"semantic-release": "^25.0.
|
|
73
|
+
"react-native-nitro-modules": "0.36.5",
|
|
74
|
+
"semantic-release": "^25.0.8",
|
|
75
75
|
"typescript": "^5.8.3"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import type { Image } from 'react-native-nitro-image';
|
|
3
3
|
import { NitroModules } from 'react-native-nitro-modules';
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
CacheOption,
|
|
7
|
+
NitroImagePipeline as NitroImagePipelineSpec,
|
|
8
|
+
Options,
|
|
9
|
+
} from './specs/nitro-image-toolkit.nitro';
|
|
10
|
+
|
|
11
|
+
export type { CacheOption, Options };
|
|
6
12
|
|
|
7
13
|
export const NitroImagePipeline =
|
|
8
14
|
NitroModules.createHybridObject<NitroImagePipelineSpec>('NitroImagePipeline');
|
|
@@ -36,31 +42,52 @@ export function useImage({
|
|
|
36
42
|
url,
|
|
37
43
|
blur = 0,
|
|
38
44
|
cornerRadius = 0,
|
|
45
|
+
cache,
|
|
39
46
|
}: {
|
|
40
47
|
url: string;
|
|
41
48
|
blur?: number;
|
|
42
49
|
cornerRadius?: number;
|
|
50
|
+
cache?: CacheOption;
|
|
43
51
|
}): Result {
|
|
44
52
|
const [image, setImage] = useState<Result>({
|
|
45
53
|
image: undefined,
|
|
46
54
|
error: undefined,
|
|
47
55
|
});
|
|
56
|
+
const loadedUrlRef = useRef(url);
|
|
48
57
|
|
|
49
58
|
useEffect(() => {
|
|
59
|
+
let cancelled = false;
|
|
60
|
+
// Only reset to the loading state when the URL changes; for same-URL
|
|
61
|
+
// param tweaks (blur/cornerRadius/cache) keep showing the current image
|
|
62
|
+
// until the new variant resolves, to avoid flashing empty.
|
|
63
|
+
if (loadedUrlRef.current !== url) {
|
|
64
|
+
loadedUrlRef.current = url;
|
|
65
|
+
setImage({ image: undefined, error: undefined });
|
|
66
|
+
}
|
|
67
|
+
|
|
50
68
|
(async () => {
|
|
51
69
|
try {
|
|
52
70
|
const result = await NitroImagePipeline.loadImage(url, {
|
|
53
71
|
blur,
|
|
54
72
|
cornerRadius,
|
|
73
|
+
cache,
|
|
55
74
|
});
|
|
56
75
|
|
|
57
|
-
|
|
76
|
+
if (!cancelled) {
|
|
77
|
+
setImage({ image: result, error: undefined });
|
|
78
|
+
}
|
|
58
79
|
} catch (e) {
|
|
59
80
|
const error = e instanceof Error ? e : new Error(`${e}`);
|
|
60
|
-
|
|
81
|
+
if (!cancelled) {
|
|
82
|
+
setImage({ image: undefined, error: error });
|
|
83
|
+
}
|
|
61
84
|
}
|
|
62
85
|
})();
|
|
63
|
-
|
|
86
|
+
|
|
87
|
+
return () => {
|
|
88
|
+
cancelled = true;
|
|
89
|
+
};
|
|
90
|
+
}, [url, blur, cornerRadius, cache]);
|
|
64
91
|
|
|
65
92
|
return image;
|
|
66
93
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { Image } from 'react-native-nitro-image';
|
|
2
2
|
import type { HybridObject } from 'react-native-nitro-modules';
|
|
3
3
|
|
|
4
|
-
type CacheOption = 'memory' | 'disk' | 'none';
|
|
5
|
-
type Options = {
|
|
4
|
+
export type CacheOption = 'memory' | 'disk' | 'none';
|
|
5
|
+
export type Options = {
|
|
6
|
+
blur?: number;
|
|
7
|
+
cache?: CacheOption;
|
|
8
|
+
cornerRadius?: number;
|
|
9
|
+
};
|
|
6
10
|
|
|
7
11
|
export interface NitroImagePipeline
|
|
8
12
|
extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
|
|
@@ -12,5 +16,5 @@ export interface NitroImagePipeline
|
|
|
12
16
|
gaussianBlur(image: Image, radius: number): Promise<Image>;
|
|
13
17
|
// Future: brightness, saturation, tint, etc.
|
|
14
18
|
|
|
15
|
-
clearCache(): void
|
|
19
|
+
clearCache(): Promise<void>;
|
|
16
20
|
}
|