@onekeyfe/react-native-native-list 3.0.121 → 3.0.123

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.
@@ -51,6 +51,11 @@ import kotlin.math.sqrt
51
51
  class NativeListView(
52
52
  private val reactContext: ThemedReactContext,
53
53
  ) : LinearLayout(reactContext) {
54
+ private data class VisibleMarketAnchor(
55
+ val key: String,
56
+ val offset: Int,
57
+ )
58
+
54
59
  private class ActionAnchorRecord(
55
60
  val token: String,
56
61
  origin: NativeListActionOrigin,
@@ -170,6 +175,7 @@ class NativeListView(
170
175
  private var lastLayoutWidth = -1
171
176
  private var lastLayoutHeight = -1
172
177
  private var lastLayoutDirection = layoutDirection
178
+ private var lastMarketPaginationAnchorLogAtMs = 0L
173
179
  private var disposed = false
174
180
 
175
181
  init {
@@ -363,6 +369,9 @@ class NativeListView(
363
369
  }
364
370
  return
365
371
  }
372
+ val preserveMarketPaginationAnchor = previous?.let {
373
+ isMarketPaginationUpdate(it, next)
374
+ } ?: false
366
375
  // Keep the first Market row at its current pixel offset when it is
367
376
  // reordered. RecyclerView otherwise follows the previous first key.
368
377
  val marketStartOffset = if (
@@ -390,7 +399,7 @@ class NativeListView(
390
399
  if (marketStartOffset != null) {
391
400
  layoutManager.scrollToPositionWithOffset(0, marketStartOffset)
392
401
  }
393
- relayoutContents()
402
+ relayoutContents(preserveMarketPaginationAnchor)
394
403
  performPendingScrollIfNeeded()
395
404
  syncSectionIndexToVisibleRows()
396
405
  scheduleVisibleEvent()
@@ -402,6 +411,40 @@ class NativeListView(
402
411
  updateReordering(next)
403
412
  }
404
413
 
414
+ private fun captureVisibleMarketAnchor(): VisibleMarketAnchor? {
415
+ val position = layoutManager.findFirstVisibleItemPosition()
416
+ if (position == RecyclerView.NO_POSITION) return null
417
+ val item = adapter.itemAt(position) ?: return null
418
+ val view = layoutManager.findViewByPosition(position) ?: return null
419
+ return VisibleMarketAnchor(
420
+ key = item.key,
421
+ offset = layoutManager.getDecoratedTop(view) - recyclerView.paddingTop,
422
+ )
423
+ }
424
+
425
+ private fun isMarketPaginationUpdate(
426
+ previous: NativeListConfig,
427
+ next: NativeListConfig,
428
+ ): Boolean {
429
+ if (!previous.loadMore && !next.loadMore) return false
430
+ val previousRows = previous.items.dropLastWhile {
431
+ it.key in MARKET_PAGINATION_KEYS
432
+ }
433
+ val nextRows = next.items.dropLastWhile {
434
+ it.key in MARKET_PAGINATION_KEYS
435
+ }
436
+ if (previousRows.isEmpty() || previousRows.size > nextRows.size) return false
437
+ if (
438
+ previousRows.firstOrNull()?.type != "market" ||
439
+ nextRows.firstOrNull()?.type != "market"
440
+ ) {
441
+ return false
442
+ }
443
+ return previousRows.indices.all { index ->
444
+ previousRows[index].key == nextRows[index].key
445
+ }
446
+ }
447
+
405
448
  /**
406
449
  * A controlled selection update sends the snapshot back after the native
407
450
  * selection delta. Its structure is unchanged; only selectedKeys and the
@@ -1839,15 +1882,36 @@ class NativeListView(
1839
1882
  * already-sized host once after a committed data update so the native rows
1840
1883
  * are measured and rebound without rebuilding the adapter.
1841
1884
  */
1842
- private fun relayoutContents() {
1885
+ private fun relayoutContents(preserveMarketPaginationAnchor: Boolean = false) {
1843
1886
  post {
1844
1887
  if (disposed || width <= 0 || height <= 0) return@post
1888
+ val anchor = if (preserveMarketPaginationAnchor) {
1889
+ captureVisibleMarketAnchor()
1890
+ } else {
1891
+ null
1892
+ }
1893
+ val anchorIndex = anchor?.let { currentAnchor ->
1894
+ adapter.currentList.indexOfFirst { it.key == currentAnchor.key }
1895
+ } ?: RecyclerView.NO_POSITION
1896
+ if (anchor != null && anchorIndex >= 0) {
1897
+ layoutManager.scrollToPositionWithOffset(anchorIndex, anchor.offset)
1898
+ }
1845
1899
  forceLayout()
1846
1900
  measure(
1847
1901
  MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
1848
1902
  MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
1849
1903
  )
1850
1904
  layout(left, top, right, bottom)
1905
+ if (anchorIndex >= 0) {
1906
+ val now = SystemClock.uptimeMillis()
1907
+ if (now - lastMarketPaginationAnchorLogAtMs >= 1_000L) {
1908
+ lastMarketPaginationAnchorLogAtMs = now
1909
+ OneKeyLog.debug(
1910
+ "NativeList",
1911
+ "market-pagination-anchor-restored generation=${config?.generation} index=$anchorIndex",
1912
+ )
1913
+ }
1914
+ }
1851
1915
  }
1852
1916
  }
1853
1917
 
@@ -1937,6 +2001,11 @@ class NativeListView(
1937
2001
  private fun sectionIndexDp(value: Int): Int = (value * density).roundToInt()
1938
2002
 
1939
2003
  companion object {
2004
+ private val MARKET_PAGINATION_KEYS = setOf(
2005
+ "market-loading-more",
2006
+ "market-load-more-retry",
2007
+ "market-end",
2008
+ )
1940
2009
  private val MARKET_QUOTE_FIELDS = setOf(
1941
2010
  "revision",
1942
2011
  "price",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-native-list",
3
- "version": "3.0.121",
3
+ "version": "3.0.123",
4
4
  "description": "Template-driven native RecyclerView and UICollectionView for React Native",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./lib/module/index.js",
@@ -83,8 +83,8 @@
83
83
  "typescript": "^5.9.2"
84
84
  },
85
85
  "peerDependencies": {
86
- "@onekeyfe/react-native-image": "3.0.121",
87
- "@onekeyfe/react-native-native-logger": "3.0.121",
86
+ "@onekeyfe/react-native-image": "3.0.123",
87
+ "@onekeyfe/react-native-native-logger": "3.0.123",
88
88
  "react": "*",
89
89
  "react-native": "*",
90
90
  "react-native-nitro-modules": "0.37.0"