@onekeyfe/react-native-native-list 3.0.114 → 3.0.115
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 +57 -3
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListAdapter.kt +17 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +543 -26
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +404 -90
- package/ios/NativeListCell.swift +506 -20
- package/ios/NativeListDesignAssets.swift +8 -0
- package/ios/RNCNativeListView.swift +271 -42
- package/ios/Resources/NativeListIcons.xcassets/onekey_badge_verified_solid.imageset/Contents.json +1 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_badge_verified_solid.imageset/icon.svg +1 -0
- package/lib/module/validation.js +129 -1
- package/lib/module/web/NativeListWebEngine.js +461 -51
- package/lib/typescript/src/models.d.ts +82 -2
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +37 -3
- package/package.json +4 -2
- package/src/models.ts +121 -3
- package/src/validation.ts +206 -0
- package/src/web/NativeListWebEngine.ts +766 -91
package/ios/NativeListCell.swift
CHANGED
|
@@ -4,6 +4,73 @@ import CoreText
|
|
|
4
4
|
import OneKeyImage
|
|
5
5
|
import UIKit
|
|
6
6
|
|
|
7
|
+
// Market/TokenListSkeleton: source geometry and the native Skeleton's 3s shimmer.
|
|
8
|
+
private final class NativeListMarketSkeleton: UIView {
|
|
9
|
+
private let marks = (0..<5).map { _ in UIView() }
|
|
10
|
+
private let gradients = (0..<5).map { _ in CAGradientLayer() }
|
|
11
|
+
|
|
12
|
+
init(background: UIColor) {
|
|
13
|
+
super.init(frame: .zero)
|
|
14
|
+
var white: CGFloat = 1
|
|
15
|
+
background.getWhite(&white, alpha: nil)
|
|
16
|
+
let base = UIColor(nativeListHex: white < 0.5 ? "#111111" : "#FAFAFA", fallback: .white)
|
|
17
|
+
let highlight = UIColor(nativeListHex: white < 0.5 ? "#333333" : "#CDCDCD", fallback: .lightGray)
|
|
18
|
+
for (index, mark) in marks.enumerated() {
|
|
19
|
+
mark.backgroundColor = base
|
|
20
|
+
mark.clipsToBounds = true
|
|
21
|
+
mark.layer.cornerRadius = index == 0 ? 16 : 8
|
|
22
|
+
let gradient = gradients[index]
|
|
23
|
+
gradient.cornerRadius = mark.layer.cornerRadius
|
|
24
|
+
gradient.colors = [base.cgColor, highlight.cgColor, base.cgColor]
|
|
25
|
+
gradient.locations = [0, 0.5, 1]
|
|
26
|
+
gradient.startPoint = CGPoint(x: 0, y: 0.5)
|
|
27
|
+
gradient.endPoint = CGPoint(x: 1, y: 0.5)
|
|
28
|
+
mark.layer.addSublayer(gradient)
|
|
29
|
+
addSubview(mark)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
|
34
|
+
|
|
35
|
+
override func layoutSubviews() {
|
|
36
|
+
super.layoutSubviews()
|
|
37
|
+
let frames = [
|
|
38
|
+
CGRect(x: 0, y: 0, width: 32, height: 32),
|
|
39
|
+
CGRect(x: 44, y: 0, width: 80, height: 16),
|
|
40
|
+
CGRect(x: 44, y: 20, width: 60, height: 12),
|
|
41
|
+
CGRect(x: bounds.width - 168, y: 7, width: 80, height: 18),
|
|
42
|
+
CGRect(x: bounds.width - 80, y: 7, width: 80, height: 18),
|
|
43
|
+
]
|
|
44
|
+
CATransaction.begin()
|
|
45
|
+
CATransaction.setDisableActions(true)
|
|
46
|
+
for (index, frame) in frames.enumerated() {
|
|
47
|
+
marks[index].frame = frame
|
|
48
|
+
gradients[index].frame = marks[index].bounds
|
|
49
|
+
}
|
|
50
|
+
CATransaction.commit()
|
|
51
|
+
updateAnimation()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
override func didMoveToWindow() {
|
|
55
|
+
super.didMoveToWindow()
|
|
56
|
+
updateAnimation()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private func updateAnimation() {
|
|
60
|
+
for gradient in gradients {
|
|
61
|
+
guard window != nil else { gradient.removeAllAnimations(); continue }
|
|
62
|
+
guard gradient.bounds.width > 0, gradient.animation(forKey: "shimmer") == nil else { continue }
|
|
63
|
+
let animation = CABasicAnimation(keyPath: "transform.translation.x")
|
|
64
|
+
animation.fromValue = -gradient.bounds.width
|
|
65
|
+
animation.toValue = gradient.bounds.width
|
|
66
|
+
animation.duration = 3
|
|
67
|
+
animation.repeatCount = .infinity
|
|
68
|
+
animation.timingFunction = CAMediaTimingFunction(name: .linear)
|
|
69
|
+
gradient.add(animation, forKey: "shimmer")
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
7
74
|
final class NativeListActionOrigin {
|
|
8
75
|
weak var sourceView: UIView?
|
|
9
76
|
weak var ownerCell: NativeListCell?
|
|
@@ -36,13 +103,17 @@ private final class NativeListAccessoryButton: UIButton {
|
|
|
36
103
|
didSet { invalidateIntrinsicContentSize(); setNeedsLayout() }
|
|
37
104
|
}
|
|
38
105
|
|
|
106
|
+
var marketLineHeight: CGFloat? {
|
|
107
|
+
didSet { invalidateIntrinsicContentSize(); setNeedsLayout() }
|
|
108
|
+
}
|
|
109
|
+
|
|
39
110
|
private var sourcePixelScale: CGFloat {
|
|
40
111
|
max(1, window?.screen.scale ?? traitCollection.displayScale)
|
|
41
112
|
}
|
|
42
113
|
|
|
43
114
|
override var intrinsicContentSize: CGSize {
|
|
44
115
|
var size = super.intrinsicContentSize
|
|
45
|
-
guard selectorSummaryLineHeight != nil, let title = attributedTitle(for: .normal) else { return size }
|
|
116
|
+
guard selectorSummaryLineHeight != nil || marketLineHeight != nil, let title = attributedTitle(for: .normal) else { return size }
|
|
46
117
|
let width = title.boundingRect(
|
|
47
118
|
with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude),
|
|
48
119
|
options: [.usesLineFragmentOrigin, .usesFontLeading],
|
|
@@ -54,6 +125,21 @@ private final class NativeListAccessoryButton: UIButton {
|
|
|
54
125
|
|
|
55
126
|
override func layoutSubviews() {
|
|
56
127
|
super.layoutSubviews()
|
|
128
|
+
if let lineHeight = marketLineHeight, let titleLabel, let title = attributedTitle(for: .normal) {
|
|
129
|
+
let measured = title.boundingRect(
|
|
130
|
+
with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude),
|
|
131
|
+
options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil
|
|
132
|
+
).width
|
|
133
|
+
let width = min(bounds.width, ceil(measured * sourcePixelScale) / sourcePixelScale)
|
|
134
|
+
let x = contentHorizontalAlignment == .trailing ? bounds.width - width
|
|
135
|
+
: contentHorizontalAlignment == .leading ? 0 : (bounds.width - width) / 2
|
|
136
|
+
titleLabel.frame = CGRect(
|
|
137
|
+
x: floor(x * sourcePixelScale) / sourcePixelScale,
|
|
138
|
+
y: (bounds.height - lineHeight) / 2,
|
|
139
|
+
width: width, height: lineHeight
|
|
140
|
+
)
|
|
141
|
+
return
|
|
142
|
+
}
|
|
57
143
|
guard let lineHeight = selectorSummaryLineHeight, let titleLabel else { return }
|
|
58
144
|
// OneKey patch: position the final source line box after UIKit has measured the button.
|
|
59
145
|
let top = ceil((bounds.height - lineHeight) / 2 * sourcePixelScale) / sourcePixelScale
|
|
@@ -362,6 +448,8 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
362
448
|
private let metricSubtitleLabel = UILabel()
|
|
363
449
|
private let metricCompositeStack = UIStackView()
|
|
364
450
|
private let badgeLabel = NativeListInsetLabel()
|
|
451
|
+
private let marketBadgeButtons = (0..<3).map { _ in UIButton(type: .system) }
|
|
452
|
+
private let marketBadgeImages = (0..<3).map { _ in OneKeyImageReusableView(frame: .zero) }
|
|
365
453
|
private let actionStack = UIStackView()
|
|
366
454
|
private let actionButtons = (0..<3).map { _ in UIButton(type: .system) }
|
|
367
455
|
private let trailingStack = UIStackView()
|
|
@@ -412,6 +500,7 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
412
500
|
private var boundCheckboxData: [String: Any]?
|
|
413
501
|
private var boundCheckboxTarget: NativeSelectionTarget?
|
|
414
502
|
private var leadingActionKey: String?
|
|
503
|
+
private var marketBadgeActionKeys: [String?] = []
|
|
415
504
|
private var restingBackgroundColor: UIColor = .clear
|
|
416
505
|
private var pressedBackgroundColor = UIColor(
|
|
417
506
|
nativeListHex: "#E8E8E8",
|
|
@@ -612,6 +701,23 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
612
701
|
titleRowStack.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
|
613
702
|
titleRowStack.addArrangedSubview(titleLabel)
|
|
614
703
|
titleRowStack.addArrangedSubview(badgeLabel)
|
|
704
|
+
marketBadgeButtons.enumerated().forEach { index, button in
|
|
705
|
+
button.tag = index
|
|
706
|
+
button.addTarget(self, action: #selector(marketBadgePressed(_:)), for: .touchUpInside)
|
|
707
|
+
button.titleLabel?.font = nativeListFont(ofSize: 11, weight: .medium)
|
|
708
|
+
button.layer.cornerRadius = 4
|
|
709
|
+
button.clipsToBounds = true
|
|
710
|
+
let image = marketBadgeImages[index]
|
|
711
|
+
image.isUserInteractionEnabled = false
|
|
712
|
+
image.translatesAutoresizingMaskIntoConstraints = false
|
|
713
|
+
button.addSubview(image)
|
|
714
|
+
NSLayoutConstraint.activate([
|
|
715
|
+
image.centerYAnchor.constraint(equalTo: button.centerYAnchor),
|
|
716
|
+
image.widthAnchor.constraint(equalToConstant: 14),
|
|
717
|
+
image.heightAnchor.constraint(equalToConstant: 14),
|
|
718
|
+
])
|
|
719
|
+
titleRowStack.addArrangedSubview(button)
|
|
720
|
+
}
|
|
615
721
|
titleLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
616
722
|
titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
617
723
|
badgeLabel.setContentHuggingPriority(.required, for: .horizontal)
|
|
@@ -843,6 +949,7 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
843
949
|
case "activity": bindActivity(item, theme: theme)
|
|
844
950
|
case "message": bindMessage(item, theme: theme)
|
|
845
951
|
case "dataRow": bindDataRow(item, theme: theme, checkboxState)
|
|
952
|
+
case "market": bindMarket(item, theme: theme)
|
|
846
953
|
case "mediaTile": bindMediaTile(item, theme: theme)
|
|
847
954
|
case "metricCard": bindMetricCard(item, theme: theme)
|
|
848
955
|
case "sectionHeader": bindSectionHeader(item, theme: theme, layout: layout, checkboxState)
|
|
@@ -1041,6 +1148,7 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
1041
1148
|
selectorConstraints.removeAll()
|
|
1042
1149
|
selectorImages.forEach { $0.prepareForReuse() }
|
|
1043
1150
|
selectorImages.removeAll()
|
|
1151
|
+
marketBadgeImages.forEach { $0.prepareForReuse(); $0.isHidden = true }
|
|
1044
1152
|
selectorFullWidthBackground.removeFromSuperlayer()
|
|
1045
1153
|
selectorBorder?.removeFromSuperlayer()
|
|
1046
1154
|
selectorBorder = nil
|
|
@@ -1123,6 +1231,7 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
1123
1231
|
$0.isHidden = true
|
|
1124
1232
|
$0.alpha = 1
|
|
1125
1233
|
$0.layer.cornerRadius = 0
|
|
1234
|
+
$0.layer.mask = nil
|
|
1126
1235
|
}
|
|
1127
1236
|
leadingContainer.clipsToBounds = true
|
|
1128
1237
|
leadingContainer.layer.borderWidth = 0
|
|
@@ -1178,6 +1287,19 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
1178
1287
|
badgeLabel.backgroundColor = .clear
|
|
1179
1288
|
badgeLabel.layer.cornerRadius = 0
|
|
1180
1289
|
badgeLabel.clipsToBounds = false
|
|
1290
|
+
marketBadgeButtons.forEach {
|
|
1291
|
+
$0.isHidden = true
|
|
1292
|
+
$0.isEnabled = true
|
|
1293
|
+
$0.isUserInteractionEnabled = false
|
|
1294
|
+
$0.setTitle(nil, for: .normal)
|
|
1295
|
+
$0.setImage(nil, for: .normal)
|
|
1296
|
+
$0.setTitleColor(nil, for: .normal)
|
|
1297
|
+
$0.tintColor = nil
|
|
1298
|
+
$0.backgroundColor = .clear
|
|
1299
|
+
$0.contentEdgeInsets = .zero
|
|
1300
|
+
$0.imageEdgeInsets = .zero
|
|
1301
|
+
$0.titleEdgeInsets = .zero
|
|
1302
|
+
}
|
|
1181
1303
|
[titleLabel, subtitleLabel, tertiaryLabel, statusLabel, metricSubtitleLabel, badgeLabel, actionStack]
|
|
1182
1304
|
.forEach { $0.isHidden = true }
|
|
1183
1305
|
separatorView.isHidden = true
|
|
@@ -1188,7 +1310,9 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
1188
1310
|
$0.backgroundColor = .clear
|
|
1189
1311
|
}
|
|
1190
1312
|
accessoryButtons.enumerated().forEach { index, button in
|
|
1313
|
+
button.isUserInteractionEnabled = true
|
|
1191
1314
|
button.selectorSummaryLineHeight = nil
|
|
1315
|
+
button.marketLineHeight = nil
|
|
1192
1316
|
button.titleLabel?.font = nativeListFont(
|
|
1193
1317
|
ofSize: index == 0 ? 16 : 14,
|
|
1194
1318
|
weight: index == 0 ? .medium : .regular
|
|
@@ -1233,6 +1357,7 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
1233
1357
|
boundCheckboxData = nil
|
|
1234
1358
|
boundCheckboxTarget = nil
|
|
1235
1359
|
leadingActionKey = nil
|
|
1360
|
+
marketBadgeActionKeys = []
|
|
1236
1361
|
layer.maskedCorners = []
|
|
1237
1362
|
layer.cornerRadius = 0
|
|
1238
1363
|
layer.cornerCurve = .circular
|
|
@@ -2014,6 +2139,283 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
2014
2139
|
}
|
|
2015
2140
|
}
|
|
2016
2141
|
|
|
2142
|
+
private func marketFontWeight(_ value: String, fallback: NativeListFontWeight) -> NativeListFontWeight {
|
|
2143
|
+
switch value {
|
|
2144
|
+
case "regular": return .regular
|
|
2145
|
+
case "semibold": return .semibold
|
|
2146
|
+
case "bold": return .bold
|
|
2147
|
+
case "medium": return .medium
|
|
2148
|
+
default: return fallback
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
private func marketTextAlignment(_ value: String) -> NSTextAlignment {
|
|
2153
|
+
switch value {
|
|
2154
|
+
case "center": return .center
|
|
2155
|
+
case "start": return effectiveUserInterfaceLayoutDirection == .rightToLeft ? .right : .left
|
|
2156
|
+
case "end": return effectiveUserInterfaceLayoutDirection == .rightToLeft ? .left : .right
|
|
2157
|
+
default: return .natural
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
private func applyMarketTextStyle(
|
|
2162
|
+
_ label: UILabel,
|
|
2163
|
+
data: [String: Any]?,
|
|
2164
|
+
theme: [String: Any]?,
|
|
2165
|
+
defaultSize: CGFloat,
|
|
2166
|
+
defaultLineHeight: CGFloat,
|
|
2167
|
+
defaultWeight: NativeListFontWeight,
|
|
2168
|
+
defaultColor: UIColor,
|
|
2169
|
+
defaultAlignment: NSTextAlignment = .natural
|
|
2170
|
+
) {
|
|
2171
|
+
let size = CGFloat(data?.double("fontSize", default: Double(defaultSize)) ?? Double(defaultSize))
|
|
2172
|
+
let lineHeight = CGFloat(data?.double("lineHeight", default: Double(defaultLineHeight)) ?? Double(defaultLineHeight))
|
|
2173
|
+
label.font = nativeListTabularFont(ofSize: size, weight: marketFontWeight(data?.string("fontWeight") ?? "", fallback: defaultWeight))
|
|
2174
|
+
label.textColor = data?["color"].flatMap { $0 as? String }.map { UIColor(nativeListHex: $0, fallback: defaultColor) } ?? defaultColor
|
|
2175
|
+
label.textAlignment = data?["alignment"] == nil
|
|
2176
|
+
? defaultAlignment
|
|
2177
|
+
: marketTextAlignment(data?.string("alignment") ?? "")
|
|
2178
|
+
label.numberOfLines = min(2, max(1, data?.int("lines", default: 1) ?? 1))
|
|
2179
|
+
setLineHeight(label, text: label.text ?? "", lineHeight: lineHeight)
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
private func applyMarketButtonStyle(
|
|
2183
|
+
_ button: UIButton,
|
|
2184
|
+
data: [String: Any]?,
|
|
2185
|
+
defaultSize: CGFloat,
|
|
2186
|
+
defaultLineHeight: CGFloat,
|
|
2187
|
+
defaultWeight: NativeListFontWeight,
|
|
2188
|
+
color: UIColor,
|
|
2189
|
+
defaultAlignment: UIControl.ContentHorizontalAlignment
|
|
2190
|
+
) {
|
|
2191
|
+
let size = CGFloat(data?.double("fontSize", default: Double(defaultSize)) ?? Double(defaultSize))
|
|
2192
|
+
let lineHeight = CGFloat(data?.double("lineHeight", default: Double(defaultLineHeight)) ?? Double(defaultLineHeight))
|
|
2193
|
+
setButtonLine(
|
|
2194
|
+
button,
|
|
2195
|
+
text: button.title(for: .normal) ?? "",
|
|
2196
|
+
font: nativeListTabularFont(ofSize: size, weight: marketFontWeight(data?.string("fontWeight") ?? "", fallback: defaultWeight)),
|
|
2197
|
+
color: data?["color"].flatMap { $0 as? String }.map { UIColor(nativeListHex: $0, fallback: color) } ?? color,
|
|
2198
|
+
lineHeight: lineHeight
|
|
2199
|
+
)
|
|
2200
|
+
if data?["alignment"] == nil {
|
|
2201
|
+
button.contentHorizontalAlignment = defaultAlignment
|
|
2202
|
+
} else {
|
|
2203
|
+
button.contentHorizontalAlignment = data?.string("alignment") == "start"
|
|
2204
|
+
? .leading
|
|
2205
|
+
: data?.string("alignment") == "end" ? .trailing : .center
|
|
2206
|
+
}
|
|
2207
|
+
button.titleLabel?.numberOfLines = min(2, max(1, data?.int("lines", default: 1) ?? 1))
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
private func marketAttributedText(
|
|
2211
|
+
_ text: String,
|
|
2212
|
+
segments: [[String: Any]],
|
|
2213
|
+
style: [String: Any]?,
|
|
2214
|
+
defaultSize: CGFloat,
|
|
2215
|
+
defaultLineHeight: CGFloat,
|
|
2216
|
+
defaultWeight: NativeListFontWeight,
|
|
2217
|
+
color: UIColor,
|
|
2218
|
+
defaultAlignment: NSTextAlignment
|
|
2219
|
+
) -> NSAttributedString {
|
|
2220
|
+
let size = CGFloat(style?.double("fontSize", default: Double(defaultSize)) ?? Double(defaultSize))
|
|
2221
|
+
let lineHeight = CGFloat(style?.double("lineHeight", default: Double(defaultLineHeight)) ?? Double(defaultLineHeight))
|
|
2222
|
+
let paragraph = NSMutableParagraphStyle()
|
|
2223
|
+
paragraph.minimumLineHeight = lineHeight
|
|
2224
|
+
paragraph.maximumLineHeight = lineHeight
|
|
2225
|
+
paragraph.alignment = style?["alignment"] == nil
|
|
2226
|
+
? defaultAlignment
|
|
2227
|
+
: marketTextAlignment(style?.string("alignment") ?? "")
|
|
2228
|
+
let weight = marketFontWeight(style?.string("fontWeight") ?? "", fallback: defaultWeight)
|
|
2229
|
+
let resolvedColor = style?["color"].flatMap { $0 as? String }.map { UIColor(nativeListHex: $0, fallback: color) } ?? color
|
|
2230
|
+
let result = NSMutableAttributedString(string: "")
|
|
2231
|
+
let source = segments.isEmpty ? [["text": text]] : segments
|
|
2232
|
+
for segment in source {
|
|
2233
|
+
let segmentSize = segment.string("style") == "subscript" ? ceil(size * 0.6) : size
|
|
2234
|
+
result.append(NSAttributedString(string: segment.string("text"), attributes: [
|
|
2235
|
+
.font: nativeListTabularFont(ofSize: segmentSize, weight: weight),
|
|
2236
|
+
.foregroundColor: resolvedColor,
|
|
2237
|
+
.kern: 0,
|
|
2238
|
+
.paragraphStyle: paragraph,
|
|
2239
|
+
.baselineOffset: max(0, (lineHeight - nativeListTabularFont(ofSize: size, weight: weight).lineHeight) / 2),
|
|
2240
|
+
]))
|
|
2241
|
+
}
|
|
2242
|
+
return result
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
private func marketLeading(_ item: NativeListItem, style: [String: Any]?) -> [String: Any]? {
|
|
2246
|
+
guard var visual = item.data.dictionary("leading") else { return nil }
|
|
2247
|
+
guard let imageStyle = style?.dictionary("image") else { return visual }
|
|
2248
|
+
if let shape = imageStyle["shape"] as? String { visual["shape"] = shape }
|
|
2249
|
+
if let contentFit = imageStyle["contentFit"] as? String, var image = visual.dictionary("image") {
|
|
2250
|
+
image["contentFit"] = contentFit
|
|
2251
|
+
visual["image"] = image
|
|
2252
|
+
}
|
|
2253
|
+
return visual
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
private func bindMarket(_ item: NativeListItem, theme: [String: Any]?) {
|
|
2257
|
+
let variant = item.data.string("variant")
|
|
2258
|
+
let style = item.data.dictionary("style")
|
|
2259
|
+
let imageStyle = style?.dictionary("image")
|
|
2260
|
+
let imageWidth = CGFloat(imageStyle?.double("width", default: variant == "stock" ? 40 : 32) ?? (variant == "stock" ? 40 : 32))
|
|
2261
|
+
let imageHeight = CGFloat(imageStyle?.double("height", default: variant == "stock" ? 40 : 32) ?? (variant == "stock" ? 40 : 32))
|
|
2262
|
+
let horizontalPadding = CGFloat(style?.double("horizontalPadding", default: variant == "perp" ? 16 : 20) ?? (variant == "perp" ? 16 : 20))
|
|
2263
|
+
let verticalPadding = CGFloat(style?.double("verticalPadding", default: 12) ?? 12)
|
|
2264
|
+
rootLeadingConstraint.constant = horizontalPadding
|
|
2265
|
+
rootTrailingConstraint.constant = -horizontalPadding
|
|
2266
|
+
rootTopConstraint.constant = verticalPadding
|
|
2267
|
+
rootBottomConstraint.constant = -verticalPadding
|
|
2268
|
+
rootStack.spacing = CGFloat(style?.double("leadingGap", default: variant == "perp" ? 8 : 14) ?? (variant == "perp" ? 8 : 14))
|
|
2269
|
+
leadingWidth.constant = imageWidth
|
|
2270
|
+
leadingHeight.constant = imageHeight
|
|
2271
|
+
if let visual = marketLeading(item, style: style) {
|
|
2272
|
+
addLeading(visual, key: item.key)
|
|
2273
|
+
let radius = CGFloat(imageStyle?.double("cornerRadius", default: imageStyle?.string("shape") == "square" ? 0 : imageStyle?.string("shape") == "rounded" ? 8 : Double(min(imageWidth, imageHeight) / 2)) ?? Double(min(imageWidth, imageHeight) / 2))
|
|
2274
|
+
leadingContainer.layer.cornerRadius = radius
|
|
2275
|
+
leadingImages.first?.layer.cornerRadius = radius
|
|
2276
|
+
if let image = leadingImages.first, !visual.string("borderColor").isEmpty {
|
|
2277
|
+
// Match Token's border box: the bitmap occupies the one-point inset.
|
|
2278
|
+
leadingContainer.layer.borderWidth = 1
|
|
2279
|
+
leadingContainer.layer.borderColor = UIColor(nativeListHex: visual.string("borderColor"), fallback: .clear).cgColor
|
|
2280
|
+
for constraint in leadingSlotConstraints where constraint.firstItem === image {
|
|
2281
|
+
switch constraint.firstAttribute {
|
|
2282
|
+
case .leading, .top: constraint.constant = 1
|
|
2283
|
+
case .trailing, .bottom: constraint.constant = -1
|
|
2284
|
+
default: break
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
image.layer.cornerRadius = 0
|
|
2288
|
+
let mask = CAShapeLayer()
|
|
2289
|
+
mask.path = UIBezierPath(roundedRect: CGRect(x: -1, y: -1, width: imageWidth, height: imageHeight), cornerRadius: radius).cgPath
|
|
2290
|
+
image.layer.mask = mask
|
|
2291
|
+
}
|
|
2292
|
+
if let diagnostic = item.data.dictionary("diagnostics")?.string("imageBindActionKey"), !diagnostic.isEmpty,
|
|
2293
|
+
visual.dictionary("image") != nil || visual.dictionary("networkImage") != nil {
|
|
2294
|
+
onAction?(item, diagnostic, nil, nil)
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
rootStack.addArrangedSubview(mainStack)
|
|
2298
|
+
rootStack.setCustomSpacing(0, after: mainStack)
|
|
2299
|
+
mainStack.spacing = CGFloat(style?.double("lineGap", default: 0) ?? 0)
|
|
2300
|
+
titleRowStack.spacing = CGFloat(style?.double("titleBadgeGap", default: 4) ?? 4)
|
|
2301
|
+
show(titleLabel, item.data.string("title"), lines: style?.dictionary("title")?.int("lines", default: 1) ?? 1)
|
|
2302
|
+
applyMarketTextStyle(titleLabel, data: style?.dictionary("title"), theme: theme, defaultSize: 16, defaultLineHeight: 24, defaultWeight: .medium, defaultColor: nativeListColor(theme, "primaryText", "#202020"))
|
|
2303
|
+
let badges = Array(item.data.dictionaries("badges").prefix(marketBadgeButtons.count))
|
|
2304
|
+
marketBadgeActionKeys = badges.map { $0["actionKey"] as? String }
|
|
2305
|
+
for (index, badge) in badges.enumerated() {
|
|
2306
|
+
let button = marketBadgeButtons[index]
|
|
2307
|
+
let hasBuiltInIcon = badge.string("iconName") == "verified"
|
|
2308
|
+
let hasRemoteIcon = badge.dictionary("icon") != nil
|
|
2309
|
+
let hasIcon = hasBuiltInIcon || hasRemoteIcon
|
|
2310
|
+
let text = badge.string("text")
|
|
2311
|
+
let toneColor: UIColor
|
|
2312
|
+
switch badge.string("tone") {
|
|
2313
|
+
case "success": toneColor = nativeListColor(theme, "positive", "#218358")
|
|
2314
|
+
case "danger": toneColor = nativeListColor(theme, "negative", "#CE2C31")
|
|
2315
|
+
case "info": toneColor = nativeListColor(theme, "info", "#0D74CE")
|
|
2316
|
+
case "warning": toneColor = nativeListColor(theme, "primaryText", "#202020")
|
|
2317
|
+
default: toneColor = nativeListColor(theme, "secondaryText", "#646464")
|
|
2318
|
+
}
|
|
2319
|
+
let foreground = UIColor(
|
|
2320
|
+
nativeListHex: badge.string("textColor", default: ""),
|
|
2321
|
+
fallback: toneColor
|
|
2322
|
+
)
|
|
2323
|
+
button.isHidden = false
|
|
2324
|
+
button.isEnabled = true
|
|
2325
|
+
button.isUserInteractionEnabled = !badge.string("actionKey").isEmpty
|
|
2326
|
+
button.accessibilityTraits = button.isUserInteractionEnabled ? .button : .staticText
|
|
2327
|
+
button.setTitle(text, for: .normal)
|
|
2328
|
+
button.setTitleColor(foreground, for: .normal)
|
|
2329
|
+
button.tintColor = foreground
|
|
2330
|
+
button.backgroundColor = UIColor(
|
|
2331
|
+
nativeListHex: badge.string("backgroundColor", default: ""),
|
|
2332
|
+
fallback: hasIcon && text.isEmpty
|
|
2333
|
+
? .clear
|
|
2334
|
+
: nativeListColor(theme, "strongBackground", "#0000000F")
|
|
2335
|
+
)
|
|
2336
|
+
let iconOnly = hasIcon && text.isEmpty
|
|
2337
|
+
let iconSize: CGFloat = hasBuiltInIcon ? 16 : 14
|
|
2338
|
+
button.contentEdgeInsets = UIEdgeInsets(top: 0, left: iconOnly ? 0 : hasRemoteIcon ? 20 : hasIcon ? 3 : 5, bottom: 0, right: iconOnly ? 0 : 5)
|
|
2339
|
+
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: text.isEmpty ? 0 : 3)
|
|
2340
|
+
button.titleEdgeInsets = .zero
|
|
2341
|
+
button.imageView?.contentMode = .scaleAspectFit
|
|
2342
|
+
if hasBuiltInIcon {
|
|
2343
|
+
button.setImage(
|
|
2344
|
+
nativeListIcon(named: "BadgeVerifiedSolid", size: CGSize(width: iconSize, height: iconSize)),
|
|
2345
|
+
for: .normal
|
|
2346
|
+
)
|
|
2347
|
+
}
|
|
2348
|
+
let height = button.heightAnchor.constraint(equalToConstant: 18)
|
|
2349
|
+
let textWidth = (text as NSString).size(
|
|
2350
|
+
withAttributes: [.font: nativeListFont(ofSize: 11, weight: .medium)]
|
|
2351
|
+
).width
|
|
2352
|
+
let width = button.widthAnchor.constraint(equalToConstant: iconOnly ? iconSize : ceil(textWidth) + (hasIcon ? iconSize + 11 : 10))
|
|
2353
|
+
NSLayoutConstraint.activate([width, height])
|
|
2354
|
+
selectorConstraints.append(contentsOf: [width, height])
|
|
2355
|
+
if let icon = badge.dictionary("icon") {
|
|
2356
|
+
marketBadgeImages[index].isHidden = false
|
|
2357
|
+
marketBadgeImages[index].layer.cornerRadius = 7
|
|
2358
|
+
marketBadgeImages[index].clipsToBounds = true
|
|
2359
|
+
let imageLeading = marketBadgeImages[index].leadingAnchor.constraint(equalTo: button.leadingAnchor, constant: text.isEmpty ? 0 : 4)
|
|
2360
|
+
imageLeading.isActive = true
|
|
2361
|
+
selectorConstraints.append(imageLeading)
|
|
2362
|
+
bindImage(icon, into: marketBadgeImages[index], token: item.key, slot: 20 + index, variant: "generic")
|
|
2363
|
+
}
|
|
2364
|
+
button.accessibilityLabel = badge.string("accessibilityLabel", default: badge.string("text"))
|
|
2365
|
+
}
|
|
2366
|
+
if !item.data.string("subtitle").isEmpty || !item.data.dictionaries("subtitleSegments").isEmpty {
|
|
2367
|
+
show(subtitleLabel, item.data.string("subtitle"), lines: style?.dictionary("subtitle")?.int("lines", default: 1) ?? 1)
|
|
2368
|
+
applyMarketTextStyle(subtitleLabel, data: style?.dictionary("subtitle"), theme: theme, defaultSize: 14, defaultLineHeight: 20, defaultWeight: .regular, defaultColor: nativeListColor(theme, "secondaryText", "#646464"))
|
|
2369
|
+
if !item.data.dictionaries("subtitleSegments").isEmpty {
|
|
2370
|
+
subtitleLabel.attributedText = marketAttributedText(item.data.string("subtitle"), segments: item.data.dictionaries("subtitleSegments"), style: style?.dictionary("subtitle"), defaultSize: 14, defaultLineHeight: 20, defaultWeight: .regular, color: nativeListColor(theme, "secondaryText", "#646464"), defaultAlignment: .natural)
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
rootStack.addArrangedSubview(trailingStack)
|
|
2374
|
+
trailingStack.axis = .horizontal
|
|
2375
|
+
trailingStack.alignment = .center
|
|
2376
|
+
trailingStack.spacing = CGFloat(style?.double("trailingGap", default: 8) ?? 8)
|
|
2377
|
+
updateMarketQuote(item, theme: theme)
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
func updateMarketQuote(_ item: NativeListItem, theme: [String: Any]?) {
|
|
2381
|
+
guard currentItem?.key == item.key, item.type == "market" else { return }
|
|
2382
|
+
currentItem = item
|
|
2383
|
+
NSLayoutConstraint.deactivate(accessorySizeConstraints)
|
|
2384
|
+
accessorySizeConstraints.removeAll()
|
|
2385
|
+
let style = item.data.dictionary("style")
|
|
2386
|
+
let price = accessoryButtons[0]
|
|
2387
|
+
price.isUserInteractionEnabled = false
|
|
2388
|
+
price.isHidden = false
|
|
2389
|
+
price.setAttributedTitle(nil, for: .normal)
|
|
2390
|
+
price.setTitle(item.data.string("price"), for: .normal)
|
|
2391
|
+
applyMarketButtonStyle(price, data: style?.dictionary("price"), defaultSize: 16, defaultLineHeight: 24, defaultWeight: .medium, color: nativeListColor(theme, "primaryText", "#202020"), defaultAlignment: .trailing)
|
|
2392
|
+
if !item.data.dictionaries("priceSegments").isEmpty {
|
|
2393
|
+
price.setAttributedTitle(marketAttributedText(item.data.string("price"), segments: item.data.dictionaries("priceSegments"), style: style?.dictionary("price"), defaultSize: 16, defaultLineHeight: 24, defaultWeight: .medium, color: nativeListColor(theme, "primaryText", "#202020"), defaultAlignment: marketTextAlignment("end")), for: .normal)
|
|
2394
|
+
}
|
|
2395
|
+
let changeData = item.data.dictionary("change") ?? [:]
|
|
2396
|
+
let change = accessoryButtons[1]
|
|
2397
|
+
change.isUserInteractionEnabled = false
|
|
2398
|
+
change.isHidden = false
|
|
2399
|
+
change.setAttributedTitle(nil, for: .normal)
|
|
2400
|
+
change.setTitle(changeData.string("text"), for: .normal)
|
|
2401
|
+
let defaultChangeColor = nativeListColor(theme, "inverseText", "#FFFFFF")
|
|
2402
|
+
applyMarketButtonStyle(change, data: style?.dictionary("change"), defaultSize: 14, defaultLineHeight: 20, defaultWeight: .medium, color: UIColor(nativeListHex: changeData.string("textColor", default: "#FFFFFF"), fallback: defaultChangeColor), defaultAlignment: .center)
|
|
2403
|
+
if !changeData.dictionaries("textSegments").isEmpty {
|
|
2404
|
+
let changeTextColor = UIColor(nativeListHex: changeData.string("textColor", default: ""), fallback: defaultChangeColor)
|
|
2405
|
+
change.setAttributedTitle(marketAttributedText(changeData.string("text"), segments: changeData.dictionaries("textSegments"), style: style?.dictionary("change"), defaultSize: 14, defaultLineHeight: 20, defaultWeight: .medium, color: changeTextColor, defaultAlignment: .center), for: .normal)
|
|
2406
|
+
}
|
|
2407
|
+
let toneKey = changeData.string("tone") == "positive" ? "positive" : changeData.string("tone") == "negative" ? "negative" : "secondaryText"
|
|
2408
|
+
change.backgroundColor = UIColor(nativeListHex: changeData.string("backgroundColor", default: ""), fallback: nativeListColor(theme, toneKey, changeData.string("tone") == "positive" ? "#218358" : changeData.string("tone") == "negative" ? "#CE2C31" : "#8D8D8D"))
|
|
2409
|
+
change.layer.cornerRadius = CGFloat(style?.double("changeCornerRadius", default: 8) ?? 8)
|
|
2410
|
+
change.clipsToBounds = true
|
|
2411
|
+
let width = change.widthAnchor.constraint(equalToConstant: CGFloat(style?.double("changeWidth", default: 80) ?? 80))
|
|
2412
|
+
let height = change.heightAnchor.constraint(equalToConstant: CGFloat(style?.double("changeHeight", default: 32) ?? 32))
|
|
2413
|
+
width.isActive = true
|
|
2414
|
+
height.isActive = true
|
|
2415
|
+
accessorySizeConstraints.append(contentsOf: [width, height])
|
|
2416
|
+
accessibilityLabel = item.data.string("accessibilityLabel", default: [item.data.string("title"), item.data.string("subtitle"), item.data.string("price"), changeData.string("text")].filter { !$0.isEmpty }.joined(separator: ", "))
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2017
2419
|
private func bindMediaTile(_ item: NativeListItem, theme: [String: Any]?) {
|
|
2018
2420
|
rootStack.axis = .vertical
|
|
2019
2421
|
rootStack.alignment = .fill
|
|
@@ -2630,6 +3032,76 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
2630
3032
|
rootStack.alignment = .center
|
|
2631
3033
|
rootStack.distribution = .fill
|
|
2632
3034
|
let variant = item.data.string("variant")
|
|
3035
|
+
let isMarket = item.data.string("presentation") == "market"
|
|
3036
|
+
if isMarket {
|
|
3037
|
+
rootLeadingConstraint.constant = 20
|
|
3038
|
+
rootTrailingConstraint.constant = -20
|
|
3039
|
+
rootTopConstraint.constant = 12
|
|
3040
|
+
rootBottomConstraint.constant = -12
|
|
3041
|
+
}
|
|
3042
|
+
if variant == "loading" && item.data.string("loadingStyle") == "skeleton" {
|
|
3043
|
+
rootLeadingConstraint.constant = 20
|
|
3044
|
+
rootTrailingConstraint.constant = -20
|
|
3045
|
+
rootTopConstraint.constant = 12
|
|
3046
|
+
rootBottomConstraint.constant = -12
|
|
3047
|
+
let skeleton = NativeListMarketSkeleton(background: nativeListColor(theme, "background", "#FFFFFF"))
|
|
3048
|
+
skeleton.translatesAutoresizingMaskIntoConstraints = false
|
|
3049
|
+
skeleton.heightAnchor.constraint(equalToConstant: 32).isActive = true
|
|
3050
|
+
rootStack.addArrangedSubview(skeleton)
|
|
3051
|
+
selectorViews.append(skeleton)
|
|
3052
|
+
return
|
|
3053
|
+
}
|
|
3054
|
+
if variant == "loading" && item.data.string("loadingStyle") == "spinner" {
|
|
3055
|
+
rootTopConstraint.constant = 16
|
|
3056
|
+
rootBottomConstraint.constant = -16
|
|
3057
|
+
rootStack.addArrangedSubview(mainStack)
|
|
3058
|
+
mainStack.alignment = .center
|
|
3059
|
+
mainStack.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
3060
|
+
let indicator = UIActivityIndicatorView(style: .medium)
|
|
3061
|
+
indicator.color = nativeListColor(theme, "icon", "#0000009B")
|
|
3062
|
+
indicator.startAnimating()
|
|
3063
|
+
mainStack.addArrangedSubview(indicator)
|
|
3064
|
+
selectorViews.append(indicator)
|
|
3065
|
+
return
|
|
3066
|
+
}
|
|
3067
|
+
if isMarket && variant == "noMatch" {
|
|
3068
|
+
rootTopConstraint.constant = 32
|
|
3069
|
+
rootBottomConstraint.constant = -32
|
|
3070
|
+
rootStack.addArrangedSubview(mainStack)
|
|
3071
|
+
mainStack.alignment = .center
|
|
3072
|
+
mainStack.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
3073
|
+
titleLabel.font = nativeListFont(ofSize: 16)
|
|
3074
|
+
titleLabel.textColor = nativeListColor(theme, "secondaryText", "#646464")
|
|
3075
|
+
titleLabel.textAlignment = .center
|
|
3076
|
+
show(titleLabel, item.data.string("message"), lines: 1)
|
|
3077
|
+
setLineHeight(titleLabel, text: item.data.string("message"), lineHeight: 24)
|
|
3078
|
+
return
|
|
3079
|
+
}
|
|
3080
|
+
if isMarket && variant == "end" {
|
|
3081
|
+
rootTopConstraint.constant = 16
|
|
3082
|
+
rootBottomConstraint.constant = -16
|
|
3083
|
+
rootStack.addArrangedSubview(mainStack)
|
|
3084
|
+
mainStack.alignment = .center
|
|
3085
|
+
mainStack.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
3086
|
+
let indicator = UIStackView()
|
|
3087
|
+
indicator.axis = .horizontal
|
|
3088
|
+
indicator.alignment = .center
|
|
3089
|
+
indicator.spacing = 8
|
|
3090
|
+
for width in [CGFloat(80), 4, 80] {
|
|
3091
|
+
let mark = UIView()
|
|
3092
|
+
mark.translatesAutoresizingMaskIntoConstraints = false
|
|
3093
|
+
mark.backgroundColor = nativeListColor(theme, "separator", "#0000001F")
|
|
3094
|
+
mark.layer.cornerRadius = width == 4 ? 2 : 0
|
|
3095
|
+
NSLayoutConstraint.activate([
|
|
3096
|
+
mark.widthAnchor.constraint(equalToConstant: width),
|
|
3097
|
+
mark.heightAnchor.constraint(equalToConstant: width == 4 ? 4 : 1),
|
|
3098
|
+
])
|
|
3099
|
+
indicator.addArrangedSubview(mark)
|
|
3100
|
+
}
|
|
3101
|
+
mainStack.addArrangedSubview(indicator)
|
|
3102
|
+
selectorViews.append(indicator)
|
|
3103
|
+
return
|
|
3104
|
+
}
|
|
2633
3105
|
// OneKey patch: deprecated-wallet warnings stay inside the scrolling list.
|
|
2634
3106
|
if variant == "warning" {
|
|
2635
3107
|
rootStack.addArrangedSubview(mainStack)
|
|
@@ -2661,10 +3133,10 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
2661
3133
|
return
|
|
2662
3134
|
}
|
|
2663
3135
|
if variant == "loading" {
|
|
2664
|
-
leadingWidth.constant = 40
|
|
2665
|
-
leadingHeight.constant = 40
|
|
3136
|
+
leadingWidth.constant = isMarket ? 32 : 40
|
|
3137
|
+
leadingHeight.constant = isMarket ? 32 : 40
|
|
2666
3138
|
leadingContainer.backgroundColor = nativeListColor(theme, "strongBackground", "#F0F0F0")
|
|
2667
|
-
leadingContainer.layer.cornerRadius = 20
|
|
3139
|
+
leadingContainer.layer.cornerRadius = isMarket ? 16 : 20
|
|
2668
3140
|
rootStack.addArrangedSubview(leadingContainer)
|
|
2669
3141
|
configureSkeleton(skeletonPrimary, width: 120, height: 12, theme: theme)
|
|
2670
3142
|
configureSkeleton(skeletonSecondary, width: 80, height: 12, theme: theme)
|
|
@@ -3076,8 +3548,8 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
3076
3548
|
let paragraphStyle = NSMutableParagraphStyle()
|
|
3077
3549
|
paragraphStyle.minimumLineHeight = lineHeight
|
|
3078
3550
|
paragraphStyle.maximumLineHeight = lineHeight
|
|
3079
|
-
if currentItem?.data.string("presentation") == "walletSidebar" {
|
|
3080
|
-
//
|
|
3551
|
+
if currentItem?.type == "market" || currentItem?.data.string("presentation") == "walletSidebar" {
|
|
3552
|
+
// Attributed paragraphs must preserve the label alignment and tail ellipsis.
|
|
3081
3553
|
paragraphStyle.alignment = label.textAlignment
|
|
3082
3554
|
paragraphStyle.lineBreakMode = label.lineBreakMode
|
|
3083
3555
|
}
|
|
@@ -3086,15 +3558,15 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
3086
3558
|
.foregroundColor: label.textColor as Any,
|
|
3087
3559
|
.paragraphStyle: paragraphStyle,
|
|
3088
3560
|
]
|
|
3089
|
-
if (currentItem?.data["height"] != nil && (["accountSelector", "walletSidebar"].contains(currentItem?.data.string("presentation") ?? "") || currentItem?.type == "sectionHeader" && currentItem?.data.string("presentation") == "networkSelector")) || currentItem?.type == "system" && currentItem?.data.string("variant") == "warning" {
|
|
3561
|
+
if currentItem?.type == "market" || (currentItem?.data["height"] != nil && (["accountSelector", "walletSidebar"].contains(currentItem?.data.string("presentation") ?? "") || currentItem?.type == "sectionHeader" && currentItem?.data.string("presentation") == "networkSelector")) || currentItem?.type == "system" && currentItem?.data.string("variant") == "warning" {
|
|
3090
3562
|
// OneKey patch: React Native centers font metrics inside explicit line heights.
|
|
3091
3563
|
let baselineOffset = max(0, (lineHeight - label.font.lineHeight) / 2)
|
|
3092
3564
|
// OneKey patch: TextKit's 14/20 headings align their baseline to the upper physical pixel.
|
|
3093
|
-
let isSelectorHeading = currentItem?.type == "sectionHeader" && currentItem?.data.string("presentation") == "networkSelector" &&
|
|
3565
|
+
let isSelectorHeading = lineHeight == 20 && (currentItem?.type == "sectionHeader" && currentItem?.data.string("presentation") == "networkSelector" || currentItem?.type == "market" && label.font.pointSize == 14)
|
|
3094
3566
|
let scale = window?.screen.scale ?? traitCollection.displayScale
|
|
3095
3567
|
attributes[.baselineOffset] = isSelectorHeading && scale > 0 ? ceil(baselineOffset * scale) / scale : baselineOffset
|
|
3096
3568
|
}
|
|
3097
|
-
if letterSpacing != 0 { attributes[.kern] = letterSpacing }
|
|
3569
|
+
if letterSpacing != 0 || currentItem?.type == "market" { attributes[.kern] = letterSpacing }
|
|
3098
3570
|
label.attributedText = NSAttributedString(string: text, attributes: attributes)
|
|
3099
3571
|
}
|
|
3100
3572
|
|
|
@@ -3112,8 +3584,10 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
3112
3584
|
let isSelectorValue = currentItem?.data.string("presentation") == "networkSelector" && currentItem?.data["height"] != nil && currentItem?.data.string("variant") != "summary"
|
|
3113
3585
|
let isSelectorSummary = currentItem?.type == "sectionHeader" && currentItem?.data.string("presentation") == "networkSelector" && currentItem?.data["height"] != nil && currentItem?.data.string("variant") == "summary"
|
|
3114
3586
|
(button as? NativeListAccessoryButton)?.selectorSummaryLineHeight = isSelectorSummary ? lineHeight : nil
|
|
3587
|
+
(button as? NativeListAccessoryButton)?.marketLineHeight = currentItem?.type == "market" ? lineHeight : nil
|
|
3115
3588
|
// OneKey patch: summary text uses its source line box; currency retains trailing alignment.
|
|
3116
|
-
|
|
3589
|
+
// Market's line box already handles alignment; source text starts at its origin.
|
|
3590
|
+
paragraphStyle.alignment = isSelectorValue ? .right : isSelectorSummary || currentItem?.type == "market" ? .natural : .center
|
|
3117
3591
|
if isSelectorSummary {
|
|
3118
3592
|
button.contentHorizontalAlignment = .leading
|
|
3119
3593
|
button.titleLabel?.textAlignment = .natural
|
|
@@ -3122,17 +3596,16 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
3122
3596
|
button.contentHorizontalAlignment = .trailing
|
|
3123
3597
|
button.titleLabel?.textAlignment = .right
|
|
3124
3598
|
}
|
|
3125
|
-
let baselineOffset: CGFloat = currentItem?.type == "sectionHeader" && currentItem?.data.string("presentation") == "networkSelector" && currentItem?.data["height"] != nil ? max(0, (lineHeight - font.lineHeight) / 2) : 0
|
|
3599
|
+
let baselineOffset: CGFloat = currentItem?.type == "market" || currentItem?.type == "sectionHeader" && currentItem?.data.string("presentation") == "networkSelector" && currentItem?.data["height"] != nil ? max(0, (lineHeight - font.lineHeight) / 2) : 0
|
|
3600
|
+
var attributes: [NSAttributedString.Key: Any] = [
|
|
3601
|
+
.font: font,
|
|
3602
|
+
.foregroundColor: color,
|
|
3603
|
+
.paragraphStyle: paragraphStyle,
|
|
3604
|
+
.baselineOffset: currentItem?.type == "market" && lineHeight == 20 && font.pointSize == 14 ? ceil(baselineOffset * max(1, traitCollection.displayScale)) / max(1, traitCollection.displayScale) : baselineOffset,
|
|
3605
|
+
]
|
|
3606
|
+
if currentItem?.type == "market" { attributes[.kern] = 0 }
|
|
3126
3607
|
button.setAttributedTitle(
|
|
3127
|
-
NSAttributedString(
|
|
3128
|
-
string: text,
|
|
3129
|
-
attributes: [
|
|
3130
|
-
.font: font,
|
|
3131
|
-
.foregroundColor: color,
|
|
3132
|
-
.paragraphStyle: paragraphStyle,
|
|
3133
|
-
.baselineOffset: baselineOffset,
|
|
3134
|
-
]
|
|
3135
|
-
),
|
|
3608
|
+
NSAttributedString(string: text, attributes: attributes),
|
|
3136
3609
|
for: .normal
|
|
3137
3610
|
)
|
|
3138
3611
|
}
|
|
@@ -3394,6 +3867,19 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
3394
3867
|
)
|
|
3395
3868
|
}
|
|
3396
3869
|
|
|
3870
|
+
@objc private func marketBadgePressed(_ sender: UIButton) {
|
|
3871
|
+
guard let item = currentItem,
|
|
3872
|
+
marketBadgeActionKeys.indices.contains(sender.tag),
|
|
3873
|
+
let actionKey = marketBadgeActionKeys[sender.tag],
|
|
3874
|
+
!actionKey.isEmpty else { return }
|
|
3875
|
+
onAction?(
|
|
3876
|
+
item,
|
|
3877
|
+
actionKey,
|
|
3878
|
+
nil,
|
|
3879
|
+
actionOrigin(sourceView: sender, source: "marketBadge", slot: sender.tag)
|
|
3880
|
+
)
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3397
3883
|
@objc private func footerActionPressed(_ sender: UIButton) {
|
|
3398
3884
|
guard let item = currentItem, footerActionKeys.indices.contains(sender.tag) else { return }
|
|
3399
3885
|
onAction?(
|