@onekeyfe/react-native-image 3.0.122 → 3.0.124
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/ios/OneKeyImage.swift +20 -3
- package/ios/tests/OneKeyImageInfrastructureTests.swift +13 -0
- package/lib/module/index.js +35 -1
- package/package.json +2 -2
- package/src/index.tsx +57 -1
package/ios/OneKeyImage.swift
CHANGED
|
@@ -344,11 +344,14 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
344
344
|
manager: safetyHandle.manager,
|
|
345
345
|
url: url
|
|
346
346
|
)
|
|
347
|
-
hostView.
|
|
347
|
+
hostView.sd_internalSetImage(
|
|
348
348
|
with: url,
|
|
349
349
|
placeholderImage: nil,
|
|
350
350
|
options: OneKeyImageRequestContext.renderOptions,
|
|
351
351
|
context: context,
|
|
352
|
+
setImageBlock: { [weak self] image, _, _, _ in
|
|
353
|
+
self?.applyDisplayedImage(image, generation: generation)
|
|
354
|
+
},
|
|
352
355
|
progress: { [weak self] receivedSize, _, _ in
|
|
353
356
|
guard safetyHandle.tracker.inspectReceivedByteCount(Int64(receivedSize)) else { return }
|
|
354
357
|
DispatchQueue.main.async { [weak self] in
|
|
@@ -356,7 +359,9 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
356
359
|
self.hostView.sd_cancelCurrentImageLoad()
|
|
357
360
|
}
|
|
358
361
|
},
|
|
359
|
-
completed: { [weak self] image, error, cacheType, _ in
|
|
362
|
+
completed: { [weak self] image, _, error, cacheType, finished, _ in
|
|
363
|
+
// Progressive loads deliver partial images before the terminal callback.
|
|
364
|
+
guard finished else { return }
|
|
360
365
|
guard let self, self.requestGeneration == generation else {
|
|
361
366
|
safetyHandle.finish()
|
|
362
367
|
return
|
|
@@ -515,6 +520,18 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
515
520
|
}
|
|
516
521
|
}
|
|
517
522
|
|
|
523
|
+
// OneKey patch: SDWebImage assigns `image` from inside its own completion, which
|
|
524
|
+
// runs before this object's generation guard and never re-checks whether the view
|
|
525
|
+
// still belongs to the request that started the load. Fabric recycles a single
|
|
526
|
+
// OneKeyImage UIView across unrelated images, so a superseded completion could
|
|
527
|
+
// repaint a slot that now shows a different image. Every write goes through here.
|
|
528
|
+
func applyDisplayedImage(_ image: UIImage?, generation: UInt64) {
|
|
529
|
+
guard Self.isCurrentRequestGeneration(generation, current: requestGeneration) else {
|
|
530
|
+
return
|
|
531
|
+
}
|
|
532
|
+
hostView.image = image
|
|
533
|
+
}
|
|
534
|
+
|
|
518
535
|
private func showLoading(
|
|
519
536
|
requestIsActive: Bool,
|
|
520
537
|
letLibraryStartIndicator: Bool
|
|
@@ -579,7 +596,7 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
579
596
|
skeletonIndicator.stopAnimatingIndicator()
|
|
580
597
|
fallbackLayer.isHidden = true
|
|
581
598
|
resetImageTransition()
|
|
582
|
-
|
|
599
|
+
applyDisplayedImage(image, generation: generation)
|
|
583
600
|
hostView.backgroundColor = .clear
|
|
584
601
|
if requestIsActive {
|
|
585
602
|
applyLoadedImageTransition(cacheType: .memory)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import SDWebImage
|
|
2
2
|
import SDWebImageSVGCoder
|
|
3
3
|
import SDWebImageWebPCoder
|
|
4
|
+
import UIKit
|
|
4
5
|
import XCTest
|
|
5
6
|
|
|
6
7
|
@testable import OneKeyImage
|
|
@@ -543,6 +544,18 @@ final class OneKeyImageInfrastructureTests: XCTestCase {
|
|
|
543
544
|
)
|
|
544
545
|
}
|
|
545
546
|
|
|
547
|
+
func testSupersededRequestNeverPaintsRecycledView() throws {
|
|
548
|
+
let image = HybridOneKeyImage()
|
|
549
|
+
let imageView = try XCTUnwrap(image.view as? SDAnimatedImageView)
|
|
550
|
+
let painted = UIImage()
|
|
551
|
+
|
|
552
|
+
image.applyDisplayedImage(painted, generation: .max)
|
|
553
|
+
XCTAssertNil(imageView.image)
|
|
554
|
+
|
|
555
|
+
image.applyDisplayedImage(painted, generation: 0)
|
|
556
|
+
XCTAssertIdentical(imageView.image, painted)
|
|
557
|
+
}
|
|
558
|
+
|
|
546
559
|
func testAnimatedViewUsesBoundedFrameBuffer() throws {
|
|
547
560
|
let image = HybridOneKeyImage()
|
|
548
561
|
let imageView = try XCTUnwrap(image.view as? SDAnimatedImageView)
|
package/lib/module/index.js
CHANGED
|
@@ -173,6 +173,30 @@ export function OneKeyImage({
|
|
|
173
173
|
const flattenedStyle = StyleSheet.flatten(style);
|
|
174
174
|
const hasRoundedCorners = Boolean(flattenedStyle && [flattenedStyle.borderRadius, flattenedStyle.borderTopLeftRadius, flattenedStyle.borderTopRightRadius, flattenedStyle.borderBottomLeftRadius, flattenedStyle.borderBottomRightRadius, flattenedStyle.borderStartStartRadius, flattenedStyle.borderStartEndRadius, flattenedStyle.borderEndStartRadius, flattenedStyle.borderEndEndRadius].some(radius => typeof radius === 'number' && radius > 0));
|
|
175
175
|
const shouldWrapNative = hasOverlay || hasRoundedCorners;
|
|
176
|
+
const hasBorderWidth = Boolean(flattenedStyle && [flattenedStyle.borderWidth, flattenedStyle.borderTopWidth, flattenedStyle.borderRightWidth, flattenedStyle.borderBottomWidth, flattenedStyle.borderLeftWidth].some(width => typeof width === 'number' && width > 0));
|
|
177
|
+
const borderOverlayStyle = flattenedStyle && hasRoundedCorners && hasBorderWidth ? {
|
|
178
|
+
borderWidth: flattenedStyle.borderWidth,
|
|
179
|
+
borderTopWidth: flattenedStyle.borderTopWidth,
|
|
180
|
+
borderRightWidth: flattenedStyle.borderRightWidth,
|
|
181
|
+
borderBottomWidth: flattenedStyle.borderBottomWidth,
|
|
182
|
+
borderLeftWidth: flattenedStyle.borderLeftWidth,
|
|
183
|
+
borderColor: flattenedStyle.borderColor,
|
|
184
|
+
borderTopColor: flattenedStyle.borderTopColor,
|
|
185
|
+
borderRightColor: flattenedStyle.borderRightColor,
|
|
186
|
+
borderBottomColor: flattenedStyle.borderBottomColor,
|
|
187
|
+
borderLeftColor: flattenedStyle.borderLeftColor,
|
|
188
|
+
borderStyle: flattenedStyle.borderStyle,
|
|
189
|
+
borderRadius: flattenedStyle.borderRadius,
|
|
190
|
+
borderTopLeftRadius: flattenedStyle.borderTopLeftRadius,
|
|
191
|
+
borderTopRightRadius: flattenedStyle.borderTopRightRadius,
|
|
192
|
+
borderBottomLeftRadius: flattenedStyle.borderBottomLeftRadius,
|
|
193
|
+
borderBottomRightRadius: flattenedStyle.borderBottomRightRadius,
|
|
194
|
+
borderStartStartRadius: flattenedStyle.borderStartStartRadius,
|
|
195
|
+
borderStartEndRadius: flattenedStyle.borderStartEndRadius,
|
|
196
|
+
borderEndStartRadius: flattenedStyle.borderEndStartRadius,
|
|
197
|
+
borderEndEndRadius: flattenedStyle.borderEndEndRadius,
|
|
198
|
+
zIndex: 1
|
|
199
|
+
} : undefined;
|
|
176
200
|
const native = /*#__PURE__*/createElement(NativeOneKeyImage, {
|
|
177
201
|
...viewProps,
|
|
178
202
|
...nativeCallbacks,
|
|
@@ -192,7 +216,7 @@ export function OneKeyImage({
|
|
|
192
216
|
});
|
|
193
217
|
if (!shouldWrapNative) return native;
|
|
194
218
|
return /*#__PURE__*/_jsxs(View, {
|
|
195
|
-
style: [style, styles.overlayContainer],
|
|
219
|
+
style: [style, styles.overlayContainer, borderOverlayStyle ? styles.borderlessContainer : undefined],
|
|
196
220
|
collapsable: false,
|
|
197
221
|
children: [native, effectiveState === 'loading' && placeholder != null ? /*#__PURE__*/_jsx(View, {
|
|
198
222
|
pointerEvents: "none",
|
|
@@ -202,6 +226,9 @@ export function OneKeyImage({
|
|
|
202
226
|
pointerEvents: "none",
|
|
203
227
|
style: StyleSheet.absoluteFill,
|
|
204
228
|
children: fallback
|
|
229
|
+
}) : null, borderOverlayStyle ? /*#__PURE__*/_jsx(View, {
|
|
230
|
+
pointerEvents: "none",
|
|
231
|
+
style: [StyleSheet.absoluteFill, borderOverlayStyle]
|
|
205
232
|
}) : null]
|
|
206
233
|
});
|
|
207
234
|
}
|
|
@@ -228,6 +255,13 @@ export const OneKeyImageCache = {
|
|
|
228
255
|
const styles = StyleSheet.create({
|
|
229
256
|
overlayContainer: {
|
|
230
257
|
overflow: 'hidden'
|
|
258
|
+
},
|
|
259
|
+
borderlessContainer: {
|
|
260
|
+
borderWidth: 0,
|
|
261
|
+
borderTopWidth: 0,
|
|
262
|
+
borderRightWidth: 0,
|
|
263
|
+
borderBottomWidth: 0,
|
|
264
|
+
borderLeftWidth: 0
|
|
231
265
|
}
|
|
232
266
|
});
|
|
233
267
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-image",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.124",
|
|
4
4
|
"description": "High-performance native image view for OneKey",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"typescript": "^5.9.2"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
|
-
"@onekeyfe/react-native-skeleton": "3.0.
|
|
84
|
+
"@onekeyfe/react-native-skeleton": "3.0.124",
|
|
85
85
|
"react": "*",
|
|
86
86
|
"react-native": "*",
|
|
87
87
|
"react-native-nitro-modules": "0.37.0"
|
package/src/index.tsx
CHANGED
|
@@ -325,6 +325,42 @@ export function OneKeyImage({
|
|
|
325
325
|
].some((radius) => typeof radius === 'number' && radius > 0)
|
|
326
326
|
);
|
|
327
327
|
const shouldWrapNative = hasOverlay || hasRoundedCorners;
|
|
328
|
+
const hasBorderWidth = Boolean(
|
|
329
|
+
flattenedStyle &&
|
|
330
|
+
[
|
|
331
|
+
flattenedStyle.borderWidth,
|
|
332
|
+
flattenedStyle.borderTopWidth,
|
|
333
|
+
flattenedStyle.borderRightWidth,
|
|
334
|
+
flattenedStyle.borderBottomWidth,
|
|
335
|
+
flattenedStyle.borderLeftWidth,
|
|
336
|
+
].some((width) => typeof width === 'number' && width > 0)
|
|
337
|
+
);
|
|
338
|
+
const borderOverlayStyle =
|
|
339
|
+
flattenedStyle && hasRoundedCorners && hasBorderWidth
|
|
340
|
+
? {
|
|
341
|
+
borderWidth: flattenedStyle.borderWidth,
|
|
342
|
+
borderTopWidth: flattenedStyle.borderTopWidth,
|
|
343
|
+
borderRightWidth: flattenedStyle.borderRightWidth,
|
|
344
|
+
borderBottomWidth: flattenedStyle.borderBottomWidth,
|
|
345
|
+
borderLeftWidth: flattenedStyle.borderLeftWidth,
|
|
346
|
+
borderColor: flattenedStyle.borderColor,
|
|
347
|
+
borderTopColor: flattenedStyle.borderTopColor,
|
|
348
|
+
borderRightColor: flattenedStyle.borderRightColor,
|
|
349
|
+
borderBottomColor: flattenedStyle.borderBottomColor,
|
|
350
|
+
borderLeftColor: flattenedStyle.borderLeftColor,
|
|
351
|
+
borderStyle: flattenedStyle.borderStyle,
|
|
352
|
+
borderRadius: flattenedStyle.borderRadius,
|
|
353
|
+
borderTopLeftRadius: flattenedStyle.borderTopLeftRadius,
|
|
354
|
+
borderTopRightRadius: flattenedStyle.borderTopRightRadius,
|
|
355
|
+
borderBottomLeftRadius: flattenedStyle.borderBottomLeftRadius,
|
|
356
|
+
borderBottomRightRadius: flattenedStyle.borderBottomRightRadius,
|
|
357
|
+
borderStartStartRadius: flattenedStyle.borderStartStartRadius,
|
|
358
|
+
borderStartEndRadius: flattenedStyle.borderStartEndRadius,
|
|
359
|
+
borderEndStartRadius: flattenedStyle.borderEndStartRadius,
|
|
360
|
+
borderEndEndRadius: flattenedStyle.borderEndEndRadius,
|
|
361
|
+
zIndex: 1,
|
|
362
|
+
}
|
|
363
|
+
: undefined;
|
|
328
364
|
|
|
329
365
|
const native = createElement(NativeOneKeyImage, {
|
|
330
366
|
...viewProps,
|
|
@@ -350,7 +386,14 @@ export function OneKeyImage({
|
|
|
350
386
|
if (!shouldWrapNative) return native;
|
|
351
387
|
|
|
352
388
|
return (
|
|
353
|
-
<View
|
|
389
|
+
<View
|
|
390
|
+
style={[
|
|
391
|
+
style,
|
|
392
|
+
styles.overlayContainer,
|
|
393
|
+
borderOverlayStyle ? styles.borderlessContainer : undefined,
|
|
394
|
+
]}
|
|
395
|
+
collapsable={false}
|
|
396
|
+
>
|
|
354
397
|
{native}
|
|
355
398
|
{effectiveState === 'loading' && placeholder != null ? (
|
|
356
399
|
<View pointerEvents="none" style={StyleSheet.absoluteFill}>
|
|
@@ -362,6 +405,12 @@ export function OneKeyImage({
|
|
|
362
405
|
{fallback}
|
|
363
406
|
</View>
|
|
364
407
|
) : null}
|
|
408
|
+
{borderOverlayStyle ? (
|
|
409
|
+
<View
|
|
410
|
+
pointerEvents="none"
|
|
411
|
+
style={[StyleSheet.absoluteFill, borderOverlayStyle]}
|
|
412
|
+
/>
|
|
413
|
+
) : null}
|
|
365
414
|
</View>
|
|
366
415
|
);
|
|
367
416
|
}
|
|
@@ -401,4 +450,11 @@ const styles = StyleSheet.create({
|
|
|
401
450
|
overlayContainer: {
|
|
402
451
|
overflow: 'hidden',
|
|
403
452
|
},
|
|
453
|
+
borderlessContainer: {
|
|
454
|
+
borderWidth: 0,
|
|
455
|
+
borderTopWidth: 0,
|
|
456
|
+
borderRightWidth: 0,
|
|
457
|
+
borderBottomWidth: 0,
|
|
458
|
+
borderLeftWidth: 0,
|
|
459
|
+
},
|
|
404
460
|
});
|