@onekeyfe/react-native-image 3.0.153 → 3.0.155

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.
@@ -243,7 +243,11 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
243
243
  guard !hostView.bounds.isEmpty else {
244
244
  cancelCurrentRequest(invalidateGeneration: true)
245
245
  lastRequestSignature = nil
246
- showLoading(requestIsActive: false, letLibraryStartIndicator: false)
246
+ // A memory-cached image shown from `identityDidChange` before layout
247
+ // stays up; layout re-runs this load with real bounds and confirms it.
248
+ if displayState != .image {
249
+ showLoading(requestIsActive: false, letLibraryStartIndicator: false)
250
+ }
247
251
  return
248
252
  }
249
253
  guard let rawString = sourceUri, !rawString.isEmpty else {
@@ -545,7 +549,13 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
545
549
  }
546
550
 
547
551
  private func showMemoryCachedImageIfAvailable(requestIsActive: Bool) -> Bool {
548
- guard cachePolicy == .memory || cachePolicy == .memoryDisk,
552
+ // JS leaves `cachePolicy` unset for the common case and Fabric then hands
553
+ // Nitro a nil, which the request path already treats as memory-disk; the
554
+ // probe must agree, otherwise a nil policy skips the memory lookup entirely
555
+ // and every mount pays the async pipeline round trip (an icon skeleton
556
+ // frame) for an image that is already decoded in memory.
557
+ let effectivePolicy = cachePolicy ?? .memoryDisk
558
+ guard effectivePolicy == .memory || effectivePolicy == .memoryDisk,
549
559
  let sourceUri,
550
560
  let rawURL = URL(string: sourceUri) else {
551
561
  return false
@@ -565,11 +575,19 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
565
575
  hasCustomIdentity: hasCustomIdentity
566
576
  )
567
577
  : rawURL
568
- let thumbnailPixelSize = OneKeyImageDecodeSizing.thumbnailPixelSize(
569
- viewSize: hostView.bounds.size,
570
- scale: screenScale,
571
- contentFit: contentFit ?? .cover
572
- )
578
+ // Before layout the bounds are empty; fall back to the `resizeWidth` hint so
579
+ // the probe key matches the prewarm / post-layout request instead of
580
+ // missing on every mount frame.
581
+ let thumbnailPixelSize = OneKeyImageDecodeSizing.probeViewSize(
582
+ bounds: hostView.bounds.size,
583
+ resizeWidth: resizeWidth
584
+ ).flatMap {
585
+ OneKeyImageDecodeSizing.thumbnailPixelSize(
586
+ viewSize: $0,
587
+ scale: screenScale,
588
+ contentFit: contentFit ?? .cover
589
+ )
590
+ }
573
591
  func memoryCachedImage(for url: URL) -> UIImage? {
574
592
  let context = OneKeyImageRequestContext.make(
575
593
  headersJson: sourceHeadersJson,
@@ -610,6 +610,19 @@ enum OneKeyImageDecodeSizing {
610
610
  }
611
611
  }
612
612
 
613
+ /// The logical size to probe the memory cache with. Before the first layout
614
+ /// the host view has no bounds, but the JS side already knows the display
615
+ /// size through `resizeWidth` (the same hint the prewarm used), so a square
616
+ /// from it yields the decode-thumbnail key the laid-out view will ask for.
617
+ /// A cached image can then show on the mount frame instead of a skeleton
618
+ /// until layout re-probes.
619
+ static func probeViewSize(bounds: CGSize, resizeWidth: Double?) -> CGSize? {
620
+ if bounds.width.isFinite, bounds.height.isFinite, bounds.width > 0, bounds.height > 0 {
621
+ return bounds
622
+ }
623
+ return logicalViewSize(width: resizeWidth, height: nil)
624
+ }
625
+
613
626
  static func thumbnailPixelSize(
614
627
  viewSize: CGSize,
615
628
  scale: CGFloat,
@@ -310,6 +310,33 @@ final class OneKeyImageInfrastructureTests: XCTestCase {
310
310
  )
311
311
  }
312
312
 
313
+ func testMemoryProbeFallsBackToResizeWidthBeforeLayout() throws {
314
+ // Laid out: the real bounds win, even with a hint.
315
+ XCTAssertEqual(
316
+ OneKeyImageDecodeSizing.probeViewSize(
317
+ bounds: CGSize(width: 40, height: 64),
318
+ resizeWidth: 32
319
+ ),
320
+ CGSize(width: 40, height: 64)
321
+ )
322
+ // Not laid out yet: a square from the hint, matching the prewarm key.
323
+ let probe = try XCTUnwrap(
324
+ OneKeyImageDecodeSizing.probeViewSize(bounds: .zero, resizeWidth: 40)
325
+ )
326
+ XCTAssertEqual(probe, CGSize(width: 40, height: 40))
327
+ XCTAssertEqual(
328
+ OneKeyImageDecodeSizing.thumbnailPixelSize(viewSize: probe, scale: 3, contentFit: .cover),
329
+ OneKeyImageDecodeSizing.thumbnailPixelSize(
330
+ viewSize: try XCTUnwrap(OneKeyImageDecodeSizing.logicalViewSize(width: 40, height: nil)),
331
+ scale: 3,
332
+ contentFit: .cover
333
+ )
334
+ )
335
+ // No bounds and no usable hint: nothing to probe with.
336
+ XCTAssertNil(OneKeyImageDecodeSizing.probeViewSize(bounds: .zero, resizeWidth: nil))
337
+ XCTAssertNil(OneKeyImageDecodeSizing.probeViewSize(bounds: .zero, resizeWidth: 0))
338
+ }
339
+
313
340
  func testNoSizePreloadUsesCappedDecodeTarget() {
314
341
  XCTAssertEqual(HybridOneKeyImageCache.maximumConcurrentPreloads, 4)
315
342
  XCTAssertEqual(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-image",
3
- "version": "3.0.153",
3
+ "version": "3.0.155",
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.153",
84
+ "@onekeyfe/react-native-skeleton": "3.0.155",
85
85
  "react": "*",
86
86
  "react-native": "*",
87
87
  "react-native-nitro-modules": "0.37.0"