@onekeyfe/react-native-image 3.0.100 → 3.0.101

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 CHANGED
@@ -56,6 +56,12 @@ large lists conservative, and `autoplay={true}` on other native platforms. Pass
56
56
  `autoplay` explicitly whenever a screen needs the same behavior on both
57
57
  platforms.
58
58
 
59
+ Animated-image canvas limits are intentionally platform-specific. Android caps
60
+ the unavoidable decoded ARGB canvas at 4,194,304 pixels (16 MiB), while iOS
61
+ allows metadata dimensions up to 16,000,000 pixels and separately caps its
62
+ decoded animation frame buffer at 16 MiB. These limits reflect the native
63
+ decoders' different allocation behavior.
64
+
59
65
  `recyclingKey` is available for recycled list cells. Changing it clears stale
60
66
  content before the next source is displayed.
61
67
 
@@ -38,6 +38,7 @@ private class OneKeyImageHostView(context: ThemedReactContext) : ImageView(conte
38
38
  syncPlayback()
39
39
  }
40
40
  var onReadyForRequest: (() -> Unit)? = null
41
+ var onAttachmentChanged: ((Boolean) -> Unit)? = null
41
42
 
42
43
  override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
43
44
  super.onSizeChanged(w, h, oldw, oldh)
@@ -58,11 +59,13 @@ private class OneKeyImageHostView(context: ThemedReactContext) : ImageView(conte
58
59
  override fun onAttachedToWindow() {
59
60
  super.onAttachedToWindow()
60
61
  syncPlayback()
62
+ onAttachmentChanged?.invoke(true)
61
63
  }
62
64
 
63
65
  override fun onDetachedFromWindow() {
64
66
  super.onDetachedFromWindow()
65
67
  syncPlayback()
68
+ onAttachmentChanged?.invoke(false)
66
69
  }
67
70
 
68
71
  override fun onVisibilityAggregated(isVisible: Boolean) {
@@ -129,6 +132,7 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
129
132
  private var requestActive = false
130
133
  private var displayState = DisplayState.LOADING
131
134
  private var displayRunnable: Runnable? = null
135
+ private var pendingDisplayGeneration: Long? = null
132
136
  private var fallbackRunnable: Runnable? = null
133
137
 
134
138
  override val view: View = hostView
@@ -202,6 +206,14 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
202
206
  OneKeyImageGlideRegistry.ensureRegistered(context)
203
207
  hostView.updateSkeletonStyle()
204
208
  hostView.onReadyForRequest = { scheduleLoad() }
209
+ hostView.onAttachmentChanged = { attached ->
210
+ if (attached) {
211
+ schedulePendingDisplayIfNeeded()
212
+ } else {
213
+ displayRunnable?.let(hostView::removeCallbacks)
214
+ displayRunnable = null
215
+ }
216
+ }
205
217
  applyContentFit()
206
218
  applyVariant()
207
219
  }
@@ -229,6 +241,7 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
229
241
  resetForReuse()
230
242
  disposed = true
231
243
  hostView.onReadyForRequest = null
244
+ hostView.onAttachmentChanged = null
232
245
  super.onDropView()
233
246
  }
234
247
 
@@ -236,6 +249,7 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
236
249
  disposed = true
237
250
  cancelCurrent(invalidateGeneration = true)
238
251
  hostView.onReadyForRequest = null
252
+ hostView.onAttachmentChanged = null
239
253
  super.dispose()
240
254
  }
241
255
 
@@ -495,6 +509,7 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
495
509
  loadRunnable = null
496
510
  displayRunnable?.let(hostView::removeCallbacks)
497
511
  displayRunnable = null
512
+ pendingDisplayGeneration = null
498
513
  fallbackRunnable?.let(hostView::removeCallbacks)
499
514
  fallbackRunnable = null
500
515
  clearCurrentTarget()
@@ -512,14 +527,26 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
512
527
  }
513
528
 
