@onekeyfe/react-native-native-list 6.7.0-alpha.415 → 6.7.0-alpha.418
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/NativeListAccessoryStack.kt +5 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListGeneratedIcons.kt +47 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListLeadingVisual.kt +5 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt +120 -338
- package/android/src/main/java/com/onekey/nativelist/NativeListRendererRowView.kt +5 -0
- package/docs/SPEC.md +122 -7
- package/ios/NativeListAccessoryStack.swift +6 -16
- package/ios/NativeListDataRowRenderer.swift +15 -6
- package/ios/NativeListDesignAssets.swift +13 -84
- package/ios/NativeListGeneratedIcons.swift +55 -0
- package/ios/NativeListIconBinding.swift +171 -0
- package/ios/NativeListIdentityRenderer.swift +12 -10
- package/ios/NativeListLeadingVisual.swift +11 -9
- package/ios/NativeListMarketRenderer.swift +7 -13
- package/ios/NativeListMediaTileRenderer.swift +6 -2
- package/ios/NativeListMetricCardRenderer.swift +4 -2
- package/ios/NativeListSectionHeaderRenderer.swift +12 -4
- package/ios/tests/NativeListAccessoryStackTests.swift +12 -0
- package/ios/tests/NativeListSharedIconTests.swift +236 -0
- package/lib/module/web/NativeListGeneratedIcons.js +31 -0
- package/lib/module/web/NativeListWebEngine.js +21 -323
- package/lib/module/web/templates/ActivityRowRenderer.js +4 -24
- package/lib/module/web/templates/MarketTableRowRenderer.js +11 -15
- package/lib/module/web/templates/MediaTileRowRenderer.js +2 -12
- package/lib/typescript/src/web/NativeListGeneratedIcons.d.ts +2 -0
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +0 -1
- package/package.json +10 -4
- package/react-native-native-list.podspec +0 -5
- package/scripts/generate-icons.mjs +150 -0
- package/scripts/generate-icons.test.mjs +288 -0
- package/scripts/native-list-icons.json +260 -0
- package/src/web/NativeListGeneratedIcons.ts +28 -0
- package/src/web/NativeListWebEngine.ts +25 -405
- package/src/web/templates/ActivityRowRenderer.ts +15 -53
- package/src/web/templates/MarketTableRowRenderer.ts +17 -29
- package/src/web/templates/MediaTileRowRenderer.ts +2 -15
- package/ios/Resources/NativeListIcons.xcassets/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_bottom.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_bottom.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_top.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_top.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_badge_verified_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_badge_verified_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chart_trending_up.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chart_trending_up.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_checked.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_checked.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_indeterminate.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_indeterminate.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_bottom.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_bottom.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_grabber_ver.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_grabber_ver.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_right_small.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_right_small.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_top.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_top.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_crypto_coin_outline.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_crypto_coin_outline.imageset/icon.svg +0 -4
- package/ios/Resources/NativeListIcons.xcassets/onekey_document2.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_document2.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_dot_hor.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_dot_hor.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_dot_ver_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_dot_ver_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_drag.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_drag.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_error_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_error_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_gas_solid.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_gas_solid.imageset/icon.svg +0 -3
- package/ios/Resources/NativeListIcons.xcassets/onekey_image_square_waves.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_image_square_waves.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_info_circle.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_info_circle.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_pencil.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_pencil.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_circle.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_circle.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_large.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_large.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_small.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_small.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_questionmark_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_questionmark_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_refresh_ccw_outline.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_refresh_ccw_outline.imageset/icon.svg +0 -3
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_account_error_custom.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_account_error_custom.imageset/icon.svg +0 -14
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/icon.svg +0 -12
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_apple_brand.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_apple_brand.imageset/icon.svg +0 -6
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_bot_illus.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_bot_illus.imageset/icon.svg +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_crossed_small_solid.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_crossed_small_solid.imageset/icon.svg +0 -3
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_globus_outline.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_globus_outline.imageset/icon.svg +0 -7
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_google_illus.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_google_illus.imageset/icon.svg +0 -18
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_lock_solid.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_lock_solid.imageset/icon.svg +0 -7
- package/ios/Resources/NativeListIcons.xcassets/onekey_shield_exclamation.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_shield_exclamation.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_speaker_promote.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_speaker_promote.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_star.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_star.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_star_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_star_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_swap_hor.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_swap_hor.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_ticket.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_ticket.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_wallet.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_wallet.imageset/icon.svg +0 -1
|
@@ -109,7 +109,7 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
109
109
|
private let mainStack = UIStackView()
|
|
110
110
|
private let titleRowStack = UIStackView()
|
|
111
111
|
private let titleLabel = NativeListTextLabel()
|
|
112
|
-
private let titleIconView =
|
|
112
|
+
private let titleIconView = NativeListIconImageView()
|
|
113
113
|
private var titleImageSlot: NativeListImageSlot?
|
|
114
114
|
private var titleImageConstraints: [NSLayoutConstraint] = []
|
|
115
115
|
// Absorbs the title row's slack so the title keeps its intrinsic width and the
|
|
@@ -120,7 +120,7 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
120
120
|
private let tertiaryLabel = NativeListTextLabel()
|
|
121
121
|
private let badgeLabel = NativeListInsetLabel()
|
|
122
122
|
private let trailingStack = NativeListAccessoryStack(frame: .zero)
|
|
123
|
-
private let leadingActionButton =
|
|
123
|
+
private let leadingActionButton = NativeListIconButton(type: .system)
|
|
124
124
|
private var leadingWidth: NSLayoutConstraint!
|
|
125
125
|
private var leadingHeight: NSLayoutConstraint!
|
|
126
126
|
private var leftInset: CGFloat = 12
|
|
@@ -208,6 +208,9 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
208
208
|
_ item: NativeListItem, theme: [String: Any]?, layout: String,
|
|
209
209
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String
|
|
210
210
|
) {
|
|
211
|
+
let iconUpdate = NativeListIconUpdate(self)
|
|
212
|
+
defer { iconUpdate.finish() }
|
|
213
|
+
|
|
211
214
|
selectorTypographyRestorers.reversed().forEach { $0() }
|
|
212
215
|
selectorTypographyRestorers = []
|
|
213
216
|
currentItem = item
|
|
@@ -298,7 +301,7 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
298
301
|
bindIdentity(item, theme: theme, selected: item.data.bool("selected"), checkboxState)
|
|
299
302
|
let style = item.data.dictionary("style") ?? [:]
|
|
300
303
|
if let titleIcon = item.data.dictionary("titleIcon") {
|
|
301
|
-
titleIconView.
|
|
304
|
+
titleIconView.bindIcon(titleIcon.string("name"))
|
|
302
305
|
titleIconView.tintColor = UIColor(
|
|
303
306
|
nativeListHex: titleIcon.string("tintColor",
|
|
304
307
|
default: theme?["iconSubdued"] as? String ?? "#8D8D8D"),
|
|
@@ -407,6 +410,7 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
407
410
|
}
|
|
408
411
|
settleTitleRowFillerSpacing()
|
|
409
412
|
applySelectorTypography(item)
|
|
413
|
+
|
|
410
414
|
}
|
|
411
415
|
|
|
412
416
|
/// The filler is the title row's last arranged view; the stack's default
|
|
@@ -422,6 +426,9 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
422
426
|
_ item: NativeListItem,
|
|
423
427
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String
|
|
424
428
|
) {
|
|
429
|
+
let iconUpdate = NativeListIconUpdate(self)
|
|
430
|
+
defer { iconUpdate.finish() }
|
|
431
|
+
|
|
425
432
|
currentItem = item
|
|
426
433
|
trailingStack.updateSelection(item, checkboxState: checkboxState)
|
|
427
434
|
if item.data.string("presentation") == "walletSidebar" {
|
|
@@ -439,6 +446,7 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
439
446
|
NativeListTextStyles.applyStyledText(titleLabel, style, fonts: fonts)
|
|
440
447
|
}
|
|
441
448
|
}
|
|
449
|
+
|
|
442
450
|
}
|
|
443
451
|
override func layoutSubviews() {
|
|
444
452
|
super.layoutSubviews()
|
|
@@ -610,13 +618,7 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
610
618
|
fallback: .darkGray
|
|
611
619
|
)
|
|
612
620
|
leadingActionButton.tintColor = tintColor
|
|
613
|
-
|
|
614
|
-
leadingActionButton.setImage(image, for: .normal)
|
|
615
|
-
leadingActionButton.setImage(
|
|
616
|
-
image.withTintColor(tintColor, renderingMode: .alwaysOriginal),
|
|
617
|
-
for: .disabled
|
|
618
|
-
)
|
|
619
|
-
}
|
|
621
|
+
leadingActionButton.bindIcon(leadingAction.string("name"))
|
|
620
622
|
leadingActionButton.isEnabled = !leadingAction.bool("disabled")
|
|
621
623
|
leadingActionButton.alpha = leadingActionButton.isEnabled ? 1 : 0.4
|
|
622
624
|
leadingActionKey = leadingAction.string("actionKey")
|
|
@@ -14,16 +14,16 @@ final class NativeListLeadingVisual: UIView {
|
|
|
14
14
|
var overlayTextFontSize: CGFloat = 10
|
|
15
15
|
var overlayTextLineHeight: CGFloat?
|
|
16
16
|
var overlayTextHorizontalInset: CGFloat?
|
|
17
|
-
private let icon =
|
|
17
|
+
private let icon = NativeListIconImageView()
|
|
18
18
|
private let unread = UIView()
|
|
19
19
|
private let networkBackdrop = UIView()
|
|
20
20
|
// Ring under the secondary token of an Activity pair (SPEC "Activity leading pair").
|
|
21
21
|
private let pairBackdrop = UIView()
|
|
22
|
-
private let corner =
|
|
22
|
+
private let corner = NativeListIconImageView()
|
|
23
23
|
private let cornerBackdrop = UIView()
|
|
24
24
|
private var slots: [NativeListImageSlot] = []
|
|
25
25
|
private var overlaySlots: [NativeListImageSlot] = []
|
|
26
|
-
private var overlayFallbackIcons: [
|
|
26
|
+
private var overlayFallbackIcons: [NativeListIconImageView] = []
|
|
27
27
|
private var overlays: [(UIView, [String: Any])] = []
|
|
28
28
|
private let dashedBorder = CAShapeLayer()
|
|
29
29
|
private var visual: [String: Any] = [:]
|
|
@@ -54,6 +54,8 @@ final class NativeListLeadingVisual: UIView {
|
|
|
54
54
|
isUnread: Bool, secondaryVisual: [String: Any]? = nil,
|
|
55
55
|
fonts: NativeListFontFamilies
|
|
56
56
|
) {
|
|
57
|
+
let iconUpdate = NativeListIconUpdate(self)
|
|
58
|
+
defer { iconUpdate.finish() }
|
|
57
59
|
self.fonts = fonts
|
|
58
60
|
fallback.font = nativeListFont(ofSize: 13, weight: .bold, fonts: fonts)
|
|
59
61
|
self.visual = visual
|
|
@@ -111,7 +113,7 @@ final class NativeListLeadingVisual: UIView {
|
|
|
111
113
|
fallback.text = String(visual.string("fallbackText").prefix(2))
|
|
112
114
|
fallback.textColor = UIColor(
|
|
113
115
|
nativeListHex: visual.string("fallbackTextColor", default: "#8D8D8D"), fallback: .gray)
|
|
114
|
-
icon.
|
|
116
|
+
icon.bindIcon(fallbackData?.string("name"))
|
|
115
117
|
icon.tintColor = UIColor(
|
|
116
118
|
nativeListHex: fallbackData?.string("tintColor", default: "#646464") ?? "#646464",
|
|
117
119
|
fallback: .darkGray)
|
|
@@ -175,7 +177,7 @@ final class NativeListLeadingVisual: UIView {
|
|
|
175
177
|
let badge = visual.dictionary("cornerIcon")
|
|
176
178
|
corner.isHidden = badge == nil
|
|
177
179
|
cornerBackdrop.isHidden = badge == nil
|
|
178
|
-
corner.
|
|
180
|
+
corner.bindIcon(badge?.string("name"))
|
|
179
181
|
corner.tintColor = UIColor(
|
|
180
182
|
nativeListHex: badge?.string("tintColor", default: "#646464") ?? "#646464",
|
|
181
183
|
fallback: .darkGray)
|
|
@@ -187,12 +189,12 @@ final class NativeListLeadingVisual: UIView {
|
|
|
187
189
|
overlays.removeAll(keepingCapacity: true)
|
|
188
190
|
let descriptors = Array(visual.dictionaries("overlays").prefix(2))
|
|
189
191
|
while overlaySlots.count < descriptors.count { overlaySlots.append(NativeListImageSlot()) }
|
|
190
|
-
while overlayFallbackIcons.count < descriptors.count { overlayFallbackIcons.append(
|
|
192
|
+
while overlayFallbackIcons.count < descriptors.count { overlayFallbackIcons.append(NativeListIconImageView()) }
|
|
191
193
|
for (index, data) in descriptors.enumerated() {
|
|
192
194
|
let frame = index < previousOverlays.count ? previousOverlays[index].0 : UIView()
|
|
193
195
|
let fallbackData = data.dictionary("fallbackIcon")
|
|
194
196
|
let fallbackGlyph = overlayFallbackIcons[index]
|
|
195
|
-
fallbackGlyph.
|
|
197
|
+
fallbackGlyph.bindIcon(fallbackData?.string("name"))
|
|
196
198
|
fallbackGlyph.tintColor = UIColor(
|
|
197
199
|
nativeListHex: fallbackData?.string("tintColor", default: "#646464") ?? "#646464",
|
|
198
200
|
fallback: .darkGray)
|
|
@@ -243,8 +245,8 @@ final class NativeListLeadingVisual: UIView {
|
|
|
243
245
|
child = label
|
|
244
246
|
} else {
|
|
245
247
|
overlaySlots[index].recycle()
|
|
246
|
-
let glyph = frame.subviews.first as?
|
|
247
|
-
glyph.
|
|
248
|
+
let glyph = frame.subviews.first as? NativeListIconImageView ?? NativeListIconImageView()
|
|
249
|
+
glyph.bindIcon(data.string("name"))
|
|
248
250
|
glyph.contentMode = .scaleAspectFit
|
|
249
251
|
glyph.tintColor = UIColor(
|
|
250
252
|
nativeListHex: data.string("tintColor", default: "#646464"), fallback: .darkGray)
|
|
@@ -35,7 +35,7 @@ final class NativeListMarketCell: NativeListRendererCell {
|
|
|
35
35
|
private let marketBadgeButtons = (0..<3).map { _ in NativeListAccessoryButton(type: .system) }
|
|
36
36
|
private let badgeSlots = (0..<3).map { _ in NativeListImageSlot() }
|
|
37
37
|
private var marketBadgeImages: [UIView] { badgeSlots.map { $0.view } }
|
|
38
|
-
private let leadingActionButton =
|
|
38
|
+
private let leadingActionButton = NativeListIconButton(type: .system)
|
|
39
39
|
private var leadingWidth: NSLayoutConstraint!
|
|
40
40
|
private var leadingHeight: NSLayoutConstraint!
|
|
41
41
|
private var selectorConstraints: [NSLayoutConstraint] = []
|
|
@@ -99,6 +99,9 @@ final class NativeListMarketCell: NativeListRendererCell {
|
|
|
99
99
|
_ item: NativeListItem, theme: [String: Any]?, layout: String,
|
|
100
100
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String
|
|
101
101
|
) {
|
|
102
|
+
let iconUpdate = NativeListIconUpdate(self)
|
|
103
|
+
defer { iconUpdate.finish() }
|
|
104
|
+
|
|
102
105
|
NSLayoutConstraint.deactivate(selectorConstraints + accessorySizeConstraints)
|
|
103
106
|
selectorConstraints = []
|
|
104
107
|
accessorySizeConstraints = []
|
|
@@ -166,6 +169,7 @@ final class NativeListMarketCell: NativeListRendererCell {
|
|
|
166
169
|
? .top
|
|
167
170
|
: style?.dictionary("container")?.string("contentVerticalAlignment") == "bottom"
|
|
168
171
|
? .bottom : .center
|
|
172
|
+
|
|
169
173
|
}
|
|
170
174
|
private func resetButton(_ button: NativeListAccessoryButton) {
|
|
171
175
|
button.setTitle(nil, for: .normal)
|
|
@@ -498,13 +502,7 @@ final class NativeListMarketCell: NativeListRendererCell {
|
|
|
498
502
|
fallback: .darkGray
|
|
499
503
|
)
|
|
500
504
|
leadingActionButton.tintColor = tintColor
|
|
501
|
-
|
|
502
|
-
leadingActionButton.setImage(image, for: .normal)
|
|
503
|
-
leadingActionButton.setImage(
|
|
504
|
-
image.withTintColor(tintColor, renderingMode: .alwaysOriginal),
|
|
505
|
-
for: .disabled
|
|
506
|
-
)
|
|
507
|
-
}
|
|
505
|
+
leadingActionButton.bindIcon(leadingAction.string("name"))
|
|
508
506
|
leadingActionButton.isEnabled = !leadingAction.bool("disabled")
|
|
509
507
|
leadingActionButton.alpha = leadingActionButton.isEnabled ? 1 : 0.4
|
|
510
508
|
leadingActionButton.accessibilityIdentifier = leadingAction["testID"] as? String
|
|
@@ -627,11 +625,7 @@ final class NativeListMarketCell: NativeListRendererCell {
|
|
|
627
625
|
button.titleEdgeInsets = .zero
|
|
628
626
|
button.imageView?.contentMode = .scaleAspectFit
|
|
629
627
|
if hasBuiltInIcon {
|
|
630
|
-
button.
|
|
631
|
-
nativeListIcon(
|
|
632
|
-
named: "BadgeVerifiedSolid", size: CGSize(width: iconSize, height: iconSize)),
|
|
633
|
-
for: .normal
|
|
634
|
-
)
|
|
628
|
+
button.bindIcon("BadgeVerifiedSolid", size: CGSize(width: iconSize, height: iconSize))
|
|
635
629
|
}
|
|
636
630
|
// OneKey patch: match source badge metrics at physical-pixel precision.
|
|
637
631
|
// let height = button.heightAnchor.constraint(equalToConstant: 18)
|
|
@@ -11,7 +11,7 @@ final class NativeListMediaTileCell: NativeListRendererCell {
|
|
|
11
11
|
private let picture = UIView()
|
|
12
12
|
private let image = NativeListMediaPreviewSlot()
|
|
13
13
|
private let network = NativeListImageSlot()
|
|
14
|
-
private let errorIcon =
|
|
14
|
+
private let errorIcon = NativeListIconImageView()
|
|
15
15
|
private let title = NativeListTextLabel()
|
|
16
16
|
private let subtitle = NativeListTextLabel()
|
|
17
17
|
private let badge = NativeListTextLabel()
|
|
@@ -87,6 +87,9 @@ final class NativeListMediaTileCell: NativeListRendererCell {
|
|
|
87
87
|
_ item: NativeListItem, theme: [String: Any]?, layout: String,
|
|
88
88
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String
|
|
89
89
|
) {
|
|
90
|
+
let iconUpdate = NativeListIconUpdate(self)
|
|
91
|
+
defer { iconUpdate.finish() }
|
|
92
|
+
|
|
90
93
|
let style = item.data.dictionary("style") ?? [:]
|
|
91
94
|
imageStyle = style.dictionary("image") ?? [:]
|
|
92
95
|
let hp = CGFloat(style.double("horizontalPadding", default: 10))
|
|
@@ -122,7 +125,7 @@ final class NativeListMediaTileCell: NativeListRendererCell {
|
|
|
122
125
|
let state = item.data.string("imageState")
|
|
123
126
|
let placeholder = theme?.string("strongBackground", default: "#0000000F") ?? "#0000000F"
|
|
124
127
|
errorIcon.isHidden = state != "error"
|
|
125
|
-
errorIcon.
|
|
128
|
+
errorIcon.bindIcon("ImageSquareWavesOutline")
|
|
126
129
|
errorIcon.tintColor = UIColor(nativeListHex: "#00000044", fallback: .lightGray)
|
|
127
130
|
let media = item.data.dictionary("media")
|
|
128
131
|
if state != "empty", state != "error", let source = media?.dictionary("source") ?? item.data.dictionary("image") {
|
|
@@ -164,6 +167,7 @@ final class NativeListMediaTileCell: NativeListRendererCell {
|
|
|
164
167
|
close.setTitle("×", for: .normal)
|
|
165
168
|
close.titleLabel?.font = nativeListFont(ofSize: 16, weight: .medium, fonts: fonts)
|
|
166
169
|
close.setTitleColor(nativeListColor(theme, "primaryText", "#202020"), for: .normal)
|
|
170
|
+
|
|
167
171
|
}
|
|
168
172
|
override func layoutSubviews() {
|
|
169
173
|
let width = CGFloat(
|
|
@@ -374,7 +374,7 @@ final class NativeListMetricCardCell: NativeListRendererCell {
|
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
private final class NativeListMetricVisual: UIView {
|
|
377
|
-
private let icon =
|
|
377
|
+
private let icon = NativeListIconImageView()
|
|
378
378
|
private let image = NativeListImageSlot()
|
|
379
379
|
init() {
|
|
380
380
|
super.init(frame: .zero)
|
|
@@ -389,6 +389,8 @@ private final class NativeListMetricVisual: UIView {
|
|
|
389
389
|
}
|
|
390
390
|
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
|
391
391
|
func bind(_ visual: [String: Any], key: String, placeholder: String) {
|
|
392
|
+
let iconUpdate = NativeListIconUpdate(self)
|
|
393
|
+
defer { iconUpdate.finish() }
|
|
392
394
|
backgroundColor = UIColor(
|
|
393
395
|
nativeListHex: visual.string("backgroundColor", default: "#0000000F"), fallback: .lightGray)
|
|
394
396
|
layer.cornerRadius = visual.string("shape") == "square" ? 0 : 8
|
|
@@ -397,7 +399,7 @@ private final class NativeListMetricVisual: UIView {
|
|
|
397
399
|
image.view.isHidden = true
|
|
398
400
|
if kind == "icon" {
|
|
399
401
|
image.recycle()
|
|
400
|
-
icon.
|
|
402
|
+
icon.bindIcon(visual.string("name"))
|
|
401
403
|
icon.tintColor = UIColor(
|
|
402
404
|
nativeListHex: visual.string("tintColor", default: "#00000072"), fallback: .darkGray)
|
|
403
405
|
} else if let source = kind == "stackedImages"
|
|
@@ -124,8 +124,8 @@ final class NativeListSectionHeaderCell: NativeListRendererCell {
|
|
|
124
124
|
private let trailingStack = UIStackView()
|
|
125
125
|
private let accessoryButtons = [NativeListAccessoryButton(type: .system)]
|
|
126
126
|
private let checkboxControl = NativeListAccessoryStack(frame: .zero)
|
|
127
|
-
private let headerTitleIconImageView =
|
|
128
|
-
private let headerValueIconImageView =
|
|
127
|
+
private let headerTitleIconImageView = NativeListIconImageView()
|
|
128
|
+
private let headerValueIconImageView = NativeListIconImageView()
|
|
129
129
|
private var accessoryActions: [(String, NativeSelectionTarget?)] = []
|
|
130
130
|
private var currentItem: NativeListItem?
|
|
131
131
|
private var currentTheme: [String: Any]?
|
|
@@ -187,6 +187,9 @@ final class NativeListSectionHeaderCell: NativeListRendererCell {
|
|
|
187
187
|
_ item: NativeListItem, theme: [String: Any]?, layout: String,
|
|
188
188
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String
|
|
189
189
|
) {
|
|
190
|
+
let iconUpdate = NativeListIconUpdate(self)
|
|
191
|
+
defer { iconUpdate.finish() }
|
|
192
|
+
|
|
190
193
|
selectorTypographyRestorers.reversed().forEach { $0() }
|
|
191
194
|
selectorTypographyRestorers = []
|
|
192
195
|
currentItem = item
|
|
@@ -297,6 +300,7 @@ final class NativeListSectionHeaderCell: NativeListRendererCell {
|
|
|
297
300
|
width.isActive = true
|
|
298
301
|
selectorConstraints.append(width)
|
|
299
302
|
}
|
|
303
|
+
|
|
300
304
|
}
|
|
301
305
|
private func bindPositionHeaderFields(_ item: NativeListItem, theme: [String: Any]?) {
|
|
302
306
|
let style = item.data.dictionary("style") ?? [:]
|
|
@@ -371,6 +375,9 @@ final class NativeListSectionHeaderCell: NativeListRendererCell {
|
|
|
371
375
|
_ item: NativeListItem,
|
|
372
376
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String
|
|
373
377
|
) {
|
|
378
|
+
let iconUpdate = NativeListIconUpdate(self)
|
|
379
|
+
defer { iconUpdate.finish() }
|
|
380
|
+
|
|
374
381
|
currentItem = item
|
|
375
382
|
checkboxControl.updateSelection(item, checkboxState: checkboxState)
|
|
376
383
|
if item.data.string("variant") == "summary" {
|
|
@@ -382,6 +389,7 @@ final class NativeListSectionHeaderCell: NativeListRendererCell {
|
|
|
382
389
|
applyTextStyle(item)
|
|
383
390
|
applySelectorTypography(item)
|
|
384
391
|
}
|
|
392
|
+
|
|
385
393
|
}
|
|
386
394
|
private func bindCheckbox(
|
|
387
395
|
_ item: NativeListItem, _ descriptor: [String: Any],
|
|
@@ -523,7 +531,7 @@ final class NativeListSectionHeaderCell: NativeListRendererCell {
|
|
|
523
531
|
topInset = 12
|
|
524
532
|
bottomInset = -2
|
|
525
533
|
if let icon = item.data.dictionary("titleIcon") {
|
|
526
|
-
headerTitleIconImageView.
|
|
534
|
+
headerTitleIconImageView.bindIcon(icon.string("name"))
|
|
527
535
|
headerTitleIconImageView.tintColor = UIColor(
|
|
528
536
|
nativeListHex: icon.string("tintColor", default: "#646464"),
|
|
529
537
|
fallback: .darkGray
|
|
@@ -593,7 +601,7 @@ final class NativeListSectionHeaderCell: NativeListRendererCell {
|
|
|
593
601
|
for: .normal
|
|
594
602
|
)
|
|
595
603
|
if let icon = item.data.dictionary("valueIcon") {
|
|
596
|
-
headerValueIconImageView.
|
|
604
|
+
headerValueIconImageView.bindIcon(icon.string("name"))
|
|
597
605
|
headerValueIconImageView.tintColor = UIColor(
|
|
598
606
|
nativeListHex: icon.string("tintColor", default: "#646464"),
|
|
599
607
|
fallback: .darkGray
|
|
@@ -42,6 +42,18 @@ final class NativeListAccessoryStackTests: XCTestCase {
|
|
|
42
42
|
XCTAssertTrue(stack.hitTest(value.center, with: nil) === stack)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
func testSwapMenuUsesTheDotVerSolidImage() throws {
|
|
46
|
+
let metadata = try XCTUnwrap(nativeListIconMetadata(named: "DotVerSolid"))
|
|
47
|
+
XCTAssertGreaterThan(metadata.size.width, 0)
|
|
48
|
+
XCTAssertGreaterThan(metadata.size.height, 0)
|
|
49
|
+
let stack = try makeStack(trailing: [[
|
|
50
|
+
"kind": "icon", "name": "DotVerSolid", "buttonStyle": "tertiary",
|
|
51
|
+
"actionKey": "menu",
|
|
52
|
+
]])
|
|
53
|
+
let button = try XCTUnwrap(try visibleControl(stack) as? UIButton)
|
|
54
|
+
XCTAssertNotNil(button.image(for: .normal))
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
func testValuePairAccessoryLeavesTouchesToTheRow() throws {
|
|
46
58
|
let stack = try makeStack(trailing: [
|
|
47
59
|
["kind": "valuePair", "primary": "$15.60", "secondary": "0.002 BTC"],
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
import XCTest
|
|
3
|
+
import OneKeyImage
|
|
4
|
+
@testable import NativeListModule
|
|
5
|
+
|
|
6
|
+
@MainActor
|
|
7
|
+
final class NativeListSharedIconTests: XCTestCase {
|
|
8
|
+
private func field<T>(_ object: Any, _ name: String, as type: T.Type = T.self) throws -> T {
|
|
9
|
+
var mirror: Mirror? = Mirror(reflecting: object)
|
|
10
|
+
while let current = mirror {
|
|
11
|
+
if let child = current.children.first(where: { $0.label == name }) { return try XCTUnwrap(child.value as? T) }
|
|
12
|
+
mirror = current.superclassMirror
|
|
13
|
+
}
|
|
14
|
+
throw NSError(domain: "Missing field " + name, code: 1)
|
|
15
|
+
}
|
|
16
|
+
private func binding(_ button: UIButton) throws -> Any {
|
|
17
|
+
let mirror = try XCTUnwrap(Mirror(reflecting: button).superclassMirror)
|
|
18
|
+
let storage = try XCTUnwrap(mirror.children.first { $0.label?.contains("iconBinding") == true }?.value)
|
|
19
|
+
return try XCTUnwrap(Mirror(reflecting: storage).children.first?.value)
|
|
20
|
+
}
|
|
21
|
+
private func eventually(_ message: String, _ condition: () -> Bool) {
|
|
22
|
+
let end = Date().addingTimeInterval(5)
|
|
23
|
+
while !condition(), Date() < end { RunLoop.main.run(until: Date().addingTimeInterval(0.01)) }
|
|
24
|
+
XCTAssertTrue(condition(), message)
|
|
25
|
+
}
|
|
26
|
+
private func window() throws -> UIWindow {
|
|
27
|
+
guard UIApplication.perform(NSSelectorFromString("sharedApplication"))?.takeUnretainedValue() is UIApplication else {
|
|
28
|
+
throw XCTSkip("A UIApplication host is required for attached glyph acceptance")
|
|
29
|
+
}
|
|
30
|
+
let window = UIWindow(frame: UIScreen.main.bounds)
|
|
31
|
+
window.rootViewController = UIViewController()
|
|
32
|
+
window.makeKeyAndVisible()
|
|
33
|
+
return window
|
|
34
|
+
}
|
|
35
|
+
private func save(_ image: UIImage, _ name: String) throws {
|
|
36
|
+
guard let directory = ProcessInfo.processInfo.environment["ICON_EVIDENCE_DIR"] else { return }
|
|
37
|
+
try XCTUnwrap(image.pngData()).write(to: URL(fileURLWithPath: directory).appendingPathComponent(name + ".png"))
|
|
38
|
+
}
|
|
39
|
+
private func bind(_ stack: NativeListAccessoryStack, amount: String = "1", name: String = "DotVerSolid", kind: String = "icon", disabled: Bool = false) throws {
|
|
40
|
+
let descriptors: [[String: Any]] = [
|
|
41
|
+
["kind": "valuePair", "primary": amount, "secondary": "$1"],
|
|
42
|
+
["kind": kind, "name": name, "text": "Text", "buttonStyle": "tertiary", "actionKey": "menu", "disabled": disabled],
|
|
43
|
+
]
|
|
44
|
+
let item = try NativeListItem(data: ["key": "swap", "type": "identity", "title": "USDC", "height": 60, "trailing": descriptors])
|
|
45
|
+
stack.axis = .horizontal
|
|
46
|
+
stack.bind(item, descriptors: descriptors, theme: nil,
|
|
47
|
+
style: ["horizontalPadding": 20, "verticalPadding": 8, "leadingGap": 12, "trailingGap": 8],
|
|
48
|
+
checkboxState: { _, _, fallback in fallback }, fonts: NativeListFontFamilies())
|
|
49
|
+
stack.setNeedsLayout(); stack.layoutIfNeeded()
|
|
50
|
+
}
|
|
51
|
+
func testDetachedMeasurementAndRealSwapRebindPreservePendingAndReadyImage() throws {
|
|
52
|
+
let service = OneKeyNamedSVG.shared
|
|
53
|
+
let metadataLock: NSLock = try field(service, "metadataLock")
|
|
54
|
+
let stateLock: NSLock = try field(service, "lock")
|
|
55
|
+
func state(_ owner: NSObject? = nil) throws -> (Int, Int) {
|
|
56
|
+
stateLock.lock(); defer { stateLock.unlock() }
|
|
57
|
+
let pending: Any = try field(service, "pending")
|
|
58
|
+
let keys = Mirror(reflecting: pending).children.compactMap { Mirror(reflecting: $0.value).children.first?.value as? ObjectIdentifier }
|
|
59
|
+
return (try field(service, "workers"), owner.map { owner in keys.filter { $0 == ObjectIdentifier(owner) }.count } ?? keys.count)
|
|
60
|
+
}
|
|
61
|
+
metadataLock.lock()
|
|
62
|
+
var locked = true
|
|
63
|
+
defer { if locked { metadataLock.unlock() } }
|
|
64
|
+
let baseline = try state()
|
|
65
|
+
let stack = NativeListAccessoryStack(frame: CGRect(x: 10, y: 80, width: 180, height: 44))
|
|
66
|
+
try bind(stack)
|
|
67
|
+
let button = try XCTUnwrap(stack.subviews.compactMap { $0 as? NativeListAccessoryButton }.first { !$0.isHidden && $0.image(for: .normal) != nil })
|
|
68
|
+
let slot = try binding(button)
|
|
69
|
+
let owner: NSObject = try field(slot, "owner")
|
|
70
|
+
XCTAssertEqual(try state(owner).0, baseline.0)
|
|
71
|
+
XCTAssertEqual(try state(owner).1, 0)
|
|
72
|
+
let beforeSize = try XCTUnwrap(button.image(for: .normal)).size
|
|
73
|
+
let detachedSize = stack.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
|
|
74
|
+
XCTAssertEqual(try state(owner).0, baseline.0)
|
|
75
|
+
XCTAssertEqual(try state(owner).1, 0)
|
|
76
|
+
let window = try window(); defer { window.isHidden = true }
|
|
77
|
+
do {
|
|
78
|
+
defer { metadataLock.unlock(); locked = false }
|
|
79
|
+
window.rootViewController!.view.addSubview(stack)
|
|
80
|
+
stack.layoutIfNeeded()
|
|
81
|
+
XCTAssertGreaterThan(try state(owner).0 + state(owner).1, baseline.0)
|
|
82
|
+
let bounds = button.bounds.size
|
|
83
|
+
let revision: UInt64 = try field(slot, "revision")
|
|
84
|
+
for index in 0..<100 { try bind(stack, amount: String(index)) }
|
|
85
|
+
XCTAssertEqual(try field(slot, "revision", as: UInt64.self), revision)
|
|
86
|
+
XCTAssertEqual(button.image(for: .normal)?.size, beforeSize)
|
|
87
|
+
XCTAssertEqual(button.bounds.size, bounds)
|
|
88
|
+
XCTAssertEqual(stack.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height, detachedSize.height)
|
|
89
|
+
}
|
|
90
|
+
eventually("Attached Swap glyph has an actual raster") { button.image(for: .normal)?.cgImage != nil && button.image(for: .normal)?.size == beforeSize && (try? self.field(slot, "currentImage", as: UIImage?.self)) != nil }
|
|
91
|
+
// A transparent measurement placeholder also has a CGImage: require painted pixels.
|
|
92
|
+
eventually("Decoded glyph is visible") { self.alphaCount(button.image(for: .normal)) > 0 }
|
|
93
|
+
let ready = try XCTUnwrap(button.image(for: .normal))
|
|
94
|
+
XCTAssertEqual(maximumAlpha(ready), 255)
|
|
95
|
+
try bind(stack, amount: "101")
|
|
96
|
+
XCTAssertTrue(button.image(for: .normal) === ready)
|
|
97
|
+
button.tintColor = .blue
|
|
98
|
+
XCTAssertTrue(button.image(for: .normal) === ready)
|
|
99
|
+
XCTAssertNotNil(button.image(for: .disabled))
|
|
100
|
+
try bind(stack, amount: "101")
|
|
101
|
+
func captureButton() -> UIImage {
|
|
102
|
+
let rect = button.convert(button.bounds, to: stack)
|
|
103
|
+
return UIGraphicsImageRenderer(size: button.bounds.size).image { context in
|
|
104
|
+
context.cgContext.translateBy(x: -rect.minX, y: -rect.minY)
|
|
105
|
+
stack.drawHierarchy(in: stack.bounds, afterScreenUpdates: true)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
try save(captureButton(), "ios-swap-menu-normal")
|
|
109
|
+
var actions = 0
|
|
110
|
+
stack.onAction = { _, _, _, _ in actions += 1 }
|
|
111
|
+
try bind(stack, amount: "101", disabled: true)
|
|
112
|
+
XCTAssertFalse(button.isEnabled); XCTAssertEqual(button.alpha, 0.4, accuracy: 0.000001)
|
|
113
|
+
XCTAssertTrue(button.image(for: .normal) === ready)
|
|
114
|
+
button.sendActions(for: .touchUpInside); XCTAssertEqual(actions, 0)
|
|
115
|
+
button.layoutIfNeeded()
|
|
116
|
+
try save(captureButton(), "ios-swap-menu-disabled")
|
|
117
|
+
try bind(stack, amount: "101")
|
|
118
|
+
try bind(stack, name: "UnknownIcon")
|
|
119
|
+
XCTAssertNil(button.image(for: .normal))
|
|
120
|
+
try bind(stack, kind: "value")
|
|
121
|
+
XCTAssertNil(button.image(for: .normal))
|
|
122
|
+
stack.removeFromSuperview()
|
|
123
|
+
RunLoop.main.run(until: Date().addingTimeInterval(0.05))
|
|
124
|
+
XCTAssertNil(button.image(for: .normal))
|
|
125
|
+
}
|
|
126
|
+
private func alphaCount(_ image: UIImage?) -> Int {
|
|
127
|
+
guard let cg = image?.cgImage else { return 0 }
|
|
128
|
+
var bytes = [UInt8](repeating: 0, count: cg.width * cg.height * 4)
|
|
129
|
+
guard let context = CGContext(data: &bytes, width: cg.width, height: cg.height, bitsPerComponent: 8,
|
|
130
|
+
bytesPerRow: cg.width * 4, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else { return 0 }
|
|
131
|
+
context.draw(cg, in: CGRect(x: 0, y: 0, width: cg.width, height: cg.height))
|
|
132
|
+
return stride(from: 3, to: bytes.count, by: 4).filter { bytes[$0] > 0 }.count
|
|
133
|
+
}
|
|
134
|
+
func testMainLineGlyphsPaintInTheSharedNativeSlot() throws {
|
|
135
|
+
let window = try window(); defer { window.isHidden = true }
|
|
136
|
+
let view = NativeListIconImageView(frame: CGRect(x: 10, y: 80, width: 64, height: 64))
|
|
137
|
+
window.rootViewController!.view.addSubview(view)
|
|
138
|
+
for name in ["Document2Outline", "WalletOutline", "PlusLargeOutline", "TicketOutline"] {
|
|
139
|
+
let metadata = try XCTUnwrap(nativeListIconMetadata(named: name))
|
|
140
|
+
XCTAssertEqual(metadata.name, name)
|
|
141
|
+
XCTAssertEqual(metadata.size, CGSize(width: 24, height: 24))
|
|
142
|
+
view.bindIcon(name)
|
|
143
|
+
eventually("Shared main-line glyph paints: " + name) { self.alphaCount(view.image) > 0 }
|
|
144
|
+
let image = try XCTUnwrap(view.image)
|
|
145
|
+
XCTAssertEqual(image.renderingMode, .alwaysTemplate)
|
|
146
|
+
try save(image.withTintColor(.red, renderingMode: .alwaysOriginal), "ios-main-integration-" + name)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
private func maximumAlpha(_ image: UIImage) -> UInt8 {
|
|
150
|
+
guard let cg = image.cgImage else { return 0 }
|
|
151
|
+
var bytes = [UInt8](repeating: 0, count: cg.width * cg.height * 4)
|
|
152
|
+
guard let context = CGContext(data: &bytes, width: cg.width, height: cg.height, bitsPerComponent: 8,
|
|
153
|
+
bytesPerRow: cg.width * 4, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else { return 0 }
|
|
154
|
+
context.draw(cg, in: CGRect(x: 0, y: 0, width: cg.width, height: cg.height))
|
|
155
|
+
return stride(from: 3, to: bytes.count, by: 4).map { bytes[$0] }.max() ?? 0
|
|
156
|
+
}
|
|
157
|
+
func testLifecycleLegacyModesAndSizeOverride() throws {
|
|
158
|
+
let window = try window(); defer { window.isHidden = true }
|
|
159
|
+
let view = NativeListIconImageView(frame: CGRect(x: 10, y: 80, width: 64, height: 64))
|
|
160
|
+
window.rootViewController!.view.addSubview(view)
|
|
161
|
+
for name in ["AppleBrand", "BotIllus", "GoogleIllus", "AccountErrorCustom", "StarOutline", "StarSolid", "BadgeVerifiedSolid", "CheckboxCheckedCustom", "CheckboxIndeterminateCustom"] {
|
|
162
|
+
view.bindIcon(name)
|
|
163
|
+
eventually(name) { self.alphaCount(view.image) > 0 }
|
|
164
|
+
let metadata = try XCTUnwrap(nativeListIconMetadata(named: name))
|
|
165
|
+
XCTAssertEqual(view.image?.size, metadata.size)
|
|
166
|
+
if !metadata.original { XCTAssertEqual(maximumAlpha(try XCTUnwrap(view.image)), 255) }
|
|
167
|
+
XCTAssertEqual(view.image?.renderingMode, metadata.original ? .alwaysOriginal : .alwaysTemplate)
|
|
168
|
+
let image = try XCTUnwrap(view.image)
|
|
169
|
+
try save(metadata.original ? image : image.withTintColor(.red, renderingMode: .alwaysOriginal), "ios-new-" + name)
|
|
170
|
+
}
|
|
171
|
+
view.bindIcon("AppleBrand", size: CGSize(width: 28, height: 20))
|
|
172
|
+
eventually("Non-square source stretches to legacy explicit size") { self.alphaCount(view.image) > 0 }
|
|
173
|
+
XCTAssertEqual(view.image?.size, CGSize(width: 28, height: 20))
|
|
174
|
+
XCTAssertEqual(view.image?.renderingMode, .alwaysTemplate)
|
|
175
|
+
try save(try XCTUnwrap(view.image).withTintColor(.red, renderingMode: .alwaysOriginal), "ios-new-AppleBrand-override")
|
|
176
|
+
view.bindIcon("DotVerSolid")
|
|
177
|
+
view.removeFromSuperview()
|
|
178
|
+
RunLoop.main.run(until: Date().addingTimeInterval(0.1))
|
|
179
|
+
XCTAssertEqual(alphaCount(view.image), 0)
|
|
180
|
+
window.rootViewController!.view.addSubview(view)
|
|
181
|
+
eventually("Same glyph redraws after detached completion") { self.alphaCount(view.image) > 0 }
|
|
182
|
+
view.bindIcon("UnknownIcon"); XCTAssertNil(view.image)
|
|
183
|
+
let ordinary = UIGraphicsImageRenderer(size: CGSize(width: 8, height: 8)).image { c in UIColor.green.setFill(); c.fill(CGRect(x: 0, y: 0, width: 8, height: 8)) }
|
|
184
|
+
view.image = ordinary
|
|
185
|
+
XCTAssertTrue(view.image === ordinary)
|
|
186
|
+
}
|
|
187
|
+
func testLegacyCompiledAssetRasterComparison() throws {
|
|
188
|
+
guard let directory = ProcessInfo.processInfo.environment["NATIVE_LIST_LEGACY_BUNDLE"], let bundle = Bundle(path: directory) else {
|
|
189
|
+
throw XCTSkip("An archived pre-migration resource bundle is required for raster comparison")
|
|
190
|
+
}
|
|
191
|
+
let window = try window(); defer { window.isHidden = true }
|
|
192
|
+
let view = NativeListIconImageView(frame: CGRect(x: 0, y: 80, width: 96, height: 96))
|
|
193
|
+
window.rootViewController!.view.addSubview(view)
|
|
194
|
+
let names = [
|
|
195
|
+
"AppleBrand": "onekey_selector_apple_brand", "BotIllus": "onekey_selector_bot_illus",
|
|
196
|
+
"GoogleIllus": "onekey_selector_google_illus", "AccountErrorCustom": "onekey_selector_account_error_custom",
|
|
197
|
+
"BadgeVerifiedSolid": "onekey_badge_verified_solid", "StarOutline": "onekey_star", "StarSolid": "onekey_star_solid",
|
|
198
|
+
"CheckboxCheckedCustom": "onekey_checkbox_checked", "CheckboxIndeterminateCustom": "onekey_checkbox_indeterminate",
|
|
199
|
+
]
|
|
200
|
+
func raster(_ image: UIImage) -> UIImage {
|
|
201
|
+
let format = UIGraphicsImageRendererFormat(); format.scale = 1
|
|
202
|
+
return UIGraphicsImageRenderer(size: CGSize(width: 96, height: 96), format: format).image { _ in
|
|
203
|
+
image.draw(in: CGRect(x: 0, y: 0, width: 96, height: 96))
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
for name in names.keys.sorted() {
|
|
207
|
+
let metadata = try XCTUnwrap(nativeListIconMetadata(named: name))
|
|
208
|
+
let old = try XCTUnwrap(UIImage(named: names[name]!, in: bundle, compatibleWith: nil))
|
|
209
|
+
view.bindIcon(name)
|
|
210
|
+
eventually("Shared raster for legacy comparison " + name) { self.alphaCount(view.image) > 0 }
|
|
211
|
+
let next = try XCTUnwrap(view.image)
|
|
212
|
+
XCTAssertEqual(next.size, old.size)
|
|
213
|
+
let before = raster(metadata.original ? old : old.withTintColor(.red, renderingMode: .alwaysOriginal))
|
|
214
|
+
let after = raster(metadata.original ? next : next.withTintColor(.red, renderingMode: .alwaysOriginal))
|
|
215
|
+
XCTAssertGreaterThan(alphaCount(before), 0); XCTAssertGreaterThan(alphaCount(after), 0)
|
|
216
|
+
if name == "AccountErrorCustom" {
|
|
217
|
+
// The source eye center is inside its 0.447-opacity fill, away from raster edges.
|
|
218
|
+
for image in [before, after] {
|
|
219
|
+
let cg = try XCTUnwrap(image.cgImage)
|
|
220
|
+
var bytes = [UInt8](repeating: 0, count: 96 * 96 * 4)
|
|
221
|
+
let context = try XCTUnwrap(CGContext(data: &bytes, width: 96, height: 96, bitsPerComponent: 8,
|
|
222
|
+
bytesPerRow: 96 * 4, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue))
|
|
223
|
+
context.draw(cg, in: CGRect(x: 0, y: 0, width: 96, height: 96))
|
|
224
|
+
for (x,y) in [(6,48),(48,6),(48,91),(91,64),(67,61)] {
|
|
225
|
+
let index = (y * 96 + x) * 4
|
|
226
|
+
XCTAssertEqual(Array(bytes[index..<(index+3)]), [0,0,0])
|
|
227
|
+
XCTAssertEqual(bytes[index+3], 114)
|
|
228
|
+
}
|
|
229
|
+
for (x,y) in [(48,48),(15,48)] { XCTAssertEqual(bytes[(y * 96 + x) * 4 + 3], 0) }
|
|
230
|
+
}
|
|
231
|
+
} else { XCTAssertEqual(maximumAlpha(after), maximumAlpha(before)) }
|
|
232
|
+
try save(before, "ios-before-" + name); try save(after, "ios-after-" + name)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Generated by scripts/generate-icons.mjs. Coverage and legacy names only; hosts provide SVG resources.
|
|
4
|
+
export const nativeListGeneratedIcons = {
|
|
5
|
+
AccountErrorCustom: 'AccountErrorCustom',
|
|
6
|
+
AllNetworksSolid: 'AllNetworksSolid',
|
|
7
|
+
AppleBrand: 'AppleBrand',
|
|
8
|
+
BadgeVerifiedSolid: 'BadgeRecognizedSolid',
|
|
9
|
+
BotIllus: 'BotIllus',
|
|
10
|
+
CheckboxCheckedCustom: 'CheckboxCheckedCustom',
|
|
11
|
+
CheckboxIndeterminateCustom: 'CheckboxIndeterminateCustom',
|
|
12
|
+
ChevronRightSmallOutline: 'ChevronRightSmallOutline',
|
|
13
|
+
Circle: 'Circle',
|
|
14
|
+
CrossedSmallSolid: 'CrossedSmallSolid',
|
|
15
|
+
CryptoCoinOutline: 'CryptoCoinOutline',
|
|
16
|
+
Document2Outline: 'Document2Outline',
|
|
17
|
+
DotHorOutline: 'DotHorOutline',
|
|
18
|
+
DotVerSolid: 'DotVerSolid',
|
|
19
|
+
DragOutline: 'DragOutline',
|
|
20
|
+
GasSolid: 'GasSolid',
|
|
21
|
+
GlobusOutline: 'GlobusOutline',
|
|
22
|
+
GoogleIllus: 'GoogleIllus',
|
|
23
|
+
LockSolid: 'LockSolid',
|
|
24
|
+
PencilOutline: 'PencilOutline',
|
|
25
|
+
PlusLargeOutline: 'PlusLargeOutline',
|
|
26
|
+
PlusSmallOutline: 'PlusSmallOutline',
|
|
27
|
+
RefreshCcwOutline: 'RefreshCcwOutline',
|
|
28
|
+
TicketOutline: 'TicketOutline',
|
|
29
|
+
WalletOutline: 'WalletOutline'
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=NativeListGeneratedIcons.js.map
|