@onekeyfe/react-native-native-list 3.0.117 → 3.0.119
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 +6 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +72 -22
- package/ios/NativeListCell.swift +97 -26
- package/ios/RNCNativeListView.swift +250 -23
- package/lib/module/NativeList.js +12 -8
- package/lib/module/NativeList.web.js +14 -0
- package/lib/module/index.js +1 -1
- package/lib/module/web/NativeListWebEngine.js +112 -18
- package/lib/typescript/src/NativeList.d.ts +2 -0
- package/lib/typescript/src/NativeList.web.d.ts +2 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/models.d.ts +1 -0
- package/package.json +3 -3
- package/src/NativeList.tsx +23 -15
- package/src/NativeList.web.tsx +38 -1
- package/src/index.ts +1 -1
- package/src/models.ts +1 -0
- package/src/web/NativeListWebEngine.ts +161 -28
|
@@ -94,7 +94,10 @@ final class NativeListView: UIView {
|
|
|
94
94
|
private var interactiveReorderCompactKey: String?
|
|
95
95
|
private var interactiveReorderUsesAtomicTargeting = false
|
|
96
96
|
private var interactiveReorderTargetIndex: Int?
|
|
97
|
+
private var interactiveReorderTargetKey: String?
|
|
98
|
+
private var interactiveReorderLockedCrossAxisPosition: CGFloat?
|
|
97
99
|
private var interactiveReorderTransformedCells: [NativeListCell] = []
|
|
100
|
+
private var deferredReorderReconfigureKeys = Set<String>()
|
|
98
101
|
private let interactiveReorderPlaceholder = UIView()
|
|
99
102
|
private var interactiveReorderAnimator: UIViewPropertyAnimator?
|
|
100
103
|
private let reorderStartFeedback = UIImpactFeedbackGenerator(style: .medium)
|
|
@@ -248,14 +251,20 @@ final class NativeListView: UIView {
|
|
|
248
251
|
guard let next = try? NativeListConfig.parse(json: json) else { return }
|
|
249
252
|
invalidateActionAnchor(reason: "snapshot")
|
|
250
253
|
if let current = config, isControlledSelectionSnapshotUpdate(from: current, to: next) {
|
|
251
|
-
let changedSummaryKeys = Set(zip(current.items, next.items).compactMap { old, new in
|
|
252
|
-
old.content != new.content ? new.key : nil
|
|
253
|
-
})
|
|
254
254
|
config = next
|
|
255
255
|
itemsByKey = Dictionary(uniqueKeysWithValues: next.items.map { ($0.key, $0) })
|
|
256
|
-
|
|
256
|
+
// OneKey patch: A controlled selection echo is fully handled by the
|
|
257
|
+
// lightweight updater. Rebinding unchanged visuals clears cached images.
|
|
258
|
+
refreshVisibleSelection()
|
|
257
259
|
return
|
|
258
260
|
}
|
|
261
|
+
if let current = config, interactiveReorderSource != nil {
|
|
262
|
+
if canDeferSnapshotDuringInteractiveReorder(from: current, to: next) {
|
|
263
|
+
deferSnapshotDuringInteractiveReorder(from: current, to: next)
|
|
264
|
+
return
|
|
265
|
+
}
|
|
266
|
+
cancelInteractiveReorderForStructuralUpdate()
|
|
267
|
+
}
|
|
259
268
|
let oldItems = itemsByKey
|
|
260
269
|
let themeChanged = !dictionariesEqual(config?.theme, next.theme)
|
|
261
270
|
if config?.generation != next.generation { endReachedGeneration = nil }
|
|
@@ -270,11 +279,13 @@ final class NativeListView: UIView {
|
|
|
270
279
|
snapshot.appendSections([0])
|
|
271
280
|
let keys = next.items.map(\.key)
|
|
272
281
|
snapshot.appendItems(keys, toSection: 0)
|
|
273
|
-
|
|
282
|
+
var changedKeys = Set(keys.filter { key in
|
|
274
283
|
guard let old = oldItems[key], let new = itemsByKey[key] else { return false }
|
|
275
284
|
return themeChanged || old.revision != new.revision || old.content != new.content
|
|
276
|
-
}
|
|
277
|
-
|
|
285
|
+
})
|
|
286
|
+
changedKeys.formUnion(deferredReorderReconfigureKeys)
|
|
287
|
+
deferredReorderReconfigureKeys.removeAll()
|
|
288
|
+
snapshot.reconfigureItems(changedKeys.filter { itemsByKey[$0] != nil })
|
|
278
289
|
dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in
|
|
279
290
|
guard let self else { return }
|
|
280
291
|
self.collectionView.layoutIfNeeded()
|
|
@@ -301,9 +312,10 @@ final class NativeListView: UIView {
|
|
|
301
312
|
pending.append((index, changes))
|
|
302
313
|
}
|
|
303
314
|
|
|
304
|
-
var changedKeys
|
|
315
|
+
var changedKeys = Set<String>()
|
|
305
316
|
var marketQuoteKeys = Set<String>()
|
|
306
317
|
var sizeChanged = false
|
|
318
|
+
var reorderStructureChanged = false
|
|
307
319
|
let marketQuoteFields: Set<String> = [
|
|
308
320
|
"revision", "price", "priceSegments", "change", "accessibilityLabel",
|
|
309
321
|
]
|
|
@@ -314,22 +326,37 @@ final class NativeListView: UIView {
|
|
|
314
326
|
if key != "key" && key != "type" { merged[key] = value }
|
|
315
327
|
}
|
|
316
328
|
guard let item = try? NativeListItem(data: merged) else { return }
|
|
317
|
-
if rowHeight(
|
|
329
|
+
if rowHeight(previous, usingCompactReorderHeight: false) != rowHeight(item, usingCompactReorderHeight: false) ||
|
|
330
|
+
current.orientation == "horizontal" && previous.type == "rail" && railWidth(previous) != railWidth(item) ||
|
|
331
|
+
current.sectionIndexEnabled && previous.type == "sectionHeader" &&
|
|
332
|
+
previous.data.string("indexTitle").isEmpty != item.data.string("indexTitle").isEmpty {
|
|
333
|
+
sizeChanged = true
|
|
334
|
+
}
|
|
335
|
+
if previous.sectionKey != item.sectionKey ||
|
|
336
|
+
previous.isReorderable != item.isReorderable ||
|
|
337
|
+
!hasSameNestedReorderStructure(previous, item) ||
|
|
338
|
+
previous.type == "identity" &&
|
|
339
|
+
previous.data.string("presentation") != item.data.string("presentation") {
|
|
340
|
+
reorderStructureChanged = true
|
|
341
|
+
}
|
|
318
342
|
current.items[index] = item
|
|
319
343
|
if previous.type == "market", Set(changes.keys).isSubset(of: marketQuoteFields) {
|
|
320
344
|
marketQuoteKeys.insert(item.key)
|
|
321
345
|
} else {
|
|
322
|
-
changedKeys.
|
|
346
|
+
changedKeys.insert(item.key)
|
|
323
347
|
}
|
|
324
348
|
if let selected = changes["selected"] as? Bool {
|
|
325
349
|
if selected { current.selectedKeys.insert(item.key) } else { current.selectedKeys.remove(item.key) }
|
|
326
350
|
}
|
|
327
351
|
}
|
|
328
352
|
if !changedKeys.isEmpty { invalidateActionAnchor(reason: "snapshot") }
|
|
353
|
+
if interactiveReorderSource != nil, sizeChanged || reorderStructureChanged {
|
|
354
|
+
cancelInteractiveReorderForStructuralUpdate()
|
|
355
|
+
}
|
|
329
356
|
config = current
|
|
330
357
|
itemsByKey = Dictionary(uniqueKeysWithValues: current.items.map { ($0.key, $0) })
|
|
331
358
|
for indexPath in collectionView.indexPathsForVisibleItems {
|
|
332
|
-
guard let item =
|
|
359
|
+
guard let item = item(at: indexPath),
|
|
333
360
|
marketQuoteKeys.contains(item.key),
|
|
334
361
|
let cell = collectionView.cellForItem(at: indexPath) as? NativeListCell else { continue }
|
|
335
362
|
cell.updateMarketQuote(item, theme: current.theme)
|
|
@@ -340,6 +367,13 @@ final class NativeListView: UIView {
|
|
|
340
367
|
checkEndReached()
|
|
341
368
|
return
|
|
342
369
|
}
|
|
370
|
+
if interactiveReorderSource != nil {
|
|
371
|
+
deferredReorderReconfigureKeys.formUnion(changedKeys)
|
|
372
|
+
configureFooter(current)
|
|
373
|
+
return
|
|
374
|
+
}
|
|
375
|
+
changedKeys.formUnion(deferredReorderReconfigureKeys)
|
|
376
|
+
deferredReorderReconfigureKeys.removeAll()
|
|
343
377
|
var snapshot = dataSource.snapshot()
|
|
344
378
|
snapshot.reconfigureItems(changedKeys.filter { snapshot.indexOfItem($0) != nil })
|
|
345
379
|
dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in
|
|
@@ -568,6 +602,9 @@ final class NativeListView: UIView {
|
|
|
568
602
|
let cell = collectionView.cellForItem(at: indexPath) as? NativeListCell else {
|
|
569
603
|
interactiveReorderSource = nil
|
|
570
604
|
interactiveReorderCell = nil
|
|
605
|
+
interactiveReorderTargetIndex = nil
|
|
606
|
+
interactiveReorderTargetKey = nil
|
|
607
|
+
interactiveReorderLockedCrossAxisPosition = nil
|
|
571
608
|
return
|
|
572
609
|
}
|
|
573
610
|
// OneKey patch: a hidden wallet child starts dragging its whole logical group.
|
|
@@ -582,6 +619,11 @@ final class NativeListView: UIView {
|
|
|
582
619
|
item.data.string("presentation") == "walletSidebar" &&
|
|
583
620
|
current.items.contains { $0.type == "walletGroup" }
|
|
584
621
|
interactiveReorderTargetIndex = indexPath.item
|
|
622
|
+
interactiveReorderTargetKey = item.key
|
|
623
|
+
let sourceCenter = flowLayout.layoutAttributesForItem(at: indexPath)?.center ?? cell.center
|
|
624
|
+
interactiveReorderLockedCrossAxisPosition = current.orientation == "horizontal"
|
|
625
|
+
? sourceCenter.y
|
|
626
|
+
: sourceCenter.x
|
|
585
627
|
if item.type == "walletGroup" {
|
|
586
628
|
interactiveReorderCompactKey = item.key
|
|
587
629
|
cell.setWalletGroupReorderCompact(true)
|
|
@@ -597,6 +639,8 @@ final class NativeListView: UIView {
|
|
|
597
639
|
interactiveReorderCell = nil
|
|
598
640
|
interactiveReorderUsesAtomicTargeting = false
|
|
599
641
|
interactiveReorderTargetIndex = nil
|
|
642
|
+
interactiveReorderTargetKey = nil
|
|
643
|
+
interactiveReorderLockedCrossAxisPosition = nil
|
|
600
644
|
interactiveReorderFeedbackIndex = nil
|
|
601
645
|
flowLayout.invalidateLayout()
|
|
602
646
|
collectionView.layoutIfNeeded()
|
|
@@ -609,13 +653,20 @@ final class NativeListView: UIView {
|
|
|
609
653
|
showInteractiveReorderPlaceholder(at: indexPath, item: item, config: current)
|
|
610
654
|
case .changed:
|
|
611
655
|
guard interactiveReorderSource != nil else { return }
|
|
612
|
-
|
|
656
|
+
var point = gesture.location(in: collectionView)
|
|
657
|
+
if let lockedPosition = interactiveReorderLockedCrossAxisPosition {
|
|
658
|
+
if config?.orientation == "horizontal" {
|
|
659
|
+
point.y = lockedPosition
|
|
660
|
+
} else {
|
|
661
|
+
point.x = lockedPosition
|
|
662
|
+
}
|
|
663
|
+
}
|
|
613
664
|
collectionView.updateInteractiveMovementTargetPosition(point)
|
|
614
665
|
if let indexPath = nearestReorderIndexPath(to: point),
|
|
615
666
|
let item = item(at: indexPath) {
|
|
616
667
|
let targetChanged = interactiveReorderFeedbackIndex != indexPath.item
|
|
617
668
|
if interactiveReorderUsesAtomicTargeting {
|
|
618
|
-
updateAtomicReorderTarget(to: indexPath.item, animated: targetChanged)
|
|
669
|
+
updateAtomicReorderTarget(to: indexPath.item, key: item.key, animated: targetChanged)
|
|
619
670
|
}
|
|
620
671
|
if targetChanged {
|
|
621
672
|
interactiveReorderFeedbackIndex = indexPath.item
|
|
@@ -629,6 +680,7 @@ final class NativeListView: UIView {
|
|
|
629
680
|
interactiveReorderCell?.setPressed(false)
|
|
630
681
|
finishInteractiveReorder(cancelled: false)
|
|
631
682
|
case .cancelled, .failed:
|
|
683
|
+
guard interactiveReorderSource != nil else { return }
|
|
632
684
|
interactiveReorderCell?.setPressed(false)
|
|
633
685
|
finishInteractiveReorder(cancelled: true)
|
|
634
686
|
default:
|
|
@@ -637,10 +689,17 @@ final class NativeListView: UIView {
|
|
|
637
689
|
}
|
|
638
690
|
|
|
639
691
|
private func nearestReorderIndexPath(to point: CGPoint) -> IndexPath? {
|
|
640
|
-
|
|
692
|
+
let isHorizontal = config?.orientation == "horizontal"
|
|
693
|
+
return collectionView.indexPathsForVisibleItems.min { lhs, rhs in
|
|
641
694
|
let lhsFrame = flowLayout.layoutAttributesForItem(at: lhs)?.frame ?? .zero
|
|
642
695
|
let rhsFrame = flowLayout.layoutAttributesForItem(at: rhs)?.frame ?? .zero
|
|
643
|
-
|
|
696
|
+
let lhsDistance = isHorizontal
|
|
697
|
+
? abs(lhsFrame.midX - point.x)
|
|
698
|
+
: abs(lhsFrame.midY - point.y)
|
|
699
|
+
let rhsDistance = isHorizontal
|
|
700
|
+
? abs(rhsFrame.midX - point.x)
|
|
701
|
+
: abs(rhsFrame.midY - point.y)
|
|
702
|
+
return lhsDistance < rhsDistance
|
|
644
703
|
}
|
|
645
704
|
}
|
|
646
705
|
|
|
@@ -686,6 +745,7 @@ final class NativeListView: UIView {
|
|
|
686
745
|
}
|
|
687
746
|
|
|
688
747
|
private func finishInteractiveReorder(cancelled: Bool) {
|
|
748
|
+
interactiveReorderLockedCrossAxisPosition = nil
|
|
689
749
|
if interactiveReorderUsesAtomicTargeting {
|
|
690
750
|
finishAtomicInteractiveReorder(cancelled: cancelled)
|
|
691
751
|
return
|
|
@@ -716,12 +776,58 @@ final class NativeListView: UIView {
|
|
|
716
776
|
} else {
|
|
717
777
|
self.interactiveReorderSource = nil
|
|
718
778
|
self.interactiveReorderCell = nil
|
|
779
|
+
self.interactiveReorderTargetIndex = nil
|
|
780
|
+
self.interactiveReorderTargetKey = nil
|
|
781
|
+
self.scheduleDeferredReorderRefresh()
|
|
719
782
|
}
|
|
720
783
|
}
|
|
721
784
|
interactiveReorderAnimator = animator
|
|
722
785
|
animator.startAnimation()
|
|
723
786
|
}
|
|
724
787
|
|
|
788
|
+
private func cancelInteractiveReorderForStructuralUpdate() {
|
|
789
|
+
guard interactiveReorderSource != nil else { return }
|
|
790
|
+
interactiveReorderAnimator?.stopAnimation(true)
|
|
791
|
+
interactiveReorderAnimator = nil
|
|
792
|
+
collectionView.cancelInteractiveMovement()
|
|
793
|
+
interactiveReorderCell?.setPressed(false)
|
|
794
|
+
if interactiveReorderCompactKey != nil {
|
|
795
|
+
interactiveReorderCell?.setWalletGroupReorderCompact(false)
|
|
796
|
+
interactiveReorderCompactKey = nil
|
|
797
|
+
flowLayout.invalidateLayout()
|
|
798
|
+
}
|
|
799
|
+
resetAtomicReorderTransforms()
|
|
800
|
+
interactiveReorderSource = nil
|
|
801
|
+
interactiveReorderCell = nil
|
|
802
|
+
interactiveReorderFeedbackIndex = nil
|
|
803
|
+
clearAtomicInteractiveReorderState()
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
private func scheduleDeferredReorderRefresh() {
|
|
807
|
+
guard !deferredReorderReconfigureKeys.isEmpty else { return }
|
|
808
|
+
DispatchQueue.main.async { [weak self] in
|
|
809
|
+
self?.flushDeferredReorderRefresh()
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
private func flushDeferredReorderRefresh() {
|
|
814
|
+
guard interactiveReorderSource == nil,
|
|
815
|
+
interactiveReorderCompactKey == nil,
|
|
816
|
+
interactiveReorderAnimator == nil,
|
|
817
|
+
!deferredReorderReconfigureKeys.isEmpty else { return }
|
|
818
|
+
var snapshot = dataSource.snapshot()
|
|
819
|
+
let keys = deferredReorderReconfigureKeys.filter { snapshot.indexOfItem($0) != nil }
|
|
820
|
+
deferredReorderReconfigureKeys.removeAll()
|
|
821
|
+
guard !keys.isEmpty else { return }
|
|
822
|
+
snapshot.reconfigureItems(Array(keys))
|
|
823
|
+
dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in
|
|
824
|
+
guard let self else { return }
|
|
825
|
+
self.collectionView.layoutIfNeeded()
|
|
826
|
+
self.emitVisibleRangeIfNeeded()
|
|
827
|
+
self.checkEndReached()
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
725
831
|
private func completeInteractiveReorder(
|
|
726
832
|
_ snapshot: NSDiffableDataSourceSnapshot<Int, String>
|
|
727
833
|
) {
|
|
@@ -730,6 +836,10 @@ final class NativeListView: UIView {
|
|
|
730
836
|
defer {
|
|
731
837
|
interactiveReorderSource = nil
|
|
732
838
|
interactiveReorderCell = nil
|
|
839
|
+
interactiveReorderTargetIndex = nil
|
|
840
|
+
interactiveReorderTargetKey = nil
|
|
841
|
+
interactiveReorderLockedCrossAxisPosition = nil
|
|
842
|
+
scheduleDeferredReorderRefresh()
|
|
733
843
|
}
|
|
734
844
|
let keys = snapshot.itemIdentifiers
|
|
735
845
|
let items = keys.compactMap { itemsByKey[$0] }
|
|
@@ -769,6 +879,10 @@ final class NativeListView: UIView {
|
|
|
769
879
|
self.walletGroupCell(for: key)?.finishWalletGroupReorderExpansion()
|
|
770
880
|
self.interactiveReorderSource = nil
|
|
771
881
|
self.interactiveReorderCell = nil
|
|
882
|
+
self.interactiveReorderTargetIndex = nil
|
|
883
|
+
self.interactiveReorderTargetKey = nil
|
|
884
|
+
self.interactiveReorderAnimator = nil
|
|
885
|
+
self.scheduleDeferredReorderRefresh()
|
|
772
886
|
}
|
|
773
887
|
interactiveReorderAnimator = animator
|
|
774
888
|
animator.startAnimation()
|
|
@@ -786,9 +900,10 @@ final class NativeListView: UIView {
|
|
|
786
900
|
return config?.items[safe: indexPath.item]
|
|
787
901
|
}
|
|
788
902
|
|
|
789
|
-
private func updateAtomicReorderTarget(to targetIndex: Int, animated: Bool) {
|
|
903
|
+
private func updateAtomicReorderTarget(to targetIndex: Int, key targetKey: String, animated: Bool) {
|
|
790
904
|
guard let source = interactiveReorderSource else { return }
|
|
791
905
|
interactiveReorderTargetIndex = targetIndex
|
|
906
|
+
interactiveReorderTargetKey = targetKey
|
|
792
907
|
let distance = CGFloat(68) + flowLayout.minimumLineSpacing
|
|
793
908
|
let updates = { [weak self] in
|
|
794
909
|
guard let self else { return }
|
|
@@ -828,17 +943,28 @@ final class NativeListView: UIView {
|
|
|
828
943
|
|
|
829
944
|
private func finishAtomicInteractiveReorder(cancelled: Bool) {
|
|
830
945
|
interactiveReorderAnimator?.stopAnimation(true)
|
|
946
|
+
interactiveReorderAnimator = nil
|
|
831
947
|
interactiveReorderFeedbackIndex = nil
|
|
832
948
|
guard let source = interactiveReorderSource else { return }
|
|
833
|
-
let targetIndex = interactiveReorderTargetIndex ?? source.index
|
|
834
949
|
collectionView.cancelInteractiveMovement()
|
|
835
950
|
|
|
836
|
-
if !cancelled
|
|
951
|
+
if !cancelled {
|
|
837
952
|
var snapshot = dataSource.snapshot()
|
|
838
953
|
let keys = snapshot.itemIdentifiers
|
|
839
|
-
|
|
954
|
+
guard let targetKey = interactiveReorderTargetKey,
|
|
955
|
+
targetKey != source.key,
|
|
956
|
+
let sourceIndex = keys.firstIndex(of: source.key),
|
|
957
|
+
let targetIndex = keys.firstIndex(of: targetKey) else {
|
|
958
|
+
resetAtomicReorderTransforms()
|
|
959
|
+
interactiveReorderSource = nil
|
|
960
|
+
interactiveReorderCell = nil
|
|
961
|
+
clearAtomicInteractiveReorderState()
|
|
962
|
+
scheduleDeferredReorderRefresh()
|
|
963
|
+
return
|
|
964
|
+
}
|
|
965
|
+
if targetIndex < sourceIndex {
|
|
840
966
|
snapshot.moveItem(source.key, beforeItem: targetKey)
|
|
841
|
-
} else
|
|
967
|
+
} else {
|
|
842
968
|
snapshot.moveItem(source.key, afterItem: targetKey)
|
|
843
969
|
}
|
|
844
970
|
dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in
|
|
@@ -853,12 +979,15 @@ final class NativeListView: UIView {
|
|
|
853
979
|
interactiveReorderSource = nil
|
|
854
980
|
interactiveReorderCell = nil
|
|
855
981
|
clearAtomicInteractiveReorderState()
|
|
982
|
+
scheduleDeferredReorderRefresh()
|
|
856
983
|
}
|
|
857
984
|
}
|
|
858
985
|
|
|
859
986
|
private func clearAtomicInteractiveReorderState() {
|
|
860
987
|
interactiveReorderUsesAtomicTargeting = false
|
|
861
988
|
interactiveReorderTargetIndex = nil
|
|
989
|
+
interactiveReorderTargetKey = nil
|
|
990
|
+
interactiveReorderLockedCrossAxisPosition = nil
|
|
862
991
|
interactiveReorderPlaceholder.isHidden = true
|
|
863
992
|
interactiveReorderPlaceholder.alpha = 1
|
|
864
993
|
}
|
|
@@ -1132,7 +1261,7 @@ final class NativeListView: UIView {
|
|
|
1132
1261
|
self?.resolveCheckboxState(item: item, target: target, fallback: fallback) ?? fallback
|
|
1133
1262
|
}
|
|
1134
1263
|
for indexPath in collectionView.indexPathsForVisibleItems {
|
|
1135
|
-
guard let item =
|
|
1264
|
+
guard let item = item(at: indexPath),
|
|
1136
1265
|
let cell = collectionView.cellForItem(at: indexPath) as? NativeListCell else { continue }
|
|
1137
1266
|
if changedSummaryKeys.contains(item.key) {
|
|
1138
1267
|
bind(cell: cell, item: item, itemIndex: indexPath.item)
|
|
@@ -1201,6 +1330,101 @@ final class NativeListView: UIView {
|
|
|
1201
1330
|
}
|
|
1202
1331
|
}
|
|
1203
1332
|
|
|
1333
|
+
private func canDeferSnapshotDuringInteractiveReorder(
|
|
1334
|
+
from current: NativeListConfig,
|
|
1335
|
+
to next: NativeListConfig
|
|
1336
|
+
) -> Bool {
|
|
1337
|
+
guard current.layout == next.layout,
|
|
1338
|
+
current.orientation == next.orientation,
|
|
1339
|
+
current.gridColumns == next.gridColumns,
|
|
1340
|
+
current.stickyHeaders == next.stickyHeaders,
|
|
1341
|
+
current.contentPadding == next.contentPadding,
|
|
1342
|
+
current.contentPaddingHorizontal == next.contentPaddingHorizontal,
|
|
1343
|
+
current.contentPaddingTop == next.contentPaddingTop,
|
|
1344
|
+
current.contentPaddingBottom == next.contentPaddingBottom,
|
|
1345
|
+
current.itemSpacing == next.itemSpacing,
|
|
1346
|
+
current.reorderable == next.reorderable,
|
|
1347
|
+
current.sectionIndexEnabled == next.sectionIndexEnabled,
|
|
1348
|
+
current.items.count == next.items.count,
|
|
1349
|
+
hasSameFooterGeometry(current.fixedFooter, next.fixedFooter) else { return false }
|
|
1350
|
+
|
|
1351
|
+
return zip(current.items, next.items).allSatisfy { old, new in
|
|
1352
|
+
guard old.key == new.key,
|
|
1353
|
+
old.type == new.type,
|
|
1354
|
+
old.sectionKey == new.sectionKey,
|
|
1355
|
+
old.isReorderable == new.isReorderable,
|
|
1356
|
+
hasSameNestedReorderStructure(old, new),
|
|
1357
|
+
rowHeight(old, usingCompactReorderHeight: false) == rowHeight(new, usingCompactReorderHeight: false) else {
|
|
1358
|
+
return false
|
|
1359
|
+
}
|
|
1360
|
+
if current.orientation == "horizontal", old.type == "rail", railWidth(old) != railWidth(new) {
|
|
1361
|
+
return false
|
|
1362
|
+
}
|
|
1363
|
+
if old.type == "identity", old.data.string("presentation") != new.data.string("presentation") {
|
|
1364
|
+
return false
|
|
1365
|
+
}
|
|
1366
|
+
if current.sectionIndexEnabled,
|
|
1367
|
+
old.type == "sectionHeader",
|
|
1368
|
+
old.data.string("indexTitle").isEmpty != new.data.string("indexTitle").isEmpty {
|
|
1369
|
+
return false
|
|
1370
|
+
}
|
|
1371
|
+
return true
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
private func hasSameNestedReorderStructure(_ current: NativeListItem, _ next: NativeListItem) -> Bool {
|
|
1376
|
+
guard current.type == "walletGroup" else { return true }
|
|
1377
|
+
guard let currentParent = current.data.dictionary("parent"),
|
|
1378
|
+
let nextParent = next.data.dictionary("parent") else { return false }
|
|
1379
|
+
let currentMembers = [currentParent] + current.data.dictionaries("children")
|
|
1380
|
+
let nextMembers = [nextParent] + next.data.dictionaries("children")
|
|
1381
|
+
guard currentMembers.count == nextMembers.count else { return false }
|
|
1382
|
+
return zip(currentMembers, nextMembers).allSatisfy { old, new in
|
|
1383
|
+
old.string("key") == new.string("key") &&
|
|
1384
|
+
old.string("type") == new.string("type") &&
|
|
1385
|
+
old.string("sectionKey") == new.string("sectionKey") &&
|
|
1386
|
+
old.string("presentation") == new.string("presentation")
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
private func hasSameFooterGeometry(_ current: NativeListItem?, _ next: NativeListItem?) -> Bool {
|
|
1391
|
+
switch (current, next) {
|
|
1392
|
+
case (nil, nil):
|
|
1393
|
+
return true
|
|
1394
|
+
case let (current?, next?):
|
|
1395
|
+
return current.key == next.key &&
|
|
1396
|
+
current.type == next.type &&
|
|
1397
|
+
rowHeight(current, usingCompactReorderHeight: false) == rowHeight(next, usingCompactReorderHeight: false)
|
|
1398
|
+
default:
|
|
1399
|
+
return false
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
private func deferSnapshotDuringInteractiveReorder(
|
|
1404
|
+
from current: NativeListConfig,
|
|
1405
|
+
to next: NativeListConfig
|
|
1406
|
+
) {
|
|
1407
|
+
let oldItems = itemsByKey
|
|
1408
|
+
let themeChanged = !dictionariesEqual(current.theme, next.theme)
|
|
1409
|
+
let selectionChanged = current.selectionMode != next.selectionMode ||
|
|
1410
|
+
current.selectedKeys != next.selectedKeys
|
|
1411
|
+
if current.generation != next.generation { endReachedGeneration = nil }
|
|
1412
|
+
config = next
|
|
1413
|
+
itemsByKey = Dictionary(uniqueKeysWithValues: next.items.map { ($0.key, $0) })
|
|
1414
|
+
let changedKeys = next.items.compactMap { item -> String? in
|
|
1415
|
+
guard let old = oldItems[item.key] else { return nil }
|
|
1416
|
+
return themeChanged || selectionChanged || old.revision != item.revision || old.content != item.content
|
|
1417
|
+
? item.key
|
|
1418
|
+
: nil
|
|
1419
|
+
}
|
|
1420
|
+
deferredReorderReconfigureKeys.formUnion(changedKeys)
|
|
1421
|
+
configureSectionIndex(next)
|
|
1422
|
+
configureRefresh(next)
|
|
1423
|
+
configureFooter(next)
|
|
1424
|
+
emitVisibleRangeIfNeeded()
|
|
1425
|
+
checkEndReached()
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1204
1428
|
// OneKey patch: remove only fields that the existing lightweight binder refreshes.
|
|
1205
1429
|
private func selectionComparisonData(_ data: [String: Any], controlled: Bool) -> [String: Any]? {
|
|
1206
1430
|
guard let type = data["type"] as? String else { return nil }
|
|
@@ -1264,7 +1488,10 @@ final class NativeListView: UIView {
|
|
|
1264
1488
|
return try? JSONSerialization.data(withJSONObject: value, options: [.sortedKeys])
|
|
1265
1489
|
}
|
|
1266
1490
|
|
|
1267
|
-
private func rowHeight(
|
|
1491
|
+
private func rowHeight(
|
|
1492
|
+
_ item: NativeListItem,
|
|
1493
|
+
usingCompactReorderHeight: Bool = true
|
|
1494
|
+
) -> CGFloat {
|
|
1268
1495
|
// OneKey patch: honor selector baseline geometry; keep compact drag sizing.
|
|
1269
1496
|
if item.type != "walletGroup", item.data["height"] != nil { return CGFloat(item.data.double("height")) }
|
|
1270
1497
|
if item.type == "system", item.data.string("variant") == "spacer" {
|
|
@@ -1282,7 +1509,7 @@ final class NativeListView: UIView {
|
|
|
1282
1509
|
return 32 + textHeight("title", weight: .medium) + textHeight("message", weight: .regular)
|
|
1283
1510
|
}
|
|
1284
1511
|
if item.type == "walletGroup" {
|
|
1285
|
-
if item.key == interactiveReorderCompactKey { return 68 }
|
|
1512
|
+
if usingCompactReorderHeight, item.key == interactiveReorderCompactKey { return 68 }
|
|
1286
1513
|
let childCount = item.data.dictionaries("children").count
|
|
1287
1514
|
// OneKey patch: wallet badges contribute their own member heights.
|
|
1288
1515
|
// return CGFloat((childCount + 1) * 68 + childCount * 12)
|
package/lib/module/NativeList.js
CHANGED
|
@@ -10,6 +10,17 @@ import { applyRowPatches, serializePatches, serializeSnapshot } from "./validati
|
|
|
10
10
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
11
|
const NativeListConfig = require('../nitrogen/generated/shared/json/NativeListConfig.json');
|
|
12
12
|
const NativeListHost = getHostComponent('NativeList', () => NativeListConfig);
|
|
13
|
+
export function preloadNativeListAvatarImages(sources) {
|
|
14
|
+
return OneKeyImageCache.preload(sources.map(source => ({
|
|
15
|
+
uri: source.uri,
|
|
16
|
+
headers: source.headers,
|
|
17
|
+
resizeWidth: source.width,
|
|
18
|
+
resizeHeight: source.height,
|
|
19
|
+
optimizeTos: source.optimizeTos !== false,
|
|
20
|
+
overscan: source.overscan,
|
|
21
|
+
cachePolicy: source.cachePolicy === 'memory' ? OneKeyImageCachePolicy.MEMORY : source.cachePolicy === 'disk' ? OneKeyImageCachePolicy.DISK : OneKeyImageCachePolicy.MEMORY_DISK
|
|
22
|
+
})));
|
|
23
|
+
}
|
|
13
24
|
function parsePayload(payloadJson) {
|
|
14
25
|
return JSON.parse(payloadJson);
|
|
15
26
|
}
|
|
@@ -78,14 +89,7 @@ export const NativeList = /*#__PURE__*/forwardRef(function NativeList({
|
|
|
78
89
|
updateAvatarPrefetchRef.current = updateAvatarPrefetch;
|
|
79
90
|
useEffect(() => {
|
|
80
91
|
avatarLifecycle.current = 'mounted';
|
|
81
|
-
const queue = new NativeAvatarPrefetchQueue(source =>
|
|
82
|
-
uri: source.uri,
|
|
83
|
-
headers: source.headers,
|
|
84
|
-
resizeWidth: source.width,
|
|
85
|
-
resizeHeight: source.height,
|
|
86
|
-
optimizeTos: false,
|
|
87
|
-
cachePolicy: source.cachePolicy === 'memory' ? OneKeyImageCachePolicy.MEMORY : source.cachePolicy === 'disk' ? OneKeyImageCachePolicy.DISK : OneKeyImageCachePolicy.MEMORY_DISK
|
|
88
|
-
}]));
|
|
92
|
+
const queue = new NativeAvatarPrefetchQueue(source => preloadNativeListAvatarImages([source]));
|
|
89
93
|
avatarQueueRef.current = queue;
|
|
90
94
|
updateAvatarPrefetchRef.current();
|
|
91
95
|
return () => {
|
|
@@ -5,7 +5,21 @@ import { View } from 'react-native';
|
|
|
5
5
|
import { normalizeIndexScroll, normalizeKeyScroll, normalizePositionScroll, resolveLocationIndex, scrollFailure, validateOffset } from "./scrolling.js";
|
|
6
6
|
import { serializePatches, validateSnapshot } from "./validation.js";
|
|
7
7
|
import { NativeListWebEngine } from "./web/NativeListWebEngine.js";
|
|
8
|
+
import { acquireNativeListAvatar, canonicalNativeListAvatarUri } from "./web/NativeListWebAvatarCache.js";
|
|
8
9
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
export function preloadNativeListAvatarImages(sources) {
|
|
11
|
+
if (typeof document === 'undefined') return Promise.resolve(false);
|
|
12
|
+
const uris = [...new Set(sources.map(source => canonicalNativeListAvatarUri(source.uri)).filter(uri => uri !== undefined))];
|
|
13
|
+
if (!uris.length) return Promise.resolve(false);
|
|
14
|
+
return Promise.all(uris.map(uri => new Promise(resolve => {
|
|
15
|
+
let release;
|
|
16
|
+
const settle = success => {
|
|
17
|
+
resolve(success);
|
|
18
|
+
queueMicrotask(() => release?.());
|
|
19
|
+
};
|
|
20
|
+
release = acquireNativeListAvatar(document, uri, () => settle(true), () => settle(false), 0);
|
|
21
|
+
}))).then(results => results.every(Boolean));
|
|
22
|
+
}
|
|
9
23
|
export const NativeList = /*#__PURE__*/forwardRef(function NativeList({
|
|
10
24
|
snapshot,
|
|
11
25
|
webVirtualizationEnabled = true,
|
package/lib/module/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
export { NativeList } from './NativeList';
|
|
3
|
+
export { NativeList, preloadNativeListAvatarImages } from './NativeList';
|
|
4
4
|
export { applyRowPatches, serializePatches, serializeSnapshot, validatePatches, validateSnapshot } from "./validation.js";
|
|
5
5
|
export { checkboxStateForKeys, checkboxStateForSection, isSelectableRow, reduceSelection, selectionStateFromSnapshot } from "./selection.js";
|
|
6
6
|
//# sourceMappingURL=index.js.map
|