@onekeyfe/react-native-image 3.0.156 → 3.0.158
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/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImage.kt +12 -0
- package/ios/OneKeyImage.swift +114 -18
- package/ios/OneKeyImageRequestContext.swift +55 -0
- package/ios/tests/OneKeyImageInfrastructureTests.swift +134 -0
- package/lib/module/index.js +3 -1
- package/lib/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.cpp +9 -0
- package/lib/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.hpp +2 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridOneKeyImageStateUpdater.cpp +5 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyImageSpec.kt +6 -0
- package/lib/nitrogen/generated/ios/c++/HybridOneKeyImageSpecSwift.hpp +7 -0
- package/lib/nitrogen/generated/ios/c++/views/HybridOneKeyImageComponent.mm +6 -0
- package/lib/nitrogen/generated/ios/swift/HybridOneKeyImageSpec.swift +1 -0
- package/lib/nitrogen/generated/ios/swift/HybridOneKeyImageSpec_cxx.swift +24 -0
- package/lib/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.cpp +2 -0
- package/lib/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.hpp +2 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.cpp +2 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.hpp +3 -0
- package/lib/nitrogen/generated/shared/json/OneKeyImageConfig.json +1 -0
- package/lib/typescript/src/OneKeyImage.nitro.d.ts +6 -0
- package/lib/typescript/src/index.d.ts +1 -1
- package/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.cpp +9 -0
- package/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.hpp +2 -0
- package/nitrogen/generated/android/c++/views/JHybridOneKeyImageStateUpdater.cpp +5 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyImageSpec.kt +6 -0
- package/nitrogen/generated/ios/c++/HybridOneKeyImageSpecSwift.hpp +7 -0
- package/nitrogen/generated/ios/c++/views/HybridOneKeyImageComponent.mm +6 -0
- package/nitrogen/generated/ios/swift/HybridOneKeyImageSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridOneKeyImageSpec_cxx.swift +24 -0
- package/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.hpp +2 -0
- package/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.cpp +2 -0
- package/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.hpp +3 -0
- package/nitrogen/generated/shared/json/OneKeyImageConfig.json +1 -0
- package/package.json +2 -2
- package/src/OneKeyImage.nitro.ts +6 -0
- package/src/index.tsx +3 -1
|
@@ -180,6 +180,7 @@ internal fun oneKeyImageRequestSignature(
|
|
|
180
180
|
height: Int,
|
|
181
181
|
density: Float,
|
|
182
182
|
resizeWidth: Double? = null,
|
|
183
|
+
resizeHeight: Double? = null,
|
|
183
184
|
): String = listOf(
|
|
184
185
|
rawUrl,
|
|
185
186
|
sourceHeadersJson.orEmpty(),
|
|
@@ -190,6 +191,7 @@ internal fun oneKeyImageRequestSignature(
|
|
|
190
191
|
overscan.toString(),
|
|
191
192
|
round.toString(),
|
|
192
193
|
resizeWidth.toString(),
|
|
194
|
+
resizeHeight.toString(),
|
|
193
195
|
width.toString(),
|
|
194
196
|
height.toString(),
|
|
195
197
|
density.toString(),
|
|
@@ -304,6 +306,14 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
|
|
|
304
306
|
field = value
|
|
305
307
|
requestIdentityChanged()
|
|
306
308
|
}
|
|
309
|
+
// Layout hint pair with resizeWidth; only the iOS pre-layout memory probe
|
|
310
|
+
// consumes it today, Android keys the request on the measured view size.
|
|
311
|
+
override var resizeHeight: Double? = null
|
|
312
|
+
set(value) {
|
|
313
|
+
if (field == value) return
|
|
314
|
+
field = value
|
|
315
|
+
requestIdentityChanged()
|
|
316
|
+
}
|
|
307
317
|
override var loadingStrategy: OneKeyImageLoadingStrategy? = OneKeyImageLoadingStrategy.STATIC
|
|
308
318
|
set(value) {
|
|
309
319
|
field = value
|
|
@@ -403,6 +413,7 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
|
|
|
403
413
|
optimizeTos = true
|
|
404
414
|
round = false
|
|
405
415
|
resizeWidth = null
|
|
416
|
+
resizeHeight = null
|
|
406
417
|
overscan = 1.1
|
|
407
418
|
loadingStrategy = OneKeyImageLoadingStrategy.STATIC
|
|
408
419
|
placeholderColor = null
|
|
@@ -477,6 +488,7 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
|
|
|
477
488
|
height = hostView.height,
|
|
478
489
|
density = hostView.resources.displayMetrics.density,
|
|
479
490
|
resizeWidth = resizeWidth,
|
|
491
|
+
resizeHeight = resizeHeight,
|
|
480
492
|
)
|
|
481
493
|
if (!force && signature == lastSignature) return
|
|
482
494
|
lastSignature = signature
|
package/ios/OneKeyImage.swift
CHANGED
|
@@ -115,6 +115,9 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
115
115
|
private var requestActive = false
|
|
116
116
|
private var isResetting = false
|
|
117
117
|
private var displayState = DisplayState.loading
|
|
118
|
+
/// `.loading` with the previous image kept on screen as the placeholder
|
|
119
|
+
/// (see `showLoading(preservedImage:)`).
|
|
120
|
+
private var isPreservingDisplayedImage = false
|
|
118
121
|
private var requestStartedAt: CFTimeInterval?
|
|
119
122
|
|
|
120
123
|
var view: UIView { hostView }
|
|
@@ -150,6 +153,11 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
150
153
|
if !Self.equalOptionalDouble(resizeWidth, oldValue) { identityDidChange() }
|
|
151
154
|
}
|
|
152
155
|
}
|
|
156
|
+
var resizeHeight: Double? {
|
|
157
|
+
didSet {
|
|
158
|
+
if !Self.equalOptionalDouble(resizeHeight, oldValue) { identityDidChange() }
|
|
159
|
+
}
|
|
160
|
+
}
|
|
153
161
|
var overscan: Double? = 1.1 {
|
|
154
162
|
didSet {
|
|
155
163
|
if !Self.equalOptionalDouble(overscan, oldValue) { identityDidChange() }
|
|
@@ -260,7 +268,14 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
260
268
|
guard !hostView.bounds.isEmpty else {
|
|
261
269
|
cancelCurrentRequest(invalidateGeneration: true)
|
|
262
270
|
lastRequestSignature = nil
|
|
263
|
-
|
|
271
|
+
// A memory-cached image shown from `identityDidChange` before layout
|
|
272
|
+
// stays up; layout re-runs this load with real bounds and confirms it.
|
|
273
|
+
if !Self.shouldPreserveDisplayedImage(
|
|
274
|
+
isShowingImage: displayState == .image,
|
|
275
|
+
hasImage: hostView.image != nil
|
|
276
|
+
) {
|
|
277
|
+
showLoading(requestIsActive: false, letLibraryStartIndicator: false)
|
|
278
|
+
}
|
|
264
279
|
return
|
|
265
280
|
}
|
|
266
281
|
guard let rawString = sourceUri, !rawString.isEmpty else {
|
|
@@ -280,6 +295,7 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
280
295
|
contentFit?.stringValue ?? "cover",
|
|
281
296
|
String(optimizeTos ?? true),
|
|
282
297
|
resizeWidth.map(String.init(describing:)) ?? "",
|
|
298
|
+
resizeHeight.map(String.init(describing:)) ?? "",
|
|
283
299
|
String(overscan ?? 1.1),
|
|
284
300
|
String(Int(hostView.bounds.width.rounded())),
|
|
285
301
|
String(Int(hostView.bounds.height.rounded())),
|
|
@@ -295,7 +311,20 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
295
311
|
onLoadStart?()
|
|
296
312
|
guard Self.isCurrentRequestGeneration(generation, current: requestGeneration) else { return }
|
|
297
313
|
if showMemoryCachedImageIfAvailable(requestIsActive: true) { return }
|
|
298
|
-
|
|
314
|
+
// The pre-layout probe may have shown this identity from another thumbnail
|
|
315
|
+
// key (a preload's, or an earlier layout size). Keep it on screen as the
|
|
316
|
+
// placeholder while the exact rendition loads: blanking to a skeleton and
|
|
317
|
+
// redrawing is worse than the skeleton it replaced.
|
|
318
|
+
let preservedImage =
|
|
319
|
+
Self.shouldPreserveDisplayedImage(
|
|
320
|
+
isShowingImage: displayState == .image,
|
|
321
|
+
hasImage: hostView.image != nil
|
|
322
|
+
) ? hostView.image : nil
|
|
323
|
+
showLoading(
|
|
324
|
+
requestIsActive: true,
|
|
325
|
+
letLibraryStartIndicator: true,
|
|
326
|
+
preservedImage: preservedImage
|
|
327
|
+
)
|
|
299
328
|
|
|
300
329
|
if let violation = OneKeyImageSafetyPolicy.preflight(source: rawString) {
|
|
301
330
|
finishWithError(violation, generation: generation)
|
|
@@ -311,8 +340,11 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
311
340
|
}
|
|
312
341
|
|
|
313
342
|
let hasCustomIdentity = OneKeyImageRequestContext.headers(from: sourceHeadersJson) != nil
|
|
314
|
-
let displaySize =
|
|
315
|
-
|
|
343
|
+
let displaySize = OneKeyImageDecodeSizing.tosDisplaySize(
|
|
344
|
+
bounds: hostView.bounds.size,
|
|
345
|
+
resizeWidth: resizeWidth,
|
|
346
|
+
resizeHeight: resizeHeight
|
|
347
|
+
) ?? 0
|
|
316
348
|
let requestURL =
|
|
317
349
|
(optimizeTos ?? true)
|
|
318
350
|
? OneKeyTosURL.optimized(
|
|
@@ -327,7 +359,8 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
327
359
|
url: requestURL,
|
|
328
360
|
rawURL: rawURL,
|
|
329
361
|
generation: generation,
|
|
330
|
-
mayFallbackToRaw: requestURL != rawURL
|
|
362
|
+
mayFallbackToRaw: requestURL != rawURL,
|
|
363
|
+
placeholderImage: preservedImage
|
|
331
364
|
)
|
|
332
365
|
}
|
|
333
366
|
|
|
@@ -335,7 +368,8 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
335
368
|
url: URL,
|
|
336
369
|
rawURL: URL,
|
|
337
370
|
generation: UInt64,
|
|
338
|
-
mayFallbackToRaw: Bool
|
|
371
|
+
mayFallbackToRaw: Bool,
|
|
372
|
+
placeholderImage: UIImage? = nil
|
|
339
373
|
) {
|
|
340
374
|
guard Self.isCurrentRequestGeneration(generation, current: requestGeneration) else { return }
|
|
341
375
|
let rawScreenScale: CGFloat = hostView.window?.screen.scale ?? UIScreen.main.scale
|
|
@@ -363,7 +397,9 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
363
397
|
)
|
|
364
398
|
hostView.sd_internalSetImage(
|
|
365
399
|
with: url,
|
|
366
|
-
|
|
400
|
+
// SDWebImage resets the image view when it starts a load; hand it the
|
|
401
|
+
// preserved image so the view keeps showing it until the load lands.
|
|
402
|
+
placeholderImage: placeholderImage,
|
|
367
403
|
options: OneKeyImageRequestContext.renderOptions,
|
|
368
404
|
context: context,
|
|
369
405
|
setImageBlock: { [weak self] image, _, _, _ in
|
|
@@ -409,6 +445,7 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
409
445
|
guard self.claimTerminal(generation) else { return }
|
|
410
446
|
self.requestActive = false
|
|
411
447
|
self.displayState = .image
|
|
448
|
+
self.isPreservingDisplayedImage = false
|
|
412
449
|
self.skeletonIndicator.stopAnimatingIndicator()
|
|
413
450
|
self.fallbackLayer.isHidden = true
|
|
414
451
|
self.hostView.backgroundColor = .clear
|
|
@@ -517,6 +554,7 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
517
554
|
optimizeTos = true
|
|
518
555
|
round = false
|
|
519
556
|
resizeWidth = nil
|
|
557
|
+
resizeHeight = nil
|
|
520
558
|
overscan = 1.1
|
|
521
559
|
loadingStrategy = .static
|
|
522
560
|
placeholderColor = nil
|
|
@@ -547,23 +585,56 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
547
585
|
guard Self.isCurrentRequestGeneration(generation, current: requestGeneration) else {
|
|
548
586
|
return
|
|
549
587
|
}
|
|
588
|
+
// Also the placeholder write path (SDWebImage hands the preserved image
|
|
589
|
+
// back through setImageBlock when the load starts), so the preservation
|
|
590
|
+
// flag is NOT touched here; it ends with the terminal callbacks.
|
|
550
591
|
hostView.image = image
|
|
551
592
|
}
|
|
552
593
|
|
|
553
594
|
private func showLoading(
|
|
554
595
|
requestIsActive: Bool,
|
|
555
|
-
letLibraryStartIndicator: Bool
|
|
596
|
+
letLibraryStartIndicator: Bool,
|
|
597
|
+
preservedImage: UIImage? = nil
|
|
556
598
|
) {
|
|
557
599
|
displayState = .loading
|
|
558
600
|
requestActive = requestIsActive
|
|
559
601
|
resetImageTransition()
|
|
560
|
-
hostView.image = nil
|
|
561
602
|
fallbackLayer.isHidden = true
|
|
603
|
+
if let preservedImage {
|
|
604
|
+
// Same identity, different thumbnail key: the image already on screen
|
|
605
|
+
// stands in for the placeholder, so no blank frame and no indicator.
|
|
606
|
+
// The indicator is unmounted, not just stopped: SDWebImage restarts a
|
|
607
|
+
// mounted `sd_imageIndicator` when the load begins, and `applyVariant`
|
|
608
|
+
// must not reinstall it while this state lasts.
|
|
609
|
+
isPreservingDisplayedImage = true
|
|
610
|
+
hostView.image = preservedImage
|
|
611
|
+
skeletonIndicator.stopAnimatingIndicator()
|
|
612
|
+
if hostView.sd_imageIndicator != nil {
|
|
613
|
+
hostView.sd_imageIndicator = nil
|
|
614
|
+
}
|
|
615
|
+
hostView.backgroundColor = .clear
|
|
616
|
+
return
|
|
617
|
+
}
|
|
618
|
+
isPreservingDisplayedImage = false
|
|
619
|
+
hostView.image = nil
|
|
562
620
|
applyLoadingAppearance(letLibraryStartIndicator: letLibraryStartIndicator)
|
|
563
621
|
}
|
|
564
622
|
|
|
623
|
+
/// Whether the image currently on screen should stay up while a load for the
|
|
624
|
+
/// SAME identity runs (bounds not laid out yet, or the laid-out request key
|
|
625
|
+
/// differs from the one the pre-layout probe hit). Pure for testability.
|
|
626
|
+
static func shouldPreserveDisplayedImage(isShowingImage: Bool, hasImage: Bool) -> Bool {
|
|
627
|
+
isShowingImage && hasImage
|
|
628
|
+
}
|
|
629
|
+
|
|
565
630
|
private func showMemoryCachedImageIfAvailable(requestIsActive: Bool) -> Bool {
|
|
566
|
-
|
|
631
|
+
// JS leaves `cachePolicy` unset for the common case and Fabric then hands
|
|
632
|
+
// Nitro a nil, which the request path already treats as memory-disk; the
|
|
633
|
+
// probe must agree, otherwise a nil policy skips the memory lookup entirely
|
|
634
|
+
// and every mount pays the async pipeline round trip (an icon skeleton
|
|
635
|
+
// frame) for an image that is already decoded in memory.
|
|
636
|
+
let effectivePolicy = cachePolicy ?? .memoryDisk
|
|
637
|
+
guard effectivePolicy == .memory || effectivePolicy == .memoryDisk,
|
|
567
638
|
let sourceUri,
|
|
568
639
|
let rawURL = URL(string: sourceUri) else {
|
|
569
640
|
return false
|
|
@@ -571,8 +642,11 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
571
642
|
let rawScreenScale: CGFloat = hostView.window?.screen.scale ?? UIScreen.main.scale
|
|
572
643
|
let screenScale = min(max(rawScreenScale, 1), 3)
|
|
573
644
|
let hasCustomIdentity = OneKeyImageRequestContext.headers(from: sourceHeadersJson) != nil
|
|
574
|
-
let displaySize =
|
|
575
|
-
|
|
645
|
+
let displaySize = OneKeyImageDecodeSizing.tosDisplaySize(
|
|
646
|
+
bounds: hostView.bounds.size,
|
|
647
|
+
resizeWidth: resizeWidth,
|
|
648
|
+
resizeHeight: resizeHeight
|
|
649
|
+
) ?? 0
|
|
576
650
|
let requestURL =
|
|
577
651
|
(optimizeTos ?? true)
|
|
578
652
|
? OneKeyTosURL.optimized(
|
|
@@ -583,12 +657,21 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
583
657
|
hasCustomIdentity: hasCustomIdentity
|
|
584
658
|
)
|
|
585
659
|
: rawURL
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
660
|
+
// The thumbnail size is part of the cache key. Laid out, it is this view's
|
|
661
|
+
// own request key; before layout it comes from the resize hints, under
|
|
662
|
+
// this view's contentFit and under `.cover` (a preload's key). With no
|
|
663
|
+
// bounds and no hint the un-thumbnailed key is the only thing to try.
|
|
664
|
+
let thumbnailCandidates: [CGSize?] = {
|
|
665
|
+
let sizes = OneKeyImageDecodeSizing.probeThumbnailPixelSizes(
|
|
666
|
+
bounds: hostView.bounds.size,
|
|
667
|
+
resizeWidth: resizeWidth,
|
|
668
|
+
resizeHeight: resizeHeight,
|
|
669
|
+
scale: screenScale,
|
|
670
|
+
contentFit: contentFit ?? .cover
|
|
671
|
+
)
|
|
672
|
+
return sizes.isEmpty ? [nil] : sizes
|
|
673
|
+
}()
|
|
674
|
+
func memoryCachedImage(for url: URL, thumbnailPixelSize: CGSize?) -> UIImage? {
|
|
592
675
|
let context = OneKeyImageRequestContext.make(
|
|
593
676
|
headersJson: sourceHeadersJson,
|
|
594
677
|
cachePolicy: cachePolicy ?? .memoryDisk,
|
|
@@ -603,6 +686,14 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
603
686
|
}
|
|
604
687
|
return cache.imageFromMemoryCache(forKey: key)
|
|
605
688
|
}
|
|
689
|
+
func memoryCachedImage(for url: URL) -> UIImage? {
|
|
690
|
+
for thumbnailPixelSize in thumbnailCandidates {
|
|
691
|
+
if let image = memoryCachedImage(for: url, thumbnailPixelSize: thumbnailPixelSize) {
|
|
692
|
+
return image
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return nil
|
|
696
|
+
}
|
|
606
697
|
guard let image = memoryCachedImage(for: requestURL)
|
|
607
698
|
?? (requestURL == rawURL ? nil : memoryCachedImage(for: rawURL)) else {
|
|
608
699
|
return false
|
|
@@ -610,6 +701,7 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
610
701
|
let generation = requestGeneration
|
|
611
702
|
if requestIsActive, !claimTerminal(generation) { return true }
|
|
612
703
|
displayState = .image
|
|
704
|
+
isPreservingDisplayedImage = false
|
|
613
705
|
requestActive = false
|
|
614
706
|
skeletonIndicator.stopAnimatingIndicator()
|
|
615
707
|
fallbackLayer.isHidden = true
|
|
@@ -647,6 +739,7 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
647
739
|
private func showTerminalState(_ state: DisplayState) {
|
|
648
740
|
displayState = state
|
|
649
741
|
requestActive = false
|
|
742
|
+
isPreservingDisplayedImage = false
|
|
650
743
|
skeletonIndicator.stopAnimatingIndicator()
|
|
651
744
|
resetImageTransition()
|
|
652
745
|
hostView.image = nil
|
|
@@ -664,6 +757,9 @@ final class HybridOneKeyImage: HybridOneKeyImageSpec, RecyclableView {
|
|
|
664
757
|
case .image:
|
|
665
758
|
break
|
|
666
759
|
case .loading:
|
|
760
|
+
// A preserved image is the placeholder; re-applying the loading look
|
|
761
|
+
// would cover it with the skeleton / placeholder color again.
|
|
762
|
+
if isPreservingDisplayedImage { break }
|
|
667
763
|
applyLoadingAppearance(letLibraryStartIndicator: false)
|
|
668
764
|
case .error, .fallback:
|
|
669
765
|
let hidesTerminalState = loadingStrategy == .none
|
|
@@ -610,6 +610,61 @@ enum OneKeyImageDecodeSizing {
|
|
|
610
610
|
}
|
|
611
611
|
}
|
|
612
612
|
|
|
613
|
+
/// The logical display size the TOS rendition is picked for. A preload picks
|
|
614
|
+
/// it from the long edge of its `resizeWidth`/`resizeHeight` hint, so a view
|
|
615
|
+
/// carrying the same hint must do the same (before AND after layout) or its
|
|
616
|
+
/// request URL, and with it the cache key, diverges from what the preload
|
|
617
|
+
/// stored. Without a hint the laid-out bounds decide; nil before layout.
|
|
618
|
+
static func tosDisplaySize(
|
|
619
|
+
bounds: CGSize,
|
|
620
|
+
resizeWidth: Double?,
|
|
621
|
+
resizeHeight: Double?
|
|
622
|
+
) -> CGFloat? {
|
|
623
|
+
if let hint = logicalViewSize(width: resizeWidth, height: resizeHeight) {
|
|
624
|
+
return max(hint.width, hint.height)
|
|
625
|
+
}
|
|
626
|
+
let edge = max(bounds.width, bounds.height)
|
|
627
|
+
return edge.isFinite && edge > 0 ? edge : nil
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/// Decode-thumbnail sizes worth probing the memory cache with, most specific
|
|
631
|
+
/// first. The thumbnail size is part of the SDWebImage cache key, so a probe
|
|
632
|
+
/// only hits when it reproduces the key some earlier load stored under.
|
|
633
|
+
///
|
|
634
|
+
/// - Laid out: exactly the size this view's own request uses (`bounds` +
|
|
635
|
+
/// `contentFit`), nothing else.
|
|
636
|
+
/// - Before layout the bounds are empty, but the JS side already knows the
|
|
637
|
+
/// display size through the `resizeWidth` / `resizeHeight` hints (the same
|
|
638
|
+
/// fields a preload takes). Two keys can hold the image then: the one the
|
|
639
|
+
/// laid-out request will ask for (hint + this view's `contentFit`) and the
|
|
640
|
+
/// one a preload stored (hint + `.cover`, see `HybridOneKeyImageCache`).
|
|
641
|
+
/// They coincide for a square hint or `.cover`; otherwise both are probed.
|
|
642
|
+
/// - No bounds and no usable hint: nothing to probe with.
|
|
643
|
+
static func probeThumbnailPixelSizes(
|
|
644
|
+
bounds: CGSize,
|
|
645
|
+
resizeWidth: Double?,
|
|
646
|
+
resizeHeight: Double?,
|
|
647
|
+
scale: CGFloat,
|
|
648
|
+
contentFit: OneKeyImageContentFit
|
|
649
|
+
) -> [CGSize] {
|
|
650
|
+
if bounds.width.isFinite, bounds.height.isFinite, bounds.width > 0, bounds.height > 0 {
|
|
651
|
+
return thumbnailPixelSize(viewSize: bounds, scale: scale, contentFit: contentFit)
|
|
652
|
+
.map { [$0] } ?? []
|
|
653
|
+
}
|
|
654
|
+
guard let hint = logicalViewSize(width: resizeWidth, height: resizeHeight) else {
|
|
655
|
+
return []
|
|
656
|
+
}
|
|
657
|
+
var sizes: [CGSize] = []
|
|
658
|
+
for fit in [contentFit, .cover] {
|
|
659
|
+
if let size = thumbnailPixelSize(viewSize: hint, scale: scale, contentFit: fit),
|
|
660
|
+
!sizes.contains(size)
|
|
661
|
+
{
|
|
662
|
+
sizes.append(size)
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return sizes
|
|
666
|
+
}
|
|
667
|
+
|
|
613
668
|
static func thumbnailPixelSize(
|
|
614
669
|
viewSize: CGSize,
|
|
615
670
|
scale: CGFloat,
|
|
@@ -310,6 +310,91 @@ final class OneKeyImageInfrastructureTests: XCTestCase {
|
|
|
310
310
|
)
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
+
// The pre-layout memory probe is only useful if it reproduces a key some
|
|
314
|
+
// earlier load stored under. These pin the probe against the two real key
|
|
315
|
+
// producers: the laid-out render request (bounds + contentFit) and the
|
|
316
|
+
// preload (logicalViewSize(resizeWidth, resizeHeight) + .cover).
|
|
317
|
+
private func renderKey(bounds: CGSize, fit: OneKeyImageContentFit) throws -> CGSize {
|
|
318
|
+
try XCTUnwrap(
|
|
319
|
+
OneKeyImageDecodeSizing.thumbnailPixelSize(viewSize: bounds, scale: 3, contentFit: fit)
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private func preloadKey(width: Double?, height: Double?) throws -> CGSize {
|
|
324
|
+
try renderKey(
|
|
325
|
+
bounds: try XCTUnwrap(OneKeyImageDecodeSizing.logicalViewSize(width: width, height: height)),
|
|
326
|
+
fit: .cover
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
func testLaidOutProbeUsesExactlyTheRenderRequestKey() throws {
|
|
331
|
+
let bounds = CGSize(width: 40, height: 64)
|
|
332
|
+
for fit in [OneKeyImageContentFit.cover, .contain, .fill, .center] {
|
|
333
|
+
XCTAssertEqual(
|
|
334
|
+
OneKeyImageDecodeSizing.probeThumbnailPixelSizes(
|
|
335
|
+
bounds: bounds, resizeWidth: 32, resizeHeight: 48, scale: 3, contentFit: fit
|
|
336
|
+
),
|
|
337
|
+
[try renderKey(bounds: bounds, fit: fit)],
|
|
338
|
+
"\(fit)"
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
func testPreLayoutSquareHintMatchesPreloadAndRenderKeys() throws {
|
|
344
|
+
// A token icon: resizeWidth only, laid out square, cover. One key, shared
|
|
345
|
+
// by the preload, the laid-out request and the probe.
|
|
346
|
+
let sizes = OneKeyImageDecodeSizing.probeThumbnailPixelSizes(
|
|
347
|
+
bounds: .zero, resizeWidth: 40, resizeHeight: nil, scale: 3, contentFit: .cover
|
|
348
|
+
)
|
|
349
|
+
XCTAssertEqual(sizes, [CGSize(width: 120, height: 120)])
|
|
350
|
+
XCTAssertEqual(sizes, [try preloadKey(width: 40, height: nil)])
|
|
351
|
+
XCTAssertEqual(sizes, [try renderKey(bounds: CGSize(width: 40, height: 40), fit: .cover)])
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
func testPreLayoutRectangularHintProbesRenderAndPreloadKeys() throws {
|
|
355
|
+
// The README preload shape (48x64 @3) under a non-cover view: the laid-out
|
|
356
|
+
// request key comes first, the preload's cover key second, no duplicates.
|
|
357
|
+
let sizes = OneKeyImageDecodeSizing.probeThumbnailPixelSizes(
|
|
358
|
+
bounds: .zero, resizeWidth: 48, resizeHeight: 64, scale: 3, contentFit: .contain
|
|
359
|
+
)
|
|
360
|
+
XCTAssertEqual(
|
|
361
|
+
sizes,
|
|
362
|
+
[
|
|
363
|
+
try renderKey(bounds: CGSize(width: 48, height: 64), fit: .contain),
|
|
364
|
+
try preloadKey(width: 48, height: 64),
|
|
365
|
+
]
|
|
366
|
+
)
|
|
367
|
+
XCTAssertEqual(sizes, [CGSize(width: 144, height: 192), CGSize(width: 192, height: 192)])
|
|
368
|
+
// Under cover both collapse to the preload key.
|
|
369
|
+
XCTAssertEqual(
|
|
370
|
+
OneKeyImageDecodeSizing.probeThumbnailPixelSizes(
|
|
371
|
+
bounds: .zero, resizeWidth: 48, resizeHeight: 64, scale: 3, contentFit: .cover
|
|
372
|
+
),
|
|
373
|
+
[try preloadKey(width: 48, height: 64)]
|
|
374
|
+
)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
func testPreLayoutProbeWithoutHintHasNothingToTry() {
|
|
378
|
+
XCTAssertEqual(
|
|
379
|
+
OneKeyImageDecodeSizing.probeThumbnailPixelSizes(
|
|
380
|
+
bounds: .zero, resizeWidth: nil, resizeHeight: nil, scale: 3, contentFit: .cover
|
|
381
|
+
),
|
|
382
|
+
[]
|
|
383
|
+
)
|
|
384
|
+
XCTAssertEqual(
|
|
385
|
+
OneKeyImageDecodeSizing.probeThumbnailPixelSizes(
|
|
386
|
+
bounds: .zero, resizeWidth: 0, resizeHeight: -1, scale: 3, contentFit: .cover
|
|
387
|
+
),
|
|
388
|
+
[]
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
func testDisplayedImageIsPreservedOnlyWhileShowingOne() {
|
|
393
|
+
XCTAssertTrue(HybridOneKeyImage.shouldPreserveDisplayedImage(isShowingImage: true, hasImage: true))
|
|
394
|
+
XCTAssertFalse(HybridOneKeyImage.shouldPreserveDisplayedImage(isShowingImage: true, hasImage: false))
|
|
395
|
+
XCTAssertFalse(HybridOneKeyImage.shouldPreserveDisplayedImage(isShowingImage: false, hasImage: true))
|
|
396
|
+
}
|
|
397
|
+
|
|
313
398
|
func testNoSizePreloadUsesCappedDecodeTarget() {
|
|
314
399
|
XCTAssertEqual(HybridOneKeyImageCache.maximumConcurrentPreloads, 4)
|
|
315
400
|
XCTAssertEqual(
|
|
@@ -544,6 +629,55 @@ final class OneKeyImageInfrastructureTests: XCTestCase {
|
|
|
544
629
|
)
|
|
545
630
|
}
|
|
546
631
|
|
|
632
|
+
func testRecycledViewForgetsBothResizeHints() throws {
|
|
633
|
+
let image = HybridOneKeyImage()
|
|
634
|
+
image.sourceUri = "https://example.com/hinted.png"
|
|
635
|
+
image.resizeWidth = 48
|
|
636
|
+
image.resizeHeight = 64
|
|
637
|
+
image.prepareForRecycle()
|
|
638
|
+
XCTAssertNil(image.resizeWidth)
|
|
639
|
+
XCTAssertNil(image.resizeHeight)
|
|
640
|
+
XCTAssertNil(image.sourceUri)
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
func testViewAndPreloadPickTheSameTosRenditionForOneHint() throws {
|
|
644
|
+
// Preload: long edge of logicalViewSize(resizeWidth, resizeHeight).
|
|
645
|
+
// View: the same, before and after layout, so the request URL (and with it
|
|
646
|
+
// the cache key) is identical on both sides.
|
|
647
|
+
let raw = try XCTUnwrap(URL(string: "https://common.onekey-asset.com/token.png"))
|
|
648
|
+
let hint = try XCTUnwrap(OneKeyImageDecodeSizing.logicalViewSize(width: 48, height: 64))
|
|
649
|
+
let preloadDisplaySize = max(hint.width, hint.height)
|
|
650
|
+
for bounds in [CGSize.zero, CGSize(width: 48, height: 64), CGSize(width: 30, height: 30)] {
|
|
651
|
+
let viewDisplaySize = try XCTUnwrap(
|
|
652
|
+
OneKeyImageDecodeSizing.tosDisplaySize(bounds: bounds, resizeWidth: 48, resizeHeight: 64)
|
|
653
|
+
)
|
|
654
|
+
XCTAssertEqual(viewDisplaySize, preloadDisplaySize)
|
|
655
|
+
XCTAssertEqual(
|
|
656
|
+
OneKeyTosURL.optimized(
|
|
657
|
+
rawURL: raw, displaySize: viewDisplaySize, scale: 3, overscan: 1.1, hasCustomIdentity: false
|
|
658
|
+
),
|
|
659
|
+
OneKeyTosURL.optimized(
|
|
660
|
+
rawURL: raw, displaySize: preloadDisplaySize, scale: 3, overscan: 1.1, hasCustomIdentity: false
|
|
661
|
+
)
|
|
662
|
+
)
|
|
663
|
+
}
|
|
664
|
+
// Width-only hint keeps the historical behaviour (the width itself).
|
|
665
|
+
XCTAssertEqual(
|
|
666
|
+
OneKeyImageDecodeSizing.tosDisplaySize(bounds: .zero, resizeWidth: 40, resizeHeight: nil),
|
|
667
|
+
40
|
|
668
|
+
)
|
|
669
|
+
// No hint: the laid-out bounds decide; nothing before layout.
|
|
670
|
+
XCTAssertEqual(
|
|
671
|
+
OneKeyImageDecodeSizing.tosDisplaySize(
|
|
672
|
+
bounds: CGSize(width: 40, height: 64), resizeWidth: nil, resizeHeight: nil
|
|
673
|
+
),
|
|
674
|
+
64
|
|
675
|
+
)
|
|
676
|
+
XCTAssertNil(
|
|
677
|
+
OneKeyImageDecodeSizing.tosDisplaySize(bounds: .zero, resizeWidth: nil, resizeHeight: nil)
|
|
678
|
+
)
|
|
679
|
+
}
|
|
680
|
+
|
|
547
681
|
func testSupersededRequestNeverPaintsRecycledView() throws {
|
|
548
682
|
let image = HybridOneKeyImage()
|
|
549
683
|
let imageView = try XCTUnwrap(image.view as? SDAnimatedImageView)
|
package/lib/module/index.js
CHANGED
|
@@ -46,6 +46,7 @@ export function OneKeyImage({
|
|
|
46
46
|
optimizeTos = true,
|
|
47
47
|
round = false,
|
|
48
48
|
resizeWidth,
|
|
49
|
+
resizeHeight,
|
|
49
50
|
overscan = 1.1,
|
|
50
51
|
loadingStrategy = OneKeyImageLoadingStrategy.STATIC,
|
|
51
52
|
placeholderColor,
|
|
@@ -60,7 +61,7 @@ export function OneKeyImage({
|
|
|
60
61
|
}) {
|
|
61
62
|
const normalized = useMemo(() => normalizeSource(source), [source]);
|
|
62
63
|
const resolvedCachePolicy = cachePolicy ?? normalized?.cachePolicy ?? OneKeyImageCachePolicy.MEMORY_DISK;
|
|
63
|
-
const identity = `${normalized?.uri ?? ''}|${JSON.stringify(normalized?.headers ?? {})}|${resolvedCachePolicy}|${recyclingKey ?? ''}|${contentFit}|${optimizeTos ? '1' : '0'}|${resizeWidth ?? ''}|${overscan}`;
|
|
64
|
+
const identity = `${normalized?.uri ?? ''}|${JSON.stringify(normalized?.headers ?? {})}|${resolvedCachePolicy}|${recyclingKey ?? ''}|${contentFit}|${optimizeTos ? '1' : '0'}|${resizeWidth ?? ''}|${resizeHeight ?? ''}|${overscan}`;
|
|
64
65
|
const hasSource = normalized != null;
|
|
65
66
|
const hasOverlay = placeholder != null || fallback != null;
|
|
66
67
|
const [loadState, setLoadState] = useState({
|
|
@@ -215,6 +216,7 @@ export function OneKeyImage({
|
|
|
215
216
|
optimizeTos,
|
|
216
217
|
round,
|
|
217
218
|
resizeWidth,
|
|
219
|
+
resizeHeight,
|
|
218
220
|
overscan,
|
|
219
221
|
loadingStrategy: placeholder == null ? loadingStrategy : OneKeyImageLoadingStrategy.NONE,
|
|
220
222
|
placeholderColor
|
|
@@ -156,6 +156,15 @@ namespace margelo::nitro::onekeyimage {
|
|
|
156
156
|
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<jni::JDouble> /* resizeWidth */)>("setResizeWidth");
|
|
157
157
|
method(_javaPart, resizeWidth.has_value() ? jni::JDouble::valueOf(resizeWidth.value()) : nullptr);
|
|
158
158
|
}
|
|
159
|
+
std::optional<double> JHybridOneKeyImageSpec::getResizeHeight() {
|
|
160
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<jni::JDouble>()>("getResizeHeight");
|
|
161
|
+
auto __result = method(_javaPart);
|
|
162
|
+
return __result != nullptr ? std::make_optional(__result->value()) : std::nullopt;
|
|
163
|
+
}
|
|
164
|
+
void JHybridOneKeyImageSpec::setResizeHeight(std::optional<double> resizeHeight) {
|
|
165
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<jni::JDouble> /* resizeHeight */)>("setResizeHeight");
|
|
166
|
+
method(_javaPart, resizeHeight.has_value() ? jni::JDouble::valueOf(resizeHeight.value()) : nullptr);
|
|
167
|
+
}
|
|
159
168
|
std::optional<double> JHybridOneKeyImageSpec::getOverscan() {
|
|
160
169
|
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<jni::JDouble>()>("getOverscan");
|
|
161
170
|
auto __result = method(_javaPart);
|
|
@@ -70,6 +70,8 @@ namespace margelo::nitro::onekeyimage {
|
|
|
70
70
|
void setRound(std::optional<bool> round) override;
|
|
71
71
|
std::optional<double> getResizeWidth() override;
|
|
72
72
|
void setResizeWidth(std::optional<double> resizeWidth) override;
|
|
73
|
+
std::optional<double> getResizeHeight() override;
|
|
74
|
+
void setResizeHeight(std::optional<double> resizeHeight) override;
|
|
73
75
|
std::optional<double> getOverscan() override;
|
|
74
76
|
void setOverscan(std::optional<double> overscan) override;
|
|
75
77
|
std::optional<OneKeyImageLoadingStrategy> getLoadingStrategy() override;
|
|
@@ -103,6 +103,11 @@ void JHybridOneKeyImageStateUpdater::updateViewProps(jni::alias_ref<jni::JClass>
|
|
|
103
103
|
: !newProps->resizeWidth.hasSameValue(oldProps->resizeWidth)) {
|
|
104
104
|
hybridView->setResizeWidth(newProps->resizeWidth.get());
|
|
105
105
|
}
|
|
106
|
+
if (oldProps == nullptr
|
|
107
|
+
? newProps->resizeHeight.isProvided()
|
|
108
|
+
: !newProps->resizeHeight.hasSameValue(oldProps->resizeHeight)) {
|
|
109
|
+
hybridView->setResizeHeight(newProps->resizeHeight.get());
|
|
110
|
+
}
|
|
106
111
|
if (oldProps == nullptr
|
|
107
112
|
? newProps->overscan.isProvided()
|
|
108
113
|
: !newProps->overscan.hasSameValue(oldProps->overscan)) {
|
package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyImageSpec.kt
CHANGED
|
@@ -87,6 +87,12 @@ abstract class HybridOneKeyImageSpec: HybridView() {
|
|
|
87
87
|
@set:Keep
|
|
88
88
|
abstract var resizeWidth: Double?
|
|
89
89
|
|
|
90
|
+
@get:DoNotStrip
|
|
91
|
+
@get:Keep
|
|
92
|
+
@set:DoNotStrip
|
|
93
|
+
@set:Keep
|
|
94
|
+
abstract var resizeHeight: Double?
|
|
95
|
+
|
|
90
96
|
@get:DoNotStrip
|
|
91
97
|
@get:Keep
|
|
92
98
|
@set:DoNotStrip
|
|
@@ -148,6 +148,13 @@ namespace margelo::nitro::onekeyimage {
|
|
|
148
148
|
inline void setResizeWidth(std::optional<double> resizeWidth) noexcept override {
|
|
149
149
|
_swiftPart.setResizeWidth(resizeWidth);
|
|
150
150
|
}
|
|
151
|
+
inline std::optional<double> getResizeHeight() noexcept override {
|
|
152
|
+
auto __result = _swiftPart.getResizeHeight();
|
|
153
|
+
return __result;
|
|
154
|
+
}
|
|
155
|
+
inline void setResizeHeight(std::optional<double> resizeHeight) noexcept override {
|
|
156
|
+
_swiftPart.setResizeHeight(resizeHeight);
|
|
157
|
+
}
|
|
151
158
|
inline std::optional<double> getOverscan() noexcept override {
|
|
152
159
|
auto __result = _swiftPart.getOverscan();
|
|
153
160
|
return __result;
|
|
@@ -158,6 +158,12 @@ using namespace margelo::nitro::onekeyimage::views;
|
|
|
158
158
|
: !newViewProps.resizeWidth.hasSameValue(oldViewProps->resizeWidth)) {
|
|
159
159
|
swiftPart.setResizeWidth(newViewProps.resizeWidth.get());
|
|
160
160
|
}
|
|
161
|
+
// resizeHeight: optional
|
|
162
|
+
if (oldViewProps == nullptr
|
|
163
|
+
? newViewProps.resizeHeight.isProvided()
|
|
164
|
+
: !newViewProps.resizeHeight.hasSameValue(oldViewProps->resizeHeight)) {
|
|
165
|
+
swiftPart.setResizeHeight(newViewProps.resizeHeight.get());
|
|
166
|
+
}
|
|
161
167
|
// overscan: optional
|
|
162
168
|
if (oldViewProps == nullptr
|
|
163
169
|
? newViewProps.overscan.isProvided()
|
|
@@ -20,6 +20,7 @@ public protocol HybridOneKeyImageSpec_protocol: HybridObject, HybridView {
|
|
|
20
20
|
var optimizeTos: Bool? { get set }
|
|
21
21
|
var round: Bool? { get set }
|
|
22
22
|
var resizeWidth: Double? { get set }
|
|
23
|
+
var resizeHeight: Double? { get set }
|
|
23
24
|
var overscan: Double? { get set }
|
|
24
25
|
var loadingStrategy: OneKeyImageLoadingStrategy? { get set }
|
|
25
26
|
var placeholderColor: String? { get set }
|
|
@@ -340,6 +340,30 @@ open class HybridOneKeyImageSpec_cxx {
|
|
|
340
340
|
}
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
public final var resizeHeight: bridge.std__optional_double_ {
|
|
344
|
+
@inline(__always)
|
|
345
|
+
get {
|
|
346
|
+
return { () -> bridge.std__optional_double_ in
|
|
347
|
+
if let __unwrappedValue = self.__implementation.resizeHeight {
|
|
348
|
+
return bridge.create_std__optional_double_(__unwrappedValue)
|
|
349
|
+
} else {
|
|
350
|
+
return .init()
|
|
351
|
+
}
|
|
352
|
+
}()
|
|
353
|
+
}
|
|
354
|
+
@inline(__always)
|
|
355
|
+
set {
|
|
356
|
+
self.__implementation.resizeHeight = { () -> Double? in
|
|
357
|
+
if bridge.has_value_std__optional_double_(newValue) {
|
|
358
|
+
let __unwrapped = bridge.get_std__optional_double_(newValue)
|
|
359
|
+
return __unwrapped
|
|
360
|
+
} else {
|
|
361
|
+
return nil
|
|
362
|
+
}
|
|
363
|
+
}()
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
343
367
|
public final var overscan: bridge.std__optional_double_ {
|
|
344
368
|
@inline(__always)
|
|
345
369
|
get {
|
|
@@ -34,6 +34,8 @@ namespace margelo::nitro::onekeyimage {
|
|
|
34
34
|
prototype.registerHybridSetter("round", &HybridOneKeyImageSpec::setRound);
|
|
35
35
|
prototype.registerHybridGetter("resizeWidth", &HybridOneKeyImageSpec::getResizeWidth);
|
|
36
36
|
prototype.registerHybridSetter("resizeWidth", &HybridOneKeyImageSpec::setResizeWidth);
|
|
37
|
+
prototype.registerHybridGetter("resizeHeight", &HybridOneKeyImageSpec::getResizeHeight);
|
|
38
|
+
prototype.registerHybridSetter("resizeHeight", &HybridOneKeyImageSpec::setResizeHeight);
|
|
37
39
|
prototype.registerHybridGetter("overscan", &HybridOneKeyImageSpec::getOverscan);
|
|
38
40
|
prototype.registerHybridSetter("overscan", &HybridOneKeyImageSpec::setOverscan);
|
|
39
41
|
prototype.registerHybridGetter("loadingStrategy", &HybridOneKeyImageSpec::getLoadingStrategy);
|
|
@@ -80,6 +80,8 @@ namespace margelo::nitro::onekeyimage {
|
|
|
80
80
|
virtual void setRound(std::optional<bool> round) = 0;
|
|
81
81
|
virtual std::optional<double> getResizeWidth() = 0;
|
|
82
82
|
virtual void setResizeWidth(std::optional<double> resizeWidth) = 0;
|
|
83
|
+
virtual std::optional<double> getResizeHeight() = 0;
|
|
84
|
+
virtual void setResizeHeight(std::optional<double> resizeHeight) = 0;
|
|
83
85
|
virtual std::optional<double> getOverscan() = 0;
|
|
84
86
|
virtual void setOverscan(std::optional<double> overscan) = 0;
|
|
85
87
|
virtual std::optional<OneKeyImageLoadingStrategy> getLoadingStrategy() = 0;
|
|
@@ -30,6 +30,7 @@ namespace margelo::nitro::onekeyimage::views {
|
|
|
30
30
|
optimizeTos(nitro::ReactProp<std::optional<bool>>::fromRawValue("OneKeyImage", "optimizeTos", rawProps, sourceProps.optimizeTos)),
|
|
31
31
|
round(nitro::ReactProp<std::optional<bool>>::fromRawValue("OneKeyImage", "round", rawProps, sourceProps.round)),
|
|
32
32
|
resizeWidth(nitro::ReactProp<std::optional<double>>::fromRawValue("OneKeyImage", "resizeWidth", rawProps, sourceProps.resizeWidth)),
|
|
33
|
+
resizeHeight(nitro::ReactProp<std::optional<double>>::fromRawValue("OneKeyImage", "resizeHeight", rawProps, sourceProps.resizeHeight)),
|
|
33
34
|
overscan(nitro::ReactProp<std::optional<double>>::fromRawValue("OneKeyImage", "overscan", rawProps, sourceProps.overscan)),
|
|
34
35
|
loadingStrategy(nitro::ReactProp<std::optional<OneKeyImageLoadingStrategy>>::fromRawValue("OneKeyImage", "loadingStrategy", rawProps, sourceProps.loadingStrategy)),
|
|
35
36
|
placeholderColor(nitro::ReactProp<std::optional<std::string>>::fromRawValue("OneKeyImage", "placeholderColor", rawProps, sourceProps.placeholderColor)),
|
|
@@ -52,6 +53,7 @@ namespace margelo::nitro::onekeyimage::views {
|
|
|
52
53
|
case hashString("optimizeTos"): return true;
|
|
53
54
|
case hashString("round"): return true;
|
|
54
55
|
case hashString("resizeWidth"): return true;
|
|
56
|
+
case hashString("resizeHeight"): return true;
|
|
55
57
|
case hashString("overscan"): return true;
|
|
56
58
|
case hashString("loadingStrategy"): return true;
|
|
57
59
|
case hashString("placeholderColor"): return true;
|
|
@@ -58,6 +58,7 @@ namespace margelo::nitro::onekeyimage::views {
|
|
|
58
58
|
nitro::ReactProp<std::optional<bool>> optimizeTos;
|
|
59
59
|
nitro::ReactProp<std::optional<bool>> round;
|
|
60
60
|
nitro::ReactProp<std::optional<double>> resizeWidth;
|
|
61
|
+
nitro::ReactProp<std::optional<double>> resizeHeight;
|
|
61
62
|
nitro::ReactProp<std::optional<double>> overscan;
|
|
62
63
|
nitro::ReactProp<std::optional<OneKeyImageLoadingStrategy>> loadingStrategy;
|
|
63
64
|
nitro::ReactProp<std::optional<std::string>> placeholderColor;
|
|
@@ -80,6 +81,7 @@ namespace margelo::nitro::onekeyimage::views {
|
|
|
80
81
|
optimizeTos.hasSameValue(other.optimizeTos) &&
|
|
81
82
|
round.hasSameValue(other.round) &&
|
|
82
83
|
resizeWidth.hasSameValue(other.resizeWidth) &&
|
|
84
|
+
resizeHeight.hasSameValue(other.resizeHeight) &&
|
|
83
85
|
overscan.hasSameValue(other.overscan) &&
|
|
84
86
|
loadingStrategy.hasSameValue(other.loadingStrategy) &&
|
|
85
87
|
placeholderColor.hasSameValue(other.placeholderColor) &&
|
|
@@ -103,6 +105,7 @@ namespace margelo::nitro::onekeyimage::views {
|
|
|
103
105
|
optimizeTos.isProvided() ||
|
|
104
106
|
round.isProvided() ||
|
|
105
107
|
resizeWidth.isProvided() ||
|
|
108
|
+
resizeHeight.isProvided() ||
|
|
106
109
|
overscan.isProvided() ||
|
|
107
110
|
loadingStrategy.isProvided() ||
|
|
108
111
|
placeholderColor.isProvided() ||
|
|
@@ -36,6 +36,12 @@ export interface OneKeyImageNativeProps extends HybridViewProps {
|
|
|
36
36
|
round?: boolean;
|
|
37
37
|
/** Display width hint in layout units. Native applies the screen density. */
|
|
38
38
|
resizeWidth?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Display height hint in layout units. With `resizeWidth` it lets the
|
|
41
|
+
* pre-layout memory-cache probe derive the same decode thumbnail a preload
|
|
42
|
+
* with the same hints stored; omitted, the hint is a square of `resizeWidth`.
|
|
43
|
+
*/
|
|
44
|
+
resizeHeight?: number;
|
|
39
45
|
overscan?: number;
|
|
40
46
|
loadingStrategy?: OneKeyImageLoadingStrategy;
|
|
41
47
|
/** Theme-aware color used by static loading, error, and fallback states. */
|
|
@@ -45,7 +45,7 @@ export interface OneKeyImageProps extends Omit<OneKeyImageNativeProps, 'sourceUr
|
|
|
45
45
|
onError?: MaybeWrappedCallback<(event: OneKeyImageErrorEvent) => void>;
|
|
46
46
|
onLoadEnd?: MaybeWrappedCallback<() => void>;
|
|
47
47
|
}
|
|
48
|
-
export declare function OneKeyImage({ source, placeholder, fallback, variant, contentFit, cachePolicy, autoplay, recyclingKey, optimizeTos, round, resizeWidth, overscan, loadingStrategy, placeholderColor, hybridRef, onLoadStart, onLoad, onDisplay, onError, onLoadEnd, style, ...viewProps }: OneKeyImageProps): import("react/jsx-runtime").JSX.Element;
|
|
48
|
+
export declare function OneKeyImage({ source, placeholder, fallback, variant, contentFit, cachePolicy, autoplay, recyclingKey, optimizeTos, round, resizeWidth, resizeHeight, overscan, loadingStrategy, placeholderColor, hybridRef, onLoadStart, onLoad, onDisplay, onError, onLoadEnd, style, ...viewProps }: OneKeyImageProps): import("react/jsx-runtime").JSX.Element;
|
|
49
49
|
export declare const OneKeyImageCache: {
|
|
50
50
|
preload(sources: OneKeyImagePreloadInput[]): Promise<boolean>;
|
|
51
51
|
clearMemory: () => Promise<void>;
|
|
@@ -156,6 +156,15 @@ namespace margelo::nitro::onekeyimage {
|
|
|
156
156
|
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<jni::JDouble> /* resizeWidth */)>("setResizeWidth");
|
|
157
157
|
method(_javaPart, resizeWidth.has_value() ? jni::JDouble::valueOf(resizeWidth.value()) : nullptr);
|
|
158
158
|
}
|
|
159
|
+
std::optional<double> JHybridOneKeyImageSpec::getResizeHeight() {
|
|
160
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<jni::JDouble>()>("getResizeHeight");
|
|
161
|
+
auto __result = method(_javaPart);
|
|
162
|
+
return __result != nullptr ? std::make_optional(__result->value()) : std::nullopt;
|
|
163
|
+
}
|
|
164
|
+
void JHybridOneKeyImageSpec::setResizeHeight(std::optional<double> resizeHeight) {
|
|
165
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<jni::JDouble> /* resizeHeight */)>("setResizeHeight");
|
|
166
|
+
method(_javaPart, resizeHeight.has_value() ? jni::JDouble::valueOf(resizeHeight.value()) : nullptr);
|
|
167
|
+
}
|
|
159
168
|
std::optional<double> JHybridOneKeyImageSpec::getOverscan() {
|
|
160
169
|
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<jni::JDouble>()>("getOverscan");
|
|
161
170
|
auto __result = method(_javaPart);
|
|
@@ -70,6 +70,8 @@ namespace margelo::nitro::onekeyimage {
|
|
|
70
70
|
void setRound(std::optional<bool> round) override;
|
|
71
71
|
std::optional<double> getResizeWidth() override;
|
|
72
72
|
void setResizeWidth(std::optional<double> resizeWidth) override;
|
|
73
|
+
std::optional<double> getResizeHeight() override;
|
|
74
|
+
void setResizeHeight(std::optional<double> resizeHeight) override;
|
|
73
75
|
std::optional<double> getOverscan() override;
|
|
74
76
|
void setOverscan(std::optional<double> overscan) override;
|
|
75
77
|
std::optional<OneKeyImageLoadingStrategy> getLoadingStrategy() override;
|
|
@@ -103,6 +103,11 @@ void JHybridOneKeyImageStateUpdater::updateViewProps(jni::alias_ref<jni::JClass>
|
|
|
103
103
|
: !newProps->resizeWidth.hasSameValue(oldProps->resizeWidth)) {
|
|
104
104
|
hybridView->setResizeWidth(newProps->resizeWidth.get());
|
|
105
105
|
}
|
|
106
|
+
if (oldProps == nullptr
|
|
107
|
+
? newProps->resizeHeight.isProvided()
|
|
108
|
+
: !newProps->resizeHeight.hasSameValue(oldProps->resizeHeight)) {
|
|
109
|
+
hybridView->setResizeHeight(newProps->resizeHeight.get());
|
|
110
|
+
}
|
|
106
111
|
if (oldProps == nullptr
|
|
107
112
|
? newProps->overscan.isProvided()
|
|
108
113
|
: !newProps->overscan.hasSameValue(oldProps->overscan)) {
|
package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyImageSpec.kt
CHANGED
|
@@ -87,6 +87,12 @@ abstract class HybridOneKeyImageSpec: HybridView() {
|
|
|
87
87
|
@set:Keep
|
|
88
88
|
abstract var resizeWidth: Double?
|
|
89
89
|
|
|
90
|
+
@get:DoNotStrip
|
|
91
|
+
@get:Keep
|
|
92
|
+
@set:DoNotStrip
|
|
93
|
+
@set:Keep
|
|
94
|
+
abstract var resizeHeight: Double?
|
|
95
|
+
|
|
90
96
|
@get:DoNotStrip
|
|
91
97
|
@get:Keep
|
|
92
98
|
@set:DoNotStrip
|
|
@@ -148,6 +148,13 @@ namespace margelo::nitro::onekeyimage {
|
|
|
148
148
|
inline void setResizeWidth(std::optional<double> resizeWidth) noexcept override {
|
|
149
149
|
_swiftPart.setResizeWidth(resizeWidth);
|
|
150
150
|
}
|
|
151
|
+
inline std::optional<double> getResizeHeight() noexcept override {
|
|
152
|
+
auto __result = _swiftPart.getResizeHeight();
|
|
153
|
+
return __result;
|
|
154
|
+
}
|
|
155
|
+
inline void setResizeHeight(std::optional<double> resizeHeight) noexcept override {
|
|
156
|
+
_swiftPart.setResizeHeight(resizeHeight);
|
|
157
|
+
}
|
|
151
158
|
inline std::optional<double> getOverscan() noexcept override {
|
|
152
159
|
auto __result = _swiftPart.getOverscan();
|
|
153
160
|
return __result;
|
|
@@ -158,6 +158,12 @@ using namespace margelo::nitro::onekeyimage::views;
|
|
|
158
158
|
: !newViewProps.resizeWidth.hasSameValue(oldViewProps->resizeWidth)) {
|
|
159
159
|
swiftPart.setResizeWidth(newViewProps.resizeWidth.get());
|
|
160
160
|
}
|
|
161
|
+
// resizeHeight: optional
|
|
162
|
+
if (oldViewProps == nullptr
|
|
163
|
+
? newViewProps.resizeHeight.isProvided()
|
|
164
|
+
: !newViewProps.resizeHeight.hasSameValue(oldViewProps->resizeHeight)) {
|
|
165
|
+
swiftPart.setResizeHeight(newViewProps.resizeHeight.get());
|
|
166
|
+
}
|
|
161
167
|
// overscan: optional
|
|
162
168
|
if (oldViewProps == nullptr
|
|
163
169
|
? newViewProps.overscan.isProvided()
|
|
@@ -20,6 +20,7 @@ public protocol HybridOneKeyImageSpec_protocol: HybridObject, HybridView {
|
|
|
20
20
|
var optimizeTos: Bool? { get set }
|
|
21
21
|
var round: Bool? { get set }
|
|
22
22
|
var resizeWidth: Double? { get set }
|
|
23
|
+
var resizeHeight: Double? { get set }
|
|
23
24
|
var overscan: Double? { get set }
|
|
24
25
|
var loadingStrategy: OneKeyImageLoadingStrategy? { get set }
|
|
25
26
|
var placeholderColor: String? { get set }
|
|
@@ -340,6 +340,30 @@ open class HybridOneKeyImageSpec_cxx {
|
|
|
340
340
|
}
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
public final var resizeHeight: bridge.std__optional_double_ {
|
|
344
|
+
@inline(__always)
|
|
345
|
+
get {
|
|
346
|
+
return { () -> bridge.std__optional_double_ in
|
|
347
|
+
if let __unwrappedValue = self.__implementation.resizeHeight {
|
|
348
|
+
return bridge.create_std__optional_double_(__unwrappedValue)
|
|
349
|
+
} else {
|
|
350
|
+
return .init()
|
|
351
|
+
}
|
|
352
|
+
}()
|
|
353
|
+
}
|
|
354
|
+
@inline(__always)
|
|
355
|
+
set {
|
|
356
|
+
self.__implementation.resizeHeight = { () -> Double? in
|
|
357
|
+
if bridge.has_value_std__optional_double_(newValue) {
|
|
358
|
+
let __unwrapped = bridge.get_std__optional_double_(newValue)
|
|
359
|
+
return __unwrapped
|
|
360
|
+
} else {
|
|
361
|
+
return nil
|
|
362
|
+
}
|
|
363
|
+
}()
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
343
367
|
public final var overscan: bridge.std__optional_double_ {
|
|
344
368
|
@inline(__always)
|
|
345
369
|
get {
|
|
@@ -34,6 +34,8 @@ namespace margelo::nitro::onekeyimage {
|
|
|
34
34
|
prototype.registerHybridSetter("round", &HybridOneKeyImageSpec::setRound);
|
|
35
35
|
prototype.registerHybridGetter("resizeWidth", &HybridOneKeyImageSpec::getResizeWidth);
|
|
36
36
|
prototype.registerHybridSetter("resizeWidth", &HybridOneKeyImageSpec::setResizeWidth);
|
|
37
|
+
prototype.registerHybridGetter("resizeHeight", &HybridOneKeyImageSpec::getResizeHeight);
|
|
38
|
+
prototype.registerHybridSetter("resizeHeight", &HybridOneKeyImageSpec::setResizeHeight);
|
|
37
39
|
prototype.registerHybridGetter("overscan", &HybridOneKeyImageSpec::getOverscan);
|
|
38
40
|
prototype.registerHybridSetter("overscan", &HybridOneKeyImageSpec::setOverscan);
|
|
39
41
|
prototype.registerHybridGetter("loadingStrategy", &HybridOneKeyImageSpec::getLoadingStrategy);
|
|
@@ -80,6 +80,8 @@ namespace margelo::nitro::onekeyimage {
|
|
|
80
80
|
virtual void setRound(std::optional<bool> round) = 0;
|
|
81
81
|
virtual std::optional<double> getResizeWidth() = 0;
|
|
82
82
|
virtual void setResizeWidth(std::optional<double> resizeWidth) = 0;
|
|
83
|
+
virtual std::optional<double> getResizeHeight() = 0;
|
|
84
|
+
virtual void setResizeHeight(std::optional<double> resizeHeight) = 0;
|
|
83
85
|
virtual std::optional<double> getOverscan() = 0;
|
|
84
86
|
virtual void setOverscan(std::optional<double> overscan) = 0;
|
|
85
87
|
virtual std::optional<OneKeyImageLoadingStrategy> getLoadingStrategy() = 0;
|
|
@@ -30,6 +30,7 @@ namespace margelo::nitro::onekeyimage::views {
|
|
|
30
30
|
optimizeTos(nitro::ReactProp<std::optional<bool>>::fromRawValue("OneKeyImage", "optimizeTos", rawProps, sourceProps.optimizeTos)),
|
|
31
31
|
round(nitro::ReactProp<std::optional<bool>>::fromRawValue("OneKeyImage", "round", rawProps, sourceProps.round)),
|
|
32
32
|
resizeWidth(nitro::ReactProp<std::optional<double>>::fromRawValue("OneKeyImage", "resizeWidth", rawProps, sourceProps.resizeWidth)),
|
|
33
|
+
resizeHeight(nitro::ReactProp<std::optional<double>>::fromRawValue("OneKeyImage", "resizeHeight", rawProps, sourceProps.resizeHeight)),
|
|
33
34
|
overscan(nitro::ReactProp<std::optional<double>>::fromRawValue("OneKeyImage", "overscan", rawProps, sourceProps.overscan)),
|
|
34
35
|
loadingStrategy(nitro::ReactProp<std::optional<OneKeyImageLoadingStrategy>>::fromRawValue("OneKeyImage", "loadingStrategy", rawProps, sourceProps.loadingStrategy)),
|
|
35
36
|
placeholderColor(nitro::ReactProp<std::optional<std::string>>::fromRawValue("OneKeyImage", "placeholderColor", rawProps, sourceProps.placeholderColor)),
|
|
@@ -52,6 +53,7 @@ namespace margelo::nitro::onekeyimage::views {
|
|
|
52
53
|
case hashString("optimizeTos"): return true;
|
|
53
54
|
case hashString("round"): return true;
|
|
54
55
|
case hashString("resizeWidth"): return true;
|
|
56
|
+
case hashString("resizeHeight"): return true;
|
|
55
57
|
case hashString("overscan"): return true;
|
|
56
58
|
case hashString("loadingStrategy"): return true;
|
|
57
59
|
case hashString("placeholderColor"): return true;
|
|
@@ -58,6 +58,7 @@ namespace margelo::nitro::onekeyimage::views {
|
|
|
58
58
|
nitro::ReactProp<std::optional<bool>> optimizeTos;
|
|
59
59
|
nitro::ReactProp<std::optional<bool>> round;
|
|
60
60
|
nitro::ReactProp<std::optional<double>> resizeWidth;
|
|
61
|
+
nitro::ReactProp<std::optional<double>> resizeHeight;
|
|
61
62
|
nitro::ReactProp<std::optional<double>> overscan;
|
|
62
63
|
nitro::ReactProp<std::optional<OneKeyImageLoadingStrategy>> loadingStrategy;
|
|
63
64
|
nitro::ReactProp<std::optional<std::string>> placeholderColor;
|
|
@@ -80,6 +81,7 @@ namespace margelo::nitro::onekeyimage::views {
|
|
|
80
81
|
optimizeTos.hasSameValue(other.optimizeTos) &&
|
|
81
82
|
round.hasSameValue(other.round) &&
|
|
82
83
|
resizeWidth.hasSameValue(other.resizeWidth) &&
|
|
84
|
+
resizeHeight.hasSameValue(other.resizeHeight) &&
|
|
83
85
|
overscan.hasSameValue(other.overscan) &&
|
|
84
86
|
loadingStrategy.hasSameValue(other.loadingStrategy) &&
|
|
85
87
|
placeholderColor.hasSameValue(other.placeholderColor) &&
|
|
@@ -103,6 +105,7 @@ namespace margelo::nitro::onekeyimage::views {
|
|
|
103
105
|
optimizeTos.isProvided() ||
|
|
104
106
|
round.isProvided() ||
|
|
105
107
|
resizeWidth.isProvided() ||
|
|
108
|
+
resizeHeight.isProvided() ||
|
|
106
109
|
overscan.isProvided() ||
|
|
107
110
|
loadingStrategy.isProvided() ||
|
|
108
111
|
placeholderColor.isProvided() ||
|
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.158",
|
|
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.158",
|
|
85
85
|
"react": "*",
|
|
86
86
|
"react-native": "*",
|
|
87
87
|
"react-native-nitro-modules": "0.37.0"
|
package/src/OneKeyImage.nitro.ts
CHANGED
|
@@ -47,6 +47,12 @@ export interface OneKeyImageNativeProps extends HybridViewProps {
|
|
|
47
47
|
round?: boolean;
|
|
48
48
|
/** Display width hint in layout units. Native applies the screen density. */
|
|
49
49
|
resizeWidth?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Display height hint in layout units. With `resizeWidth` it lets the
|
|
52
|
+
* pre-layout memory-cache probe derive the same decode thumbnail a preload
|
|
53
|
+
* with the same hints stored; omitted, the hint is a square of `resizeWidth`.
|
|
54
|
+
*/
|
|
55
|
+
resizeHeight?: number;
|
|
50
56
|
overscan?: number;
|
|
51
57
|
loadingStrategy?: OneKeyImageLoadingStrategy;
|
|
52
58
|
/** Theme-aware color used by static loading, error, and fallback states. */
|
package/src/index.tsx
CHANGED
|
@@ -155,6 +155,7 @@ export function OneKeyImage({
|
|
|
155
155
|
optimizeTos = true,
|
|
156
156
|
round = false,
|
|
157
157
|
resizeWidth,
|
|
158
|
+
resizeHeight,
|
|
158
159
|
overscan = 1.1,
|
|
159
160
|
loadingStrategy = OneKeyImageLoadingStrategy.STATIC,
|
|
160
161
|
placeholderColor,
|
|
@@ -176,7 +177,7 @@ export function OneKeyImage({
|
|
|
176
177
|
normalized?.headers ?? {}
|
|
177
178
|
)}|${resolvedCachePolicy}|${recyclingKey ?? ''}|${contentFit}|${
|
|
178
179
|
optimizeTos ? '1' : '0'
|
|
179
|
-
}|${resizeWidth ?? ''}|${overscan}`;
|
|
180
|
+
}|${resizeWidth ?? ''}|${resizeHeight ?? ''}|${overscan}`;
|
|
180
181
|
const hasSource = normalized != null;
|
|
181
182
|
const hasOverlay = placeholder != null || fallback != null;
|
|
182
183
|
type LoadState = {
|
|
@@ -373,6 +374,7 @@ export function OneKeyImage({
|
|
|
373
374
|
optimizeTos,
|
|
374
375
|
round,
|
|
375
376
|
resizeWidth,
|
|
377
|
+
resizeHeight,
|
|
376
378
|
overscan,
|
|
377
379
|
loadingStrategy:
|
|
378
380
|
placeholder == null ? loadingStrategy : OneKeyImageLoadingStrategy.NONE,
|