@onekeyfe/react-native-native-list 3.0.105 → 3.0.107
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/onekey/nativelist/NativeListAdapter.kt +16 -3
- package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +6 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +671 -52
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +282 -54
- package/ios/HybridNativeList.swift +8 -2
- package/ios/NativeListCell.swift +630 -31
- package/ios/NativeListDesignAssets.swift +24 -1
- package/ios/RNCNativeListView.swift +216 -42
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_account_error_custom.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_account_error_custom.imageset/icon.svg +14 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/icon.svg +12 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_apple_brand.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_apple_brand.imageset/icon.svg +6 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_bot_illus.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_bot_illus.imageset/icon.svg +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_crossed_small_solid.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_crossed_small_solid.imageset/icon.svg +3 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_globus_outline.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_globus_outline.imageset/icon.svg +7 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_google_illus.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_google_illus.imageset/icon.svg +18 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_lock_solid.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_lock_solid.imageset/icon.svg +7 -0
- package/lib/module/NativeList.js +102 -8
- package/lib/module/NativeList.web.js +20 -2
- package/lib/module/avatarPrefetch.js +174 -0
- package/lib/module/validation.js +45 -2
- package/lib/module/web/NativeListAvatarWorker.js +542 -0
- package/lib/module/web/NativeListWebAvatarCache.js +267 -0
- package/lib/module/web/NativeListWebEngine.js +958 -47
- package/lib/typescript/src/NativeList.web.d.ts +2 -2
- package/lib/typescript/src/avatarPrefetch.d.ts +28 -0
- package/lib/typescript/src/models.d.ts +72 -4
- package/lib/typescript/src/web/NativeListWebAvatarCache.d.ts +6 -0
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +10 -1
- package/package.json +2 -2
- package/src/NativeList.tsx +149 -13
- package/src/NativeList.web.tsx +30 -3
- package/src/avatarPrefetch.ts +236 -0
- package/src/models.ts +98 -7
- package/src/validation.ts +131 -2
- package/src/web/NativeListAvatarWorker.js +567 -0
- package/src/web/NativeListWebAvatarCache.ts +314 -0
- package/src/web/NativeListWebEngine.ts +1409 -60
|
@@ -76,8 +76,31 @@ func nativeListTabularFont(
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
func nativeListIcon(named name: String) -> UIImage? {
|
|
79
|
+
// OneKey patch: the connection indicator is a solid circle at its requested size.
|
|
80
|
+
if name == "Circle" {
|
|
81
|
+
return UIGraphicsImageRenderer(size: CGSize(width: 24, height: 24)).image { _ in
|
|
82
|
+
UIColor.black.setFill()
|
|
83
|
+
UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 24, height: 24)).fill()
|
|
84
|
+
}.withRenderingMode(.alwaysTemplate)
|
|
85
|
+
}
|
|
79
86
|
let assetName: String
|
|
80
87
|
switch name {
|
|
88
|
+
// OneKey patch: official selector icon geometry.
|
|
89
|
+
case "AccountErrorCustom": assetName = "onekey_selector_account_error_custom"
|
|
90
|
+
// OneKey patch: official selector icon geometry.
|
|
91
|
+
case "CrossedSmallSolid": assetName = "onekey_selector_crossed_small_solid"
|
|
92
|
+
// OneKey patch: official selector icon geometry.
|
|
93
|
+
case "AllNetworksSolid": assetName = "onekey_selector_all_networks_solid"
|
|
94
|
+
// OneKey patch: official selector icon geometry.
|
|
95
|
+
case "BotIllus": assetName = "onekey_selector_bot_illus"
|
|
96
|
+
// OneKey patch: official selector icon geometry.
|
|
97
|
+
case "AppleBrand": assetName = "onekey_selector_apple_brand"
|
|
98
|
+
// OneKey patch: official selector icon geometry.
|
|
99
|
+
case "GoogleIllus": assetName = "onekey_selector_google_illus"
|
|
100
|
+
// OneKey patch: official selector icon geometry.
|
|
101
|
+
case "LockSolid": assetName = "onekey_selector_lock_solid"
|
|
102
|
+
// OneKey patch: official selector icon geometry.
|
|
103
|
+
case "GlobusOutline": assetName = "onekey_selector_globus_outline"
|
|
81
104
|
case "ArrowBottomOutline": assetName = "onekey_arrow_bottom"
|
|
82
105
|
case "ArrowTopOutline": assetName = "onekey_arrow_top"
|
|
83
106
|
case "ChartTrendingUpOutline": assetName = "onekey_chart_trending_up"
|
|
@@ -106,5 +129,5 @@ func nativeListIcon(named name: String) -> UIImage? {
|
|
|
106
129
|
default: return nil
|
|
107
130
|
}
|
|
108
131
|
return UIImage(named: assetName, in: NativeListResources.bundle, compatibleWith: nil)?
|
|
109
|
-
.withRenderingMode(.alwaysTemplate)
|
|
132
|
+
.withRenderingMode(["GoogleIllus", "BotIllus", "AccountErrorCustom"].contains(name) ? .alwaysOriginal : .alwaysTemplate)
|
|
110
133
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Foundation
|
|
2
|
+
import CoreFoundation
|
|
2
3
|
import UIKit
|
|
3
4
|
import UniformTypeIdentifiers
|
|
4
5
|
|
|
@@ -51,7 +52,12 @@ final class NativeListView: UIView {
|
|
|
51
52
|
var onSelectionDelta: ((String) -> Void)?
|
|
52
53
|
var onReorder: ((String) -> Void)?
|
|
53
54
|
var onEndReached: ((String) -> Void)?
|
|
54
|
-
var onVisibleRangeChanged: ((String) -> Void)?
|
|
55
|
+
var onVisibleRangeChanged: ((String) -> Void)? {
|
|
56
|
+
didSet {
|
|
57
|
+
lastVisibleRange = nil
|
|
58
|
+
if onVisibleRangeChanged != nil { emitVisibleRangeIfNeeded() }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
55
61
|
|
|
56
62
|
private let flowLayout = NativeListFlowLayout()
|
|
57
63
|
private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
|
|
@@ -77,6 +83,8 @@ final class NativeListView: UIView {
|
|
|
77
83
|
target: self,
|
|
78
84
|
action: #selector(reorderLongPressChanged(_:))
|
|
79
85
|
)
|
|
86
|
+
// OneKey patch: claim only vertical drags so held rows and ancestor pagers stay responsive.
|
|
87
|
+
private lazy var listBodyGestureGuard = UIPanGestureRecognizer(target: nil, action: nil)
|
|
80
88
|
private var interactiveReorderSource: (key: String, index: Int)?
|
|
81
89
|
private weak var interactiveReorderCell: NativeListCell?
|
|
82
90
|
private var interactiveReorderCompactKey: String?
|
|
@@ -92,7 +100,10 @@ final class NativeListView: UIView {
|
|
|
92
100
|
private let actionAnchorInstanceID = UUID().uuidString
|
|
93
101
|
private var actionAnchorCounter = 0
|
|
94
102
|
|
|
95
|
-
private static let
|
|
103
|
+
private static let sectionIndexContentInset: CGFloat = 16
|
|
104
|
+
private static let sectionIndexRailWidth: CGFloat = 32
|
|
105
|
+
private static let sectionIndexPreviewSize: CGFloat = 48
|
|
106
|
+
private static let sectionIndexPreviewEndMargin: CGFloat = 40
|
|
96
107
|
|
|
97
108
|
override init(frame: CGRect) {
|
|
98
109
|
super.init(frame: frame)
|
|
@@ -102,6 +113,11 @@ final class NativeListView: UIView {
|
|
|
102
113
|
collectionView.dragDelegate = self
|
|
103
114
|
collectionView.dropDelegate = self
|
|
104
115
|
collectionView.alwaysBounceVertical = true
|
|
116
|
+
// OneKey patch: keep list-body drags from being claimed by an ancestor modal sheet.
|
|
117
|
+
listBodyGestureGuard.cancelsTouchesInView = false
|
|
118
|
+
listBodyGestureGuard.isEnabled = false
|
|
119
|
+
listBodyGestureGuard.delegate = self
|
|
120
|
+
collectionView.addGestureRecognizer(listBodyGestureGuard)
|
|
105
121
|
reorderLongPress.minimumPressDuration = ReorderAnimation.longPressDuration
|
|
106
122
|
reorderLongPress.allowableMovement = ReorderAnimation.allowableMovement
|
|
107
123
|
reorderLongPress.delegate = self
|
|
@@ -136,11 +152,14 @@ final class NativeListView: UIView {
|
|
|
136
152
|
sectionIndexView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor),
|
|
137
153
|
sectionIndexView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
|
|
138
154
|
sectionIndexView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor),
|
|
139
|
-
sectionIndexView.widthAnchor.constraint(equalToConstant: Self.
|
|
140
|
-
sectionIndexPreview.
|
|
155
|
+
sectionIndexView.widthAnchor.constraint(equalToConstant: Self.sectionIndexRailWidth),
|
|
156
|
+
sectionIndexPreview.trailingAnchor.constraint(
|
|
157
|
+
equalTo: safeAreaLayoutGuide.trailingAnchor,
|
|
158
|
+
constant: -Self.sectionIndexPreviewEndMargin
|
|
159
|
+
),
|
|
141
160
|
sectionIndexPreview.centerYAnchor.constraint(equalTo: collectionView.centerYAnchor),
|
|
142
|
-
sectionIndexPreview.widthAnchor.constraint(equalToConstant:
|
|
143
|
-
sectionIndexPreview.heightAnchor.constraint(equalToConstant:
|
|
161
|
+
sectionIndexPreview.widthAnchor.constraint(equalToConstant: Self.sectionIndexPreviewSize),
|
|
162
|
+
sectionIndexPreview.heightAnchor.constraint(equalToConstant: Self.sectionIndexPreviewSize),
|
|
144
163
|
])
|
|
145
164
|
|
|
146
165
|
sectionIndexView.isHidden = true
|
|
@@ -152,11 +171,11 @@ final class NativeListView: UIView {
|
|
|
152
171
|
}
|
|
153
172
|
sectionIndexPreview.isHidden = true
|
|
154
173
|
sectionIndexPreview.alpha = 0
|
|
155
|
-
sectionIndexPreview.layer.cornerRadius =
|
|
174
|
+
sectionIndexPreview.layer.cornerRadius = 14
|
|
156
175
|
sectionIndexPreview.layer.masksToBounds = true
|
|
157
176
|
sectionIndexPreview.textAlignment = .center
|
|
158
177
|
sectionIndexPreview.adjustsFontForContentSizeCategory = true
|
|
159
|
-
sectionIndexPreview.font = nativeListFont(ofSize:
|
|
178
|
+
sectionIndexPreview.font = nativeListFont(ofSize: 22, weight: .semibold)
|
|
160
179
|
sectionIndexPreview.isAccessibilityElement = false
|
|
161
180
|
|
|
162
181
|
footerCell.onAction = { [weak self] item, action, target, origin in
|
|
@@ -225,15 +244,18 @@ final class NativeListView: UIView {
|
|
|
225
244
|
guard let next = try? NativeListConfig.parse(json: json) else { return }
|
|
226
245
|
invalidateActionAnchor(reason: "snapshot")
|
|
227
246
|
if let current = config, isControlledSelectionSnapshotUpdate(from: current, to: next) {
|
|
247
|
+
let changedSummaryKeys = Set(zip(current.items, next.items).compactMap { old, new in
|
|
248
|
+
old.content != new.content ? new.key : nil
|
|
249
|
+
})
|
|
228
250
|
config = next
|
|
229
251
|
itemsByKey = Dictionary(uniqueKeysWithValues: next.items.map { ($0.key, $0) })
|
|
230
|
-
refreshVisibleSelection()
|
|
252
|
+
refreshVisibleSelection(changedSummaryKeys: changedSummaryKeys)
|
|
231
253
|
return
|
|
232
254
|
}
|
|
233
255
|
let oldItems = itemsByKey
|
|
256
|
+
if config?.generation != next.generation { endReachedGeneration = nil }
|
|
234
257
|
config = next
|
|
235
258
|
itemsByKey = Dictionary(uniqueKeysWithValues: next.items.map { ($0.key, $0) })
|
|
236
|
-
endReachedGeneration = nil
|
|
237
259
|
configureSectionIndex(next)
|
|
238
260
|
configureLayout(next)
|
|
239
261
|
configureRefresh(next)
|
|
@@ -275,12 +297,14 @@ final class NativeListView: UIView {
|
|
|
275
297
|
}
|
|
276
298
|
|
|
277
299
|
var changedKeys: [String] = []
|
|
300
|
+
var sizeChanged = false
|
|
278
301
|
for (index, changes) in pending {
|
|
279
302
|
var merged = current.items[index].data
|
|
280
303
|
changes.forEach { key, value in
|
|
281
304
|
if key != "key" && key != "type" { merged[key] = value }
|
|
282
305
|
}
|
|
283
306
|
guard let item = try? NativeListItem(data: merged) else { return }
|
|
307
|
+
if rowHeight(current.items[index]) != rowHeight(item) { sizeChanged = true }
|
|
284
308
|
current.items[index] = item
|
|
285
309
|
changedKeys.append(item.key)
|
|
286
310
|
if let selected = changes["selected"] as? Bool {
|
|
@@ -292,7 +316,13 @@ final class NativeListView: UIView {
|
|
|
292
316
|
itemsByKey = Dictionary(uniqueKeysWithValues: current.items.map { ($0.key, $0) })
|
|
293
317
|
var snapshot = dataSource.snapshot()
|
|
294
318
|
snapshot.reconfigureItems(changedKeys.filter { snapshot.indexOfItem($0) != nil })
|
|
295
|
-
dataSource.apply(snapshot, animatingDifferences: false)
|
|
319
|
+
dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in
|
|
320
|
+
guard let self else { return }
|
|
321
|
+
if sizeChanged { self.flowLayout.invalidateLayout() }
|
|
322
|
+
self.collectionView.layoutIfNeeded()
|
|
323
|
+
self.emitVisibleRangeIfNeeded()
|
|
324
|
+
self.checkEndReached()
|
|
325
|
+
}
|
|
296
326
|
configureFooter(current)
|
|
297
327
|
}
|
|
298
328
|
|
|
@@ -472,7 +502,8 @@ final class NativeListView: UIView {
|
|
|
472
502
|
|
|
473
503
|
private func configureLayout(_ config: NativeListConfig) {
|
|
474
504
|
let isHorizontal = config.orientation == "horizontal"
|
|
475
|
-
|
|
505
|
+
// OneKey patch: the section index overlays rows and keeps only an accessory-safe inset.
|
|
506
|
+
let indexGutter = sectionIndexEntries.isEmpty ? 0 : Self.sectionIndexContentInset
|
|
476
507
|
let isRightToLeft = effectiveUserInterfaceLayoutDirection == .rightToLeft
|
|
477
508
|
flowLayout.scrollDirection = isHorizontal ? .horizontal : .vertical
|
|
478
509
|
flowLayout.minimumLineSpacing = config.itemSpacing
|
|
@@ -485,7 +516,7 @@ final class NativeListView: UIView {
|
|
|
485
516
|
)
|
|
486
517
|
flowLayout.stickyItemIndexes = config.stickyHeaders
|
|
487
518
|
? Set(config.items.enumerated().compactMap {
|
|
488
|
-
$0.element.type == "sectionHeader" && $0.element.data.string("variant") != "summary"
|
|
519
|
+
$0.element.type == "sectionHeader" && $0.element.data.bool("sticky", default: true) && $0.element.data.string("variant") != "summary"
|
|
489
520
|
? $0.offset
|
|
490
521
|
: nil
|
|
491
522
|
})
|
|
@@ -493,6 +524,7 @@ final class NativeListView: UIView {
|
|
|
493
524
|
flowLayout.invalidateLayout()
|
|
494
525
|
collectionView.alwaysBounceHorizontal = isHorizontal
|
|
495
526
|
collectionView.alwaysBounceVertical = !isHorizontal
|
|
527
|
+
listBodyGestureGuard.isEnabled = !isHorizontal
|
|
496
528
|
collectionView.showsVerticalScrollIndicator = sectionIndexEntries.isEmpty
|
|
497
529
|
collectionView.dragInteractionEnabled = false
|
|
498
530
|
lastLayoutDirection = effectiveUserInterfaceLayoutDirection
|
|
@@ -512,11 +544,12 @@ final class NativeListView: UIView {
|
|
|
512
544
|
interactiveReorderCell = nil
|
|
513
545
|
return
|
|
514
546
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
547
|
+
// OneKey patch: a hidden wallet child starts dragging its whole logical group.
|
|
548
|
+
// if item.type == "walletGroup", gesture.location(in: cell).y > 68 {
|
|
549
|
+
// interactiveReorderSource = nil
|
|
550
|
+
// interactiveReorderCell = nil
|
|
551
|
+
// return
|
|
552
|
+
// }
|
|
520
553
|
interactiveReorderSource = (item.key, indexPath.item)
|
|
521
554
|
interactiveReorderCell = cell
|
|
522
555
|
interactiveReorderUsesAtomicTargeting = item.type == "identity" &&
|
|
@@ -838,7 +871,8 @@ final class NativeListView: UIView {
|
|
|
838
871
|
sectionIndexView.configure(
|
|
839
872
|
titles: sectionIndexEntries.map(\.title),
|
|
840
873
|
textColor: nativeListColor(config.theme, "secondaryText", "#646464"),
|
|
841
|
-
activeColor: nativeListColor(config.theme, "accent", "#108303")
|
|
874
|
+
activeColor: nativeListColor(config.theme, "accent", "#108303"),
|
|
875
|
+
activeTextColor: nativeListColor(config.theme, "inverseText", "#FCFCFC")
|
|
842
876
|
)
|
|
843
877
|
sectionIndexView.isHidden = sectionIndexEntries.isEmpty
|
|
844
878
|
sectionIndexPreview.backgroundColor = nativeListColor(
|
|
@@ -932,7 +966,7 @@ final class NativeListView: UIView {
|
|
|
932
966
|
theme: config.theme,
|
|
933
967
|
layout: config.layout,
|
|
934
968
|
itemIndex: itemIndex,
|
|
935
|
-
selected: config.selectedKeys.contains(item.key),
|
|
969
|
+
selected: item.data.bool("selected") || config.selectedKeys.contains(item.key),
|
|
936
970
|
checkboxState: { [weak self] item, target, fallback in
|
|
937
971
|
self?.resolveCheckboxState(item: item, target: target, fallback: fallback) ?? fallback
|
|
938
972
|
}
|
|
@@ -943,7 +977,8 @@ final class NativeListView: UIView {
|
|
|
943
977
|
}
|
|
944
978
|
|
|
945
979
|
private func handleRowPress(_ item: NativeListItem, origin: NativeListActionOrigin?) {
|
|
946
|
-
|
|
980
|
+
// OneKey patch: missing-address rows keep accessory actions available.
|
|
981
|
+
guard let config, !item.data.bool("disabled"), !item.data.bool("pressDisabled") else { return }
|
|
947
982
|
if config.rowPressToggles && item.isSelectable && config.selectionMode != "none" {
|
|
948
983
|
updateSelection(target: NativeSelectionTarget(scope: "row", key: item.key), sourceKey: item.key)
|
|
949
984
|
return
|
|
@@ -1026,7 +1061,7 @@ final class NativeListView: UIView {
|
|
|
1026
1061
|
return "indeterminate"
|
|
1027
1062
|
}
|
|
1028
1063
|
|
|
1029
|
-
private func refreshVisibleSelection() {
|
|
1064
|
+
private func refreshVisibleSelection(changedSummaryKeys: Set<String> = []) {
|
|
1030
1065
|
guard let config else { return }
|
|
1031
1066
|
let checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String = {
|
|
1032
1067
|
[weak self] item, target, fallback in
|
|
@@ -1035,9 +1070,13 @@ final class NativeListView: UIView {
|
|
|
1035
1070
|
for indexPath in collectionView.indexPathsForVisibleItems {
|
|
1036
1071
|
guard let item = config.items[safe: indexPath.item],
|
|
1037
1072
|
let cell = collectionView.cellForItem(at: indexPath) as? NativeListCell else { continue }
|
|
1073
|
+
if changedSummaryKeys.contains(item.key) {
|
|
1074
|
+
bind(cell: cell, item: item, itemIndex: indexPath.item)
|
|
1075
|
+
continue
|
|
1076
|
+
}
|
|
1038
1077
|
cell.updateSelection(
|
|
1039
1078
|
item: item,
|
|
1040
|
-
selected: config.selectedKeys.contains(item.key),
|
|
1079
|
+
selected: item.data.bool("selected") || config.selectedKeys.contains(item.key),
|
|
1041
1080
|
checkboxState: checkboxState
|
|
1042
1081
|
)
|
|
1043
1082
|
}
|
|
@@ -1080,19 +1119,73 @@ final class NativeListView: UIView {
|
|
|
1080
1119
|
return zip(current.items, next.items).allSatisfy { old, new in
|
|
1081
1120
|
guard old.key == new.key, old.type == new.type else { return false }
|
|
1082
1121
|
if old.content == new.content { return true }
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
var
|
|
1088
|
-
|
|
1089
|
-
oldData.removeValue(forKey: "
|
|
1090
|
-
|
|
1091
|
-
newData.removeValue(forKey: "
|
|
1122
|
+
// OneKey patch: controlled echoes can also update row and checkbox selection fields.
|
|
1123
|
+
// guard old.type == "sectionHeader",
|
|
1124
|
+
// old.data.string("variant") == "summary",
|
|
1125
|
+
// new.data.string("variant") == "summary" else { return false }
|
|
1126
|
+
// var oldData = old.data
|
|
1127
|
+
// var newData = new.data
|
|
1128
|
+
// oldData.removeValue(forKey: "title")
|
|
1129
|
+
// oldData.removeValue(forKey: "value")
|
|
1130
|
+
// newData.removeValue(forKey: "title")
|
|
1131
|
+
// newData.removeValue(forKey: "value")
|
|
1132
|
+
let controlled = next.selectionMode == "single" || next.selectionMode == "multiple"
|
|
1133
|
+
guard let oldData = selectionComparisonData(old.data, controlled: controlled),
|
|
1134
|
+
let newData = selectionComparisonData(new.data, controlled: controlled) else { return false }
|
|
1092
1135
|
return jsonData(oldData) == jsonData(newData)
|
|
1093
1136
|
}
|
|
1094
1137
|
}
|
|
1095
1138
|
|
|
1139
|
+
// OneKey patch: remove only fields that the existing lightweight binder refreshes.
|
|
1140
|
+
private func selectionComparisonData(_ data: [String: Any], controlled: Bool) -> [String: Any]? {
|
|
1141
|
+
guard let type = data["type"] as? String else { return nil }
|
|
1142
|
+
var result = data
|
|
1143
|
+
if let selected = result["selected"] {
|
|
1144
|
+
guard CFGetTypeID(selected as CFTypeRef) == CFBooleanGetTypeID() else { return nil }
|
|
1145
|
+
result.removeValue(forKey: "selected")
|
|
1146
|
+
}
|
|
1147
|
+
if type == "walletGroup" {
|
|
1148
|
+
guard let parent = data["parent"] as? [String: Any], parent["type"] as? String == "identity",
|
|
1149
|
+
let children = data["children"] as? [[String: Any]],
|
|
1150
|
+
let normalizedParent = selectionComparisonData(parent, controlled: false) else { return nil }
|
|
1151
|
+
var normalizedChildren: [[String: Any]] = []
|
|
1152
|
+
for child in children {
|
|
1153
|
+
guard child["type"] as? String == "identity",
|
|
1154
|
+
let normalized = selectionComparisonData(child, controlled: false) else { return nil }
|
|
1155
|
+
normalizedChildren.append(normalized)
|
|
1156
|
+
}
|
|
1157
|
+
result["parent"] = normalizedParent
|
|
1158
|
+
result["children"] = normalizedChildren
|
|
1159
|
+
}
|
|
1160
|
+
if type == "sectionHeader", data["variant"] as? String == "summary" {
|
|
1161
|
+
result.removeValue(forKey: "title")
|
|
1162
|
+
result.removeValue(forKey: "value")
|
|
1163
|
+
}
|
|
1164
|
+
guard controlled else { return result }
|
|
1165
|
+
func checkboxData(_ value: Any) -> [String: Any]? {
|
|
1166
|
+
guard var checkbox = value as? [String: Any], checkbox["kind"] as? String == "checkbox" else { return nil }
|
|
1167
|
+
if let state = checkbox["state"] {
|
|
1168
|
+
guard let state = state as? String, ["checked", "unchecked", "indeterminate"].contains(state) else { return nil }
|
|
1169
|
+
}
|
|
1170
|
+
checkbox.removeValue(forKey: "state")
|
|
1171
|
+
return checkbox
|
|
1172
|
+
}
|
|
1173
|
+
if ["dataRow", "sectionHeader", "action"].contains(type), let checkbox = result["checkbox"] {
|
|
1174
|
+
guard let normalized = checkboxData(checkbox) else { return nil }
|
|
1175
|
+
result["checkbox"] = normalized
|
|
1176
|
+
}
|
|
1177
|
+
if type == "identity", let trailing = result["trailing"] {
|
|
1178
|
+
guard var accessories = trailing as? [[String: Any]], accessories.count <= 2,
|
|
1179
|
+
accessories.filter({ $0["kind"] as? String == "checkbox" }).count <= 1 else { return nil }
|
|
1180
|
+
for index in accessories.indices where accessories[index]["kind"] as? String == "checkbox" {
|
|
1181
|
+
guard let normalized = checkboxData(accessories[index]) else { return nil }
|
|
1182
|
+
accessories[index] = normalized
|
|
1183
|
+
}
|
|
1184
|
+
result["trailing"] = accessories
|
|
1185
|
+
}
|
|
1186
|
+
return result
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1096
1189
|
private func dictionariesEqual(_ lhs: [String: Any]?, _ rhs: [String: Any]?) -> Bool {
|
|
1097
1190
|
switch (lhs, rhs) {
|
|
1098
1191
|
case (nil, nil): return true
|
|
@@ -1107,16 +1200,36 @@ final class NativeListView: UIView {
|
|
|
1107
1200
|
}
|
|
1108
1201
|
|
|
1109
1202
|
private func rowHeight(_ item: NativeListItem) -> CGFloat {
|
|
1203
|
+
// OneKey patch: honor selector baseline geometry; keep compact drag sizing.
|
|
1204
|
+
if item.type != "walletGroup", item.data["height"] != nil { return CGFloat(item.data.double("height")) }
|
|
1110
1205
|
if item.type == "system", item.data.string("variant") == "spacer" {
|
|
1111
1206
|
return CGFloat(item.data.int("height"))
|
|
1112
1207
|
}
|
|
1208
|
+
// OneKey patch: warning height follows the current native font and available width.
|
|
1209
|
+
if item.type == "system", item.data.string("variant") == "warning" {
|
|
1210
|
+
let textWidth = max(1, collectionView.bounds.width - (config?.contentPaddingHorizontal ?? 0) * 2 - 24)
|
|
1211
|
+
func textHeight(_ key: String, weight: NativeListFontWeight) -> CGFloat {
|
|
1212
|
+
let paragraph = NSMutableParagraphStyle()
|
|
1213
|
+
paragraph.minimumLineHeight = 20
|
|
1214
|
+
paragraph.maximumLineHeight = 20
|
|
1215
|
+
return ceil((item.data.string(key) as NSString).boundingRect(with: CGSize(width: textWidth, height: .greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font: nativeListFont(ofSize: 14, weight: weight), .paragraphStyle: paragraph], context: nil).height / 20) * 20
|
|
1216
|
+
}
|
|
1217
|
+
return 32 + textHeight("title", weight: .medium) + textHeight("message", weight: .regular)
|
|
1218
|
+
}
|
|
1113
1219
|
if item.type == "walletGroup" {
|
|
1114
1220
|
if item.key == interactiveReorderCompactKey { return 68 }
|
|
1115
1221
|
let childCount = item.data.dictionaries("children").count
|
|
1116
|
-
|
|
1222
|
+
// OneKey patch: wallet badges contribute their own member heights.
|
|
1223
|
+
// return CGFloat((childCount + 1) * 68 + childCount * 12)
|
|
1224
|
+
let members = [item.data.dictionary("parent")].compactMap { $0 } + item.data.dictionaries("children")
|
|
1225
|
+
return members.reduce(CGFloat(childCount * 12 + (members.first?["height"] != nil ? 2 : 0))) { total, data in
|
|
1226
|
+
total + CGFloat(data.double("height", default: data.dictionaries("badges").isEmpty ? 68 : 92))
|
|
1227
|
+
}
|
|
1117
1228
|
}
|
|
1118
1229
|
if item.type == "identity", item.data.string("presentation") == "walletSidebar" {
|
|
1119
|
-
|
|
1230
|
+
// OneKey patch: default sidebar badge geometry is 24 points taller.
|
|
1231
|
+
// return 68
|
|
1232
|
+
return item.data.dictionaries("badges").isEmpty ? 68 : 92
|
|
1120
1233
|
}
|
|
1121
1234
|
if item.type == "identity", item.data.string("presentation") == "networkSelector" {
|
|
1122
1235
|
return 47
|
|
@@ -1268,7 +1381,7 @@ final class NativeListView: UIView {
|
|
|
1268
1381
|
actionAnchorCounter &+= 1
|
|
1269
1382
|
let generation = config?.generation ?? 0
|
|
1270
1383
|
let token = "\(actionAnchorInstanceID):\(generation):\(actionAnchorCounter):\(origin.bindingEpoch)"
|
|
1271
|
-
let rect = sourceView.convert(sourceView.bounds, to: window)
|
|
1384
|
+
let rect = sourceView.convert(sourceView.bounds, to: window).insetBy(dx: origin.anchorInset, dy: origin.anchorInset)
|
|
1272
1385
|
let record = ActionAnchorRecord(token: token, origin: origin)
|
|
1273
1386
|
actionAnchor = record
|
|
1274
1387
|
var anchor: [String: Any] = [
|
|
@@ -1341,6 +1454,7 @@ final class NativeListView: UIView {
|
|
|
1341
1454
|
DispatchQueue.main.async { [weak self] in
|
|
1342
1455
|
guard let self else { return }
|
|
1343
1456
|
self.visibleEventScheduled = false
|
|
1457
|
+
guard let onVisibleRangeChanged = self.onVisibleRangeChanged else { return }
|
|
1344
1458
|
let indexes = self.collectionView.indexPathsForVisibleItems.map(\.item).sorted()
|
|
1345
1459
|
let first = indexes.first ?? -1
|
|
1346
1460
|
let last = indexes.last ?? -1
|
|
@@ -1354,7 +1468,7 @@ final class NativeListView: UIView {
|
|
|
1354
1468
|
var payload: [String: Any] = ["firstIndex": first, "lastIndex": last]
|
|
1355
1469
|
if let firstKey { payload["firstKey"] = firstKey }
|
|
1356
1470
|
if let lastKey { payload["lastKey"] = lastKey }
|
|
1357
|
-
self.emit(
|
|
1471
|
+
self.emit(onVisibleRangeChanged, payload)
|
|
1358
1472
|
}
|
|
1359
1473
|
}
|
|
1360
1474
|
|
|
@@ -1371,10 +1485,18 @@ final class NativeListView: UIView {
|
|
|
1371
1485
|
if let lastKey = config.items.last?.key { payload["lastKey"] = lastKey }
|
|
1372
1486
|
emit(onEndReached, payload)
|
|
1373
1487
|
}
|
|
1488
|
+
|
|
1489
|
+
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
|
1490
|
+
guard gestureRecognizer === listBodyGestureGuard,
|
|
1491
|
+
let pan = gestureRecognizer as? UIPanGestureRecognizer else { return true }
|
|
1492
|
+
let velocity = pan.velocity(in: collectionView)
|
|
1493
|
+
return abs(velocity.y) > abs(velocity.x)
|
|
1494
|
+
}
|
|
1374
1495
|
}
|
|
1375
1496
|
|
|
1376
1497
|
extension NativeListView: UIGestureRecognizerDelegate {
|
|
1377
1498
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
|
|
1499
|
+
if gestureRecognizer === listBodyGestureGuard { return true }
|
|
1378
1500
|
var view = touch.view
|
|
1379
1501
|
while let current = view, current !== footerCell {
|
|
1380
1502
|
if current is UIControl { return false }
|
|
@@ -1387,7 +1509,26 @@ extension NativeListView: UIGestureRecognizerDelegate {
|
|
|
1387
1509
|
_ gestureRecognizer: UIGestureRecognizer,
|
|
1388
1510
|
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
|
|
1389
1511
|
) -> Bool {
|
|
1390
|
-
gestureRecognizer
|
|
1512
|
+
if gestureRecognizer === listBodyGestureGuard {
|
|
1513
|
+
guard let otherView = otherGestureRecognizer.view else { return false }
|
|
1514
|
+
return otherView === collectionView || otherView.isDescendant(of: collectionView)
|
|
1515
|
+
}
|
|
1516
|
+
if otherGestureRecognizer === listBodyGestureGuard {
|
|
1517
|
+
guard let gestureView = gestureRecognizer.view else { return false }
|
|
1518
|
+
return gestureView === collectionView || gestureView.isDescendant(of: collectionView)
|
|
1519
|
+
}
|
|
1520
|
+
return gestureRecognizer.view === footerCell || otherGestureRecognizer.view === footerCell
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
func gestureRecognizer(
|
|
1524
|
+
_ gestureRecognizer: UIGestureRecognizer,
|
|
1525
|
+
shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer
|
|
1526
|
+
) -> Bool {
|
|
1527
|
+
guard gestureRecognizer === listBodyGestureGuard,
|
|
1528
|
+
otherGestureRecognizer is UIPanGestureRecognizer,
|
|
1529
|
+
let otherView = otherGestureRecognizer.view,
|
|
1530
|
+
otherView !== collectionView else { return false }
|
|
1531
|
+
return collectionView.isDescendant(of: otherView)
|
|
1391
1532
|
}
|
|
1392
1533
|
}
|
|
1393
1534
|
|
|
@@ -1604,13 +1745,16 @@ private struct NativeListSectionIndexEntry {
|
|
|
1604
1745
|
let position: Int
|
|
1605
1746
|
}
|
|
1606
1747
|
|
|
1607
|
-
|
|
1748
|
+
// OneKey patch: arbitrate index scrubbing against ancestor dismissal gestures.
|
|
1749
|
+
// private final class NativeListSectionIndexView: UIControl {
|
|
1750
|
+
private final class NativeListSectionIndexView: UIControl, UIGestureRecognizerDelegate {
|
|
1608
1751
|
var onSelect: ((Int, Bool) -> Void)?
|
|
1609
1752
|
var onInteractionEnded: (() -> Void)?
|
|
1610
1753
|
private var titles: [String] = []
|
|
1611
1754
|
private var labels: [UILabel] = []
|
|
1612
1755
|
private var textColor: UIColor = .secondaryLabel
|
|
1613
1756
|
private var activeColor: UIColor = .tintColor
|
|
1757
|
+
private var activeTextColor: UIColor = .white
|
|
1614
1758
|
private var lastTouchIndex: Int?
|
|
1615
1759
|
private(set) var activeIndex: Int?
|
|
1616
1760
|
|
|
@@ -1620,16 +1764,43 @@ private final class NativeListSectionIndexView: UIControl {
|
|
|
1620
1764
|
accessibilityLabel = "Section index"
|
|
1621
1765
|
accessibilityTraits = [.adjustable]
|
|
1622
1766
|
isExclusiveTouch = true
|
|
1767
|
+
|
|
1768
|
+
// UIControl tracking alone cannot prevent an ancestor sheet pan from taking the touch.
|
|
1769
|
+
// Recognize immediately, but keep delivering touches to the existing tracking methods.
|
|
1770
|
+
let scrubGesture = UILongPressGestureRecognizer(target: nil, action: nil)
|
|
1771
|
+
scrubGesture.minimumPressDuration = 0
|
|
1772
|
+
scrubGesture.allowableMovement = .greatestFiniteMagnitude
|
|
1773
|
+
scrubGesture.cancelsTouchesInView = false
|
|
1774
|
+
scrubGesture.delaysTouchesEnded = false
|
|
1775
|
+
scrubGesture.delegate = self
|
|
1776
|
+
addGestureRecognizer(scrubGesture)
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
func gestureRecognizer(
|
|
1780
|
+
_ gestureRecognizer: UIGestureRecognizer,
|
|
1781
|
+
shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer
|
|
1782
|
+
) -> Bool {
|
|
1783
|
+
guard otherGestureRecognizer is UIPanGestureRecognizer,
|
|
1784
|
+
let otherView = otherGestureRecognizer.view,
|
|
1785
|
+
otherView !== self else { return false }
|
|
1786
|
+
// The dependency applies only to touches starting in this index, including moves outside it.
|
|
1787
|
+
return isDescendant(of: otherView)
|
|
1623
1788
|
}
|
|
1624
1789
|
|
|
1625
1790
|
required init?(coder: NSCoder) {
|
|
1626
1791
|
fatalError("init(coder:) has not been implemented")
|
|
1627
1792
|
}
|
|
1628
1793
|
|
|
1629
|
-
func configure(
|
|
1794
|
+
func configure(
|
|
1795
|
+
titles: [String],
|
|
1796
|
+
textColor: UIColor,
|
|
1797
|
+
activeColor: UIColor,
|
|
1798
|
+
activeTextColor: UIColor
|
|
1799
|
+
) {
|
|
1630
1800
|
self.titles = titles
|
|
1631
1801
|
self.textColor = textColor
|
|
1632
1802
|
self.activeColor = activeColor
|
|
1803
|
+
self.activeTextColor = activeTextColor
|
|
1633
1804
|
labels.forEach { $0.removeFromSuperview() }
|
|
1634
1805
|
labels = titles.map { title in
|
|
1635
1806
|
let label = UILabel()
|
|
@@ -1660,9 +1831,9 @@ private final class NativeListSectionIndexView: UIControl {
|
|
|
1660
1831
|
let originY = (bounds.height - height * CGFloat(labels.count)) / 2
|
|
1661
1832
|
for (index, label) in labels.enumerated() {
|
|
1662
1833
|
label.frame = CGRect(
|
|
1663
|
-
x:
|
|
1834
|
+
x: 6,
|
|
1664
1835
|
y: originY + CGFloat(index) * height,
|
|
1665
|
-
width:
|
|
1836
|
+
width: 20,
|
|
1666
1837
|
height: height
|
|
1667
1838
|
)
|
|
1668
1839
|
}
|
|
@@ -1718,7 +1889,10 @@ private final class NativeListSectionIndexView: UIControl {
|
|
|
1718
1889
|
private func updateLabelStyles() {
|
|
1719
1890
|
for (index, label) in labels.enumerated() {
|
|
1720
1891
|
let active = index == activeIndex
|
|
1721
|
-
label.textColor = active ?
|
|
1892
|
+
label.textColor = active ? activeTextColor : textColor
|
|
1893
|
+
label.backgroundColor = active ? activeColor : .clear
|
|
1894
|
+
label.layer.cornerRadius = 8
|
|
1895
|
+
label.layer.masksToBounds = active
|
|
1722
1896
|
label.font = nativeListFont(ofSize: 10, weight: active ? .semibold : .medium)
|
|
1723
1897
|
}
|
|
1724
1898
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" fill="currentColor">
|
|
2
|
+
<path
|
|
3
|
+
fill="#000"
|
|
4
|
+
fill-opacity="0.447"
|
|
5
|
+
d="M12.5 12.75a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5"
|
|
6
|
+
/>
|
|
7
|
+
<path
|
|
8
|
+
fill="#000"
|
|
9
|
+
fill-opacity="0.447"
|
|
10
|
+
fill-rule="evenodd"
|
|
11
|
+
d="M0 3.5A3.5 3.5 0 0 1 3.5 0h8.088A2.41 2.41 0 0 1 14 2.412V5h1a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3H4a4 4 0 0 1-4-4zm2 3.163V14a2 2 0 0 0 2 2h11a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H3.5c-.537 0-1.045-.12-1.5-.337M2 3.5A1.5 1.5 0 0 0 3.5 5H12V2.412A.41.41 0 0 0 11.588 2H3.5A1.5 1.5 0 0 0 2 3.5"
|
|
12
|
+
clip-rule="evenodd"
|
|
13
|
+
/>
|
|
14
|
+
</svg>
|
package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/icon.svg
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
|
2
|
+
<path
|
|
3
|
+
fill="currentColor"
|
|
4
|
+
d="M15.333 13.998a1.335 1.335 0 1 1 0 2.67 1.335 1.335 0 0 1 0-2.67"
|
|
5
|
+
/>
|
|
6
|
+
<path
|
|
7
|
+
fill="currentColor"
|
|
8
|
+
fill-rule="evenodd"
|
|
9
|
+
d="M12 0c6.627 0 12 5.373 12 12s-5.373 12-12 12S0 18.627 0 12 5.373 0 12 0M8 12.668A2 2 0 0 0 6 14.666V16c0 1.103.895 1.997 1.998 1.998h1.334A2 2 0 0 0 11.33 16v-1.334a2 2 0 0 0-1.998-1.998zm7.333 0a2.665 2.665 0 1 0 0 5.33 2.665 2.665 0 0 0 0-5.33M7.999 6.001A2 2 0 0 0 6.001 8v1.334c0 1.103.895 1.998 1.998 1.998h1.334a2 2 0 0 0 1.998-1.998V7.999a2 2 0 0 0-1.998-1.998zm6.667 0A2 2 0 0 0 12.668 8v1.334c0 1.103.895 1.998 1.998 1.998H16a2 2 0 0 0 1.998-1.998V7.999A2 2 0 0 0 16 6.001z"
|
|
10
|
+
clip-rule="evenodd"
|
|
11
|
+
/>
|
|
12
|
+
</svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 20" fill="currentColor">
|
|
2
|
+
<path
|
|
3
|
+
fill="currentColor"
|
|
4
|
+
d="M11.67.834c.117 1.074-.315 2.153-.955 2.928-.64.773-1.692 1.378-2.718 1.298-.14-1.054.38-2.151.971-2.836C9.63 1.45 10.746.872 11.67.834M14.994 7.093c-.176.108-1.992 1.224-1.972 3.482.025 2.769 2.428 3.693 2.46 3.705l-.004.015a10.1 10.1 0 0 1-1.264 2.593c-.764 1.116-1.556 2.229-2.806 2.254-.598.011-1-.162-1.416-.343-.437-.19-.891-.386-1.609-.386-.751 0-1.226.203-1.683.398-.397.169-.78.333-1.32.354-1.208.047-2.124-1.207-2.895-2.32C.909 14.57-.294 10.414 1.322 7.612c.803-1.395 2.237-2.275 3.794-2.298.671-.014 1.32.244 1.89.47.434.172.821.326 1.135.326.282 0 .659-.149 1.099-.323.692-.273 1.539-.607 2.41-.518.599.026 2.276.24 3.354 1.818z"
|
|
5
|
+
/>
|
|
6
|
+
</svg>
|