514
529
  private fun scheduleOnDisplay(requestGeneration: Long) {
530
+ pendingDisplayGeneration = requestGeneration
531
+ schedulePendingDisplayIfNeeded()
532
+ }
533
+
534
+ private fun schedulePendingDisplayIfNeeded() {
535
+ val requestGeneration = pendingDisplayGeneration ?: return
515
536
  displayRunnable?.let(hostView::removeCallbacks)
537
+ displayRunnable = null
538
+ if (!hostView.isAttachedToWindow) return
516
539
  val runnable = Runnable {
517
540
  displayRunnable = null
518
541
  if (
542
+ pendingDisplayGeneration == requestGeneration &&
519
543
  requestGeneration == generation &&
520
544
  !disposed &&
521
- displayState == DisplayState.IMAGE
545
+ displayState == DisplayState.IMAGE &&
546
+ hostView.drawable != null &&
547
+ hostView.isAttachedToWindow
522
548
  ) {
549
+ pendingDisplayGeneration = null
523
550
  onDisplay?.invoke()
524
551
  }
525
552
  }
@@ -203,7 +203,10 @@ export function OneKeyImage({
203
203
  }
204
204
  export const OneKeyImageCache = {
205
205
  preload(sources) {
206
- return nativeCache.preload(sources.filter(source => Boolean(source.uri)).map(source => ({
206
+ const hasInvalidSource = sources.some(source => !source.uri?.trim());
207
+ const validSources = sources.filter(source => Boolean(source.uri?.trim()));
208
+ if (validSources.length === 0) return Promise.resolve(false);
209
+ return nativeCache.preload(validSources.map(source => ({
207
210
  uri: source.uri,
208
211
  headersJson: source.headers ? JSON.stringify(source.headers) : undefined,
209
212
  cachePolicy: source.cachePolicy ?? OneKeyImageCachePolicy.MEMORY_DISK,
@@ -212,7 +215,7 @@ export const OneKeyImageCache = {
212
215
  pixelRatio: source.pixelRatio,
213
216
  overscan: source.overscan,
214
217
  optimizeTos: source.optimizeTos
215
- })));
218
+ }))).then(success => success && !hasInvalidSource);
216
219
  },
217
220
  clearMemory: () => nativeCache.clearMemory(),
218
221
  clearDisk: () => nativeCache.clearDisk(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-image",
3
- "version": "3.0.100",
3
+ "version": "3.0.101",
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.100",
84
+ "@onekeyfe/react-native-skeleton": "3.0.101",
85
85
  "react": "*",
86
86
  "react-native": "*",
87
87
  "react-native-nitro-modules": "0.37.0"
package/src/index.tsx CHANGED
@@ -364,11 +364,17 @@ export function OneKeyImage({
364
364
 
365
365
  export const OneKeyImageCache = {
366
366
  preload(sources: OneKeyImagePreloadInput[]) {
367
- return nativeCache.preload(
368
- sources
369
- .filter((source) => Boolean(source.uri))
370
- .map((source) => ({
371
- uri: source.uri!,
367
+ const hasInvalidSource = sources.some((source) => !source.uri?.trim());
368
+ const validSources = sources.filter(
369
+ (source): source is OneKeyImagePreloadInput & { uri: string } =>
370
+ Boolean(source.uri?.trim())
371
+ );
372
+ if (validSources.length === 0) return Promise.resolve(false);
373
+
374
+ return nativeCache
375
+ .preload(
376
+ validSources.map((source) => ({
377
+ uri: source.uri,
372
378
  headersJson: source.headers
373
379
  ? JSON.stringify(source.headers)
374
380
  : undefined,
@@ -379,7 +385,8 @@ export const OneKeyImageCache = {
379
385
  overscan: source.overscan,
380
386
  optimizeTos: source.optimizeTos,
381
387
  }))
382
- );
388
+ )
389
+ .then((success) => success && !hasInvalidSource);
383
390
  },
384
391
  clearMemory: () => nativeCache.clearMemory(),
385
392
  clearDisk: () => nativeCache.clearDisk(),