@onekeyfe/react-native-native-list 3.0.112 → 3.0.113
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/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 +26 -2
- package/ios/NativeListCell.swift +504 -20
- package/ios/NativeListDesignAssets.swift +8 -0
- package/ios/RNCNativeListView.swift +85 -7
- 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 +357 -5
- package/lib/typescript/src/models.d.ts +82 -2
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +26 -1
- package/package.json +2 -2
- package/src/models.ts +121 -3
- package/src/validation.ts +206 -0
- package/src/web/NativeListWebEngine.ts +578 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActionAnchorInvalidatedEvent, NativeListSnapshot, NativeListActionAnchor, NativeListActionSource, ReorderEvent, RowActionEvent, RowModel, RowPatch, SelectionDeltaEvent, VisibleRangeChangedEvent } from '../models';
|
|
1
|
+
import type { ActionAnchorInvalidatedEvent, MarketRow, NativeListSnapshot, NativeListActionAnchor, NativeListActionSource, ReorderEvent, RowActionEvent, RowModel, RowPatch, SelectionDeltaEvent, VisibleRangeChangedEvent } from '../models';
|
|
2
2
|
import type { ActionAnchorState, ScrollToIndexFailedInfo, ScrollToLocationParams } from '../NativeList.types';
|
|
3
3
|
import { type NormalizedPositionScroll } from '../scrolling';
|
|
4
4
|
export declare const WEB_REORDER_ANIMATION: {
|
|
@@ -7,6 +7,7 @@ export declare const WEB_REORDER_ANIMATION: {
|
|
|
7
7
|
readonly dropDurationMs: 80;
|
|
8
8
|
readonly dropTimingFunction: "ease";
|
|
9
9
|
};
|
|
10
|
+
export declare const WEB_MARKET_VERIFIED_PATH = "M9.483 11.458v3.5h-1v-3.5z M10.467 2.698a2.03 2.03 0 0 1 3.065 0l1.358 1.564a.03.03 0 0 0 .028.01l2.046-.325a2.03 2.03 0 0 1 2.347 1.971l.037 2.07q0 .016.014.026l1.776 1.066a2.03 2.03 0 0 1 .532 3.019l-1.304 1.609a.03.03 0 0 0-.005.03l.675 1.956a2.03 2.03 0 0 1-1.533 2.656l-2.033.394a.03.03 0 0 0-.023.019l-.741 1.933a2.03 2.03 0 0 1-2.88 1.05l-1.811-1.006a.03.03 0 0 0-.03 0l-1.811 1.005a2.03 2.03 0 0 1-2.88-1.049l-.742-1.933a.03.03 0 0 0-.023-.019l-2.033-.394a2.03 2.03 0 0 1-1.532-2.656l.675-1.957a.03.03 0 0 0-.005-.029l-1.304-1.61a2.03 2.03 0 0 1 .532-3.018l1.776-1.066a.03.03 0 0 0 .014-.026l.035-2.07a2.03 2.03 0 0 1 2.349-1.97l2.045.324a.03.03 0 0 0 .028-.01zm1.516 3.76a.5.5 0 0 0-.447.276l-1.861 3.724H8.483a1 1 0 0 0-1 1v3.5a1 1 0 0 0 1 1h6.692a2 2 0 0 0 1.981-1.73l.341-2.5a2 2 0 0 0-1.982-2.27h-1.939l.197-1.269a1.5 1.5 0 0 0-1.481-1.731z";
|
|
10
11
|
export type WebLayoutItem = Readonly<{
|
|
11
12
|
index: number;
|
|
12
13
|
key: string;
|
|
@@ -21,6 +22,21 @@ export type WebListLayout = Readonly<{
|
|
|
21
22
|
contentHeight: number;
|
|
22
23
|
horizontal: boolean;
|
|
23
24
|
}>;
|
|
25
|
+
export type WebMarketLayoutStyle = Readonly<{
|
|
26
|
+
horizontalPadding: number;
|
|
27
|
+
verticalPadding: number;
|
|
28
|
+
leadingGap: number;
|
|
29
|
+
titleBadgeGap: number;
|
|
30
|
+
trailingGap: number;
|
|
31
|
+
imageWidth: number;
|
|
32
|
+
imageHeight: number;
|
|
33
|
+
imageCornerRadius: number;
|
|
34
|
+
changeWidth: number;
|
|
35
|
+
changeHeight: number;
|
|
36
|
+
changeCornerRadius: number;
|
|
37
|
+
}>;
|
|
38
|
+
/** Resolves every reusable Market geometry field, including legacy defaults. */
|
|
39
|
+
export declare function resolveWebMarketLayoutStyle(row: MarketRow): WebMarketLayoutStyle;
|
|
24
40
|
export type NativeListWebCallbacks = Readonly<{
|
|
25
41
|
onRowAction?: (event: RowActionEvent) => void;
|
|
26
42
|
onActionAnchorInvalidated?: (event: ActionAnchorInvalidatedEvent) => void;
|
|
@@ -52,6 +68,9 @@ export declare function webWalletGroupReorderBadge(row: RowModel): string | unde
|
|
|
52
68
|
export declare function visibleWebLayoutItems(layout: WebListLayout, offset: number, viewportLength: number, overscan?: number): readonly WebLayoutItem[];
|
|
53
69
|
export declare function webLayoutItemsForMount(layout: WebListLayout, offset: number, viewportLength: number, virtualizationEnabled: boolean, overscan?: number): readonly WebLayoutItem[];
|
|
54
70
|
export declare function webRowRenderSignature(row: RowModel): string;
|
|
71
|
+
export declare function isWebMarketQuotePatch(patch: RowPatch): boolean;
|
|
72
|
+
/** Number of Market image-binding passes caused by one Web patch transaction. */
|
|
73
|
+
export declare function webMarketImageBindDeltaForPatches(patches: readonly RowPatch[]): 0 | 1;
|
|
55
74
|
export declare const WEB_LIST_CSS: string;
|
|
56
75
|
export declare class NativeListWebEngine {
|
|
57
76
|
private readonly document;
|
|
@@ -101,6 +120,8 @@ export declare class NativeListWebEngine {
|
|
|
101
120
|
private readonly actionAnchorInstanceId;
|
|
102
121
|
private actionAnchorCounter;
|
|
103
122
|
private bindingEpochCounter;
|
|
123
|
+
private marketPointer;
|
|
124
|
+
private marketLongPressFired;
|
|
104
125
|
private lastViewportWidth;
|
|
105
126
|
private lastViewportHeight;
|
|
106
127
|
private destroyed;
|
|
@@ -152,6 +173,10 @@ export declare class NativeListWebEngine {
|
|
|
152
173
|
private invalidateActionAnchor;
|
|
153
174
|
private emitActionAnchorInvalidated;
|
|
154
175
|
private handleRowPress;
|
|
176
|
+
private cancelMarketPointer;
|
|
177
|
+
private handleMarketPointerDown;
|
|
178
|
+
private handleMarketPointerMove;
|
|
179
|
+
private handleMarketPointerEnd;
|
|
155
180
|
private handleTitlePointerOver;
|
|
156
181
|
private handleTitlePointerOut;
|
|
157
182
|
private handleClick;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-native-list",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.113",
|
|
4
4
|
"description": "Template-driven native RecyclerView and UICollectionView for React Native",
|
|
5
5
|
"source": "./src/index.ts",
|
|
6
6
|
"main": "./lib/module/index.js",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"typescript": "^5.9.2"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
|
-
"@onekeyfe/react-native-image": "3.0.
|
|
85
|
+
"@onekeyfe/react-native-image": "3.0.113",
|
|
86
86
|
"react": "*",
|
|
87
87
|
"react-native": "*",
|
|
88
88
|
"react-native-nitro-modules": "0.37.0"
|
package/src/models.ts
CHANGED
|
@@ -33,6 +33,62 @@ export type BadgeModel = Readonly<{
|
|
|
33
33
|
tone?: 'neutral' | 'info' | 'success' | 'warning' | 'danger';
|
|
34
34
|
}>;
|
|
35
35
|
|
|
36
|
+
export type MarketTextStyle = Readonly<{
|
|
37
|
+
fontSize?: number;
|
|
38
|
+
fontWeight?: 'regular' | 'medium' | 'semibold' | 'bold';
|
|
39
|
+
color?: string;
|
|
40
|
+
lineHeight?: number;
|
|
41
|
+
lines?: 1 | 2;
|
|
42
|
+
alignment?: 'start' | 'center' | 'end';
|
|
43
|
+
}>;
|
|
44
|
+
|
|
45
|
+
export type MarketImageStyle = Readonly<{
|
|
46
|
+
width?: number;
|
|
47
|
+
height?: number;
|
|
48
|
+
shape?: 'circle' | 'rounded' | 'square';
|
|
49
|
+
cornerRadius?: number;
|
|
50
|
+
contentFit?: ImageContentFit;
|
|
51
|
+
}>;
|
|
52
|
+
|
|
53
|
+
export type MarketRowStyle = Readonly<{
|
|
54
|
+
horizontalPadding?: number;
|
|
55
|
+
verticalPadding?: number;
|
|
56
|
+
leadingGap?: number;
|
|
57
|
+
/** Space between title and subtitle; 0 by default, bounded to 0..16. */
|
|
58
|
+
lineGap?: number;
|
|
59
|
+
titleBadgeGap?: number;
|
|
60
|
+
trailingGap?: number;
|
|
61
|
+
image?: MarketImageStyle;
|
|
62
|
+
title?: MarketTextStyle;
|
|
63
|
+
subtitle?: MarketTextStyle;
|
|
64
|
+
price?: MarketTextStyle;
|
|
65
|
+
change?: MarketTextStyle;
|
|
66
|
+
changeWidth?: number;
|
|
67
|
+
changeHeight?: number;
|
|
68
|
+
changeCornerRadius?: number;
|
|
69
|
+
}>;
|
|
70
|
+
|
|
71
|
+
export type MarketBadgeModel = Readonly<{
|
|
72
|
+
key: string;
|
|
73
|
+
text?: string;
|
|
74
|
+
/** Finite native glyph set; currently only the community verified mark. */
|
|
75
|
+
iconName?: 'verified';
|
|
76
|
+
icon?: ImageSource;
|
|
77
|
+
tone?: 'neutral' | 'info' | 'success' | 'warning' | 'danger';
|
|
78
|
+
textColor?: string;
|
|
79
|
+
backgroundColor?: string;
|
|
80
|
+
actionKey?: string;
|
|
81
|
+
accessibilityLabel?: string;
|
|
82
|
+
}>;
|
|
83
|
+
|
|
84
|
+
export type MarketChangeModel = Readonly<{
|
|
85
|
+
text: string;
|
|
86
|
+
textSegments?: readonly ValueTextSegment[];
|
|
87
|
+
tone: 'positive' | 'negative' | 'neutral';
|
|
88
|
+
textColor?: string;
|
|
89
|
+
backgroundColor?: string;
|
|
90
|
+
}>;
|
|
91
|
+
|
|
36
92
|
// OneKey patch: keep selector decoration and text data serializable.
|
|
37
93
|
export type SelectorTextSegment = Readonly<{
|
|
38
94
|
text: string;
|
|
@@ -272,6 +328,26 @@ export type DataRow = RowBase &
|
|
|
272
328
|
favoriteActive?: boolean;
|
|
273
329
|
}>;
|
|
274
330
|
|
|
331
|
+
/** Native Market quote row. All values are already localized/formatted strings. */
|
|
332
|
+
export type MarketRow = RowBase &
|
|
333
|
+
Readonly<{
|
|
334
|
+
type: 'market';
|
|
335
|
+
variant: 'token' | 'stock' | 'perp';
|
|
336
|
+
leading: LeadingVisual;
|
|
337
|
+
title: string;
|
|
338
|
+
subtitle?: string;
|
|
339
|
+
subtitleSegments?: readonly ValueTextSegment[];
|
|
340
|
+
price: string;
|
|
341
|
+
priceSegments?: readonly ValueTextSegment[];
|
|
342
|
+
change: MarketChangeModel;
|
|
343
|
+
badges?: readonly MarketBadgeModel[];
|
|
344
|
+
pressActionKey?: string;
|
|
345
|
+
pressInActionKey?: string;
|
|
346
|
+
longPressActionKey?: string;
|
|
347
|
+
diagnostics?: Readonly<{ imageBindActionKey?: string }>;
|
|
348
|
+
style?: MarketRowStyle;
|
|
349
|
+
}>;
|
|
350
|
+
|
|
275
351
|
export type MediaTileRow = RowBase &
|
|
276
352
|
Readonly<{
|
|
277
353
|
type: 'mediaTile';
|
|
@@ -345,10 +421,17 @@ export type ActionRow = RowBase &
|
|
|
345
421
|
|
|
346
422
|
export type SystemRow = RowBase &
|
|
347
423
|
(
|
|
348
|
-
| Readonly<{
|
|
424
|
+
| Readonly<{
|
|
425
|
+
type: 'system';
|
|
426
|
+
variant: 'loading';
|
|
427
|
+
presentation?: 'market';
|
|
428
|
+
loadingStyle?: 'skeleton' | 'spinner';
|
|
429
|
+
message?: string;
|
|
430
|
+
}>
|
|
349
431
|
| Readonly<{
|
|
350
432
|
type: 'system';
|
|
351
433
|
variant: 'retry';
|
|
434
|
+
presentation?: 'market';
|
|
352
435
|
message: string;
|
|
353
436
|
actionKey: string;
|
|
354
437
|
}>
|
|
@@ -359,8 +442,18 @@ export type SystemRow = RowBase &
|
|
|
359
442
|
message: string;
|
|
360
443
|
borderColor?: string;
|
|
361
444
|
}>
|
|
362
|
-
| Readonly<{
|
|
363
|
-
|
|
445
|
+
| Readonly<{
|
|
446
|
+
type: 'system';
|
|
447
|
+
variant: 'noMatch';
|
|
448
|
+
presentation?: 'market';
|
|
449
|
+
message: string;
|
|
450
|
+
}>
|
|
451
|
+
| Readonly<{
|
|
452
|
+
type: 'system';
|
|
453
|
+
variant: 'end';
|
|
454
|
+
presentation?: 'market';
|
|
455
|
+
message?: string;
|
|
456
|
+
}>
|
|
364
457
|
| Readonly<{ type: 'system'; variant: 'spacer'; height: number }>
|
|
365
458
|
);
|
|
366
459
|
|
|
@@ -371,6 +464,7 @@ export type RowModel =
|
|
|
371
464
|
| ActivityRow
|
|
372
465
|
| MessageRow
|
|
373
466
|
| DataRow
|
|
467
|
+
| MarketRow
|
|
374
468
|
| MediaTileRow
|
|
375
469
|
| MetricCardRow
|
|
376
470
|
| SectionHeaderRow
|
|
@@ -545,6 +639,29 @@ export type RowPatch =
|
|
|
545
639
|
>
|
|
546
640
|
>;
|
|
547
641
|
}>
|
|
642
|
+
| Readonly<{
|
|
643
|
+
type: 'market';
|
|
644
|
+
key: string;
|
|
645
|
+
changes: Partial<
|
|
646
|
+
Pick<
|
|
647
|
+
MarketRow,
|
|
648
|
+
| CommonPatchFields
|
|
649
|
+
| 'leading'
|
|
650
|
+
| 'title'
|
|
651
|
+
| 'subtitle'
|
|
652
|
+
| 'subtitleSegments'
|
|
653
|
+
| 'price'
|
|
654
|
+
| 'priceSegments'
|
|
655
|
+
| 'change'
|
|
656
|
+
| 'badges'
|
|
657
|
+
| 'pressActionKey'
|
|
658
|
+
| 'pressInActionKey'
|
|
659
|
+
| 'longPressActionKey'
|
|
660
|
+
| 'diagnostics'
|
|
661
|
+
| 'style'
|
|
662
|
+
>
|
|
663
|
+
>;
|
|
664
|
+
}>
|
|
548
665
|
| Readonly<{
|
|
549
666
|
type: 'mediaTile';
|
|
550
667
|
key: string;
|
|
@@ -629,6 +746,7 @@ export type RowPatch =
|
|
|
629
746
|
|
|
630
747
|
export type NativeListActionSource =
|
|
631
748
|
| 'row'
|
|
749
|
+
| 'marketBadge'
|
|
632
750
|
| 'leadingAction'
|
|
633
751
|
| 'trailingAccessory'
|
|
634
752
|
| 'footerAction'
|
package/src/validation.ts
CHANGED
|
@@ -3,6 +3,9 @@ import type {
|
|
|
3
3
|
IdentityRow,
|
|
4
4
|
ImageSource,
|
|
5
5
|
LeadingVisual,
|
|
6
|
+
MarketRow,
|
|
7
|
+
MarketRowStyle,
|
|
8
|
+
MarketTextStyle,
|
|
6
9
|
NativeListSnapshot,
|
|
7
10
|
RowModel,
|
|
8
11
|
RowPatch,
|
|
@@ -19,6 +22,7 @@ const MAX_IMAGE_HEADERS = 32;
|
|
|
19
22
|
const MAX_HEADER_LENGTH = 4096;
|
|
20
23
|
const MAX_SPACER_HEIGHT = 512;
|
|
21
24
|
const MAX_SECTION_INDEX_TITLE_LENGTH = 8;
|
|
25
|
+
const MAX_MARKET_BADGES = 3;
|
|
22
26
|
|
|
23
27
|
function fail(path: string, message: string): never {
|
|
24
28
|
throw new Error(`NativeList ${path}: ${message}`);
|
|
@@ -109,6 +113,173 @@ function assertTextTone(tone: string | undefined, path: string): void {
|
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
|
|
116
|
+
function assertBoundedStyleNumber(
|
|
117
|
+
value: number | undefined,
|
|
118
|
+
path: string,
|
|
119
|
+
min: number,
|
|
120
|
+
max: number
|
|
121
|
+
): void {
|
|
122
|
+
if (
|
|
123
|
+
value !== undefined &&
|
|
124
|
+
(!Number.isFinite(value) || value < min || value > max)
|
|
125
|
+
) {
|
|
126
|
+
fail(path, `must be within ${min}...${max}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function assertMarketTextStyle(
|
|
131
|
+
style: MarketTextStyle | undefined,
|
|
132
|
+
path: string
|
|
133
|
+
): void {
|
|
134
|
+
if (!style) return;
|
|
135
|
+
assertBoundedStyleNumber(style.fontSize, `${path}.fontSize`, 8, 48);
|
|
136
|
+
assertBoundedStyleNumber(style.lineHeight, `${path}.lineHeight`, 8, 64);
|
|
137
|
+
if (
|
|
138
|
+
style.fontWeight !== undefined &&
|
|
139
|
+
!['regular', 'medium', 'semibold', 'bold'].includes(style.fontWeight)
|
|
140
|
+
) {
|
|
141
|
+
fail(`${path}.fontWeight`, 'must be regular, medium, semibold, or bold');
|
|
142
|
+
}
|
|
143
|
+
if (
|
|
144
|
+
style.alignment !== undefined &&
|
|
145
|
+
!['start', 'center', 'end'].includes(style.alignment)
|
|
146
|
+
) {
|
|
147
|
+
fail(`${path}.alignment`, 'must be start, center, or end');
|
|
148
|
+
}
|
|
149
|
+
if (style.lines !== undefined && ![1, 2].includes(style.lines)) {
|
|
150
|
+
fail(`${path}.lines`, 'must be 1 or 2');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function assertMarketStyle(
|
|
155
|
+
style: MarketRowStyle | undefined,
|
|
156
|
+
path: string
|
|
157
|
+
): void {
|
|
158
|
+
if (!style) return;
|
|
159
|
+
for (const field of [
|
|
160
|
+
'horizontalPadding',
|
|
161
|
+
'verticalPadding',
|
|
162
|
+
'leadingGap',
|
|
163
|
+
'titleBadgeGap',
|
|
164
|
+
'trailingGap',
|
|
165
|
+
] as const) {
|
|
166
|
+
assertBoundedStyleNumber(style[field], `${path}.${field}`, 0, 64);
|
|
167
|
+
}
|
|
168
|
+
assertBoundedStyleNumber(style.lineGap, `${path}.lineGap`, 0, 16);
|
|
169
|
+
assertBoundedStyleNumber(style.changeWidth, `${path}.changeWidth`, 1, 160);
|
|
170
|
+
assertBoundedStyleNumber(style.changeHeight, `${path}.changeHeight`, 1, 160);
|
|
171
|
+
assertBoundedStyleNumber(
|
|
172
|
+
style.changeCornerRadius,
|
|
173
|
+
`${path}.changeCornerRadius`,
|
|
174
|
+
0,
|
|
175
|
+
80
|
|
176
|
+
);
|
|
177
|
+
if (style.image) {
|
|
178
|
+
assertBoundedStyleNumber(style.image.width, `${path}.image.width`, 1, 160);
|
|
179
|
+
assertBoundedStyleNumber(
|
|
180
|
+
style.image.height,
|
|
181
|
+
`${path}.image.height`,
|
|
182
|
+
1,
|
|
183
|
+
160
|
|
184
|
+
);
|
|
185
|
+
assertBoundedStyleNumber(
|
|
186
|
+
style.image.cornerRadius,
|
|
187
|
+
`${path}.image.cornerRadius`,
|
|
188
|
+
0,
|
|
189
|
+
80
|
|
190
|
+
);
|
|
191
|
+
assertVisualShape(style.image.shape, `${path}.image.shape`);
|
|
192
|
+
if (
|
|
193
|
+
style.image.contentFit !== undefined &&
|
|
194
|
+
!['cover', 'contain', 'fill', 'center'].includes(style.image.contentFit)
|
|
195
|
+
) {
|
|
196
|
+
fail(
|
|
197
|
+
`${path}.image.contentFit`,
|
|
198
|
+
'must be cover, contain, fill, or center'
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
assertMarketTextStyle(style.title, `${path}.title`);
|
|
203
|
+
assertMarketTextStyle(style.subtitle, `${path}.subtitle`);
|
|
204
|
+
assertMarketTextStyle(style.price, `${path}.price`);
|
|
205
|
+
assertMarketTextStyle(style.change, `${path}.change`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function assertMarketRow(row: MarketRow, path: string): void {
|
|
209
|
+
if (!['token', 'stock', 'perp'].includes(row.variant)) {
|
|
210
|
+
fail(`${path}.variant`, 'must be token, stock, or perp');
|
|
211
|
+
}
|
|
212
|
+
assertText(row.title, `${path}.title`);
|
|
213
|
+
assertText(row.subtitle, `${path}.subtitle`);
|
|
214
|
+
assertText(row.price, `${path}.price`);
|
|
215
|
+
assertText(row.change.text, `${path}.change.text`);
|
|
216
|
+
assertLeadingVisual(row.leading, `${path}.leading`);
|
|
217
|
+
const assertSegments = (
|
|
218
|
+
segments: MarketRow['priceSegments'],
|
|
219
|
+
segmentPath: string
|
|
220
|
+
) => {
|
|
221
|
+
segments?.forEach((segment, index) => {
|
|
222
|
+
assertText(segment.text, `${segmentPath}[${index}].text`);
|
|
223
|
+
if (segment.style !== undefined && segment.style !== 'subscript') {
|
|
224
|
+
fail(
|
|
225
|
+
`${segmentPath}[${index}].style`,
|
|
226
|
+
'must be subscript when provided'
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
};
|
|
231
|
+
assertSegments(row.subtitleSegments, `${path}.subtitleSegments`);
|
|
232
|
+
assertSegments(row.priceSegments, `${path}.priceSegments`);
|
|
233
|
+
assertSegments(row.change.textSegments, `${path}.change.textSegments`);
|
|
234
|
+
if (!['positive', 'negative', 'neutral'].includes(row.change.tone)) {
|
|
235
|
+
fail(`${path}.change.tone`, 'must be positive, negative, or neutral');
|
|
236
|
+
}
|
|
237
|
+
if ((row.badges?.length ?? 0) > MAX_MARKET_BADGES) {
|
|
238
|
+
fail(`${path}.badges`, `supports at most ${MAX_MARKET_BADGES} badges`);
|
|
239
|
+
}
|
|
240
|
+
const badgeKeys = new Set<string>();
|
|
241
|
+
row.badges?.forEach((badge, index) => {
|
|
242
|
+
const badgePath = `${path}.badges[${index}]`;
|
|
243
|
+
assertKey(badge.key, `${badgePath}.key`);
|
|
244
|
+
if (badgeKeys.has(badge.key)) {
|
|
245
|
+
fail(`${path}.badges`, `duplicate badge key "${badge.key}"`);
|
|
246
|
+
}
|
|
247
|
+
badgeKeys.add(badge.key);
|
|
248
|
+
assertText(badge.text, `${badgePath}.text`);
|
|
249
|
+
if (badge.iconName !== undefined && badge.iconName !== 'verified') {
|
|
250
|
+
fail(`${badgePath}.iconName`, 'must be verified when provided');
|
|
251
|
+
}
|
|
252
|
+
if (badge.iconName !== undefined && badge.icon !== undefined) {
|
|
253
|
+
fail(`${badgePath}.icon`, 'cannot be combined with iconName');
|
|
254
|
+
}
|
|
255
|
+
assertImage(badge.icon, `${badgePath}.icon`);
|
|
256
|
+
if (
|
|
257
|
+
badge.tone !== undefined &&
|
|
258
|
+
!['neutral', 'info', 'success', 'warning', 'danger'].includes(badge.tone)
|
|
259
|
+
) {
|
|
260
|
+
fail(
|
|
261
|
+
`${badgePath}.tone`,
|
|
262
|
+
'must be neutral, info, success, warning, or danger'
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
if (!badge.text && !badge.icon && !badge.iconName) {
|
|
266
|
+
fail(badgePath, 'requires text, icon, or iconName');
|
|
267
|
+
}
|
|
268
|
+
if (badge.actionKey !== undefined) {
|
|
269
|
+
assertKey(badge.actionKey, `${badgePath}.actionKey`);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
for (const [key, value] of [
|
|
273
|
+
['pressActionKey', row.pressActionKey],
|
|
274
|
+
['pressInActionKey', row.pressInActionKey],
|
|
275
|
+
['longPressActionKey', row.longPressActionKey],
|
|
276
|
+
['diagnostics.imageBindActionKey', row.diagnostics?.imageBindActionKey],
|
|
277
|
+
] as const) {
|
|
278
|
+
if (value !== undefined) assertKey(value, `${path}.${key}`);
|
|
279
|
+
}
|
|
280
|
+
assertMarketStyle(row.style, `${path}.style`);
|
|
281
|
+
}
|
|
282
|
+
|
|
112
283
|
function assertSectionHeaderVariant(
|
|
113
284
|
variant: string | undefined,
|
|
114
285
|
path: string
|
|
@@ -295,6 +466,8 @@ function assertVisual(row: RowModel, path: string): void {
|
|
|
295
466
|
? [row.leading]
|
|
296
467
|
: row.type === 'dataRow'
|
|
297
468
|
? [row.leading]
|
|
469
|
+
: row.type === 'market'
|
|
470
|
+
? [row.leading]
|
|
298
471
|
: row.type === 'metricCard'
|
|
299
472
|
? [row.visual]
|
|
300
473
|
: [];
|
|
@@ -488,6 +661,9 @@ function assertRow(
|
|
|
488
661
|
fail(`${path}.badges`, `supports at most ${MAX_BADGES} badges`);
|
|
489
662
|
}
|
|
490
663
|
break;
|
|
664
|
+
case 'market':
|
|
665
|
+
assertMarketRow(row, path);
|
|
666
|
+
break;
|
|
491
667
|
case 'mediaTile':
|
|
492
668
|
assertImage(row.image, `${path}.image`);
|
|
493
669
|
if (
|
|
@@ -583,6 +759,10 @@ function assertRow(
|
|
|
583
759
|
assertTrailingAccessories(row.trailing, `${path}.trailing`);
|
|
584
760
|
break;
|
|
585
761
|
case 'system':
|
|
762
|
+
const presentation = 'presentation' in row ? row.presentation : undefined;
|
|
763
|
+
if (presentation !== undefined && presentation !== 'market') {
|
|
764
|
+
fail(`${path}.presentation`, 'must be market when provided');
|
|
765
|
+
}
|
|
586
766
|
if (
|
|
587
767
|
// OneKey patch: deprecated-wallet warnings retain the original scrolling semantics.
|
|
588
768
|
!['loading', 'retry', 'noMatch', 'end', 'spacer', 'warning'].includes(
|
|
@@ -595,6 +775,17 @@ function assertRow(
|
|
|
595
775
|
);
|
|
596
776
|
}
|
|
597
777
|
if (row.variant === 'warning') assertText(row.title, `${path}.title`);
|
|
778
|
+
if (
|
|
779
|
+
row.variant === 'loading' &&
|
|
780
|
+
row.loadingStyle !== undefined &&
|
|
781
|
+
row.loadingStyle !== 'skeleton' &&
|
|
782
|
+
row.loadingStyle !== 'spinner'
|
|
783
|
+
) {
|
|
784
|
+
fail(
|
|
785
|
+
`${path}.loadingStyle`,
|
|
786
|
+
'must be skeleton or spinner when provided'
|
|
787
|
+
);
|
|
788
|
+
}
|
|
598
789
|
if (row.variant !== 'spacer') {
|
|
599
790
|
assertText(row.message, `${path}.message`);
|
|
600
791
|
}
|
|
@@ -879,6 +1070,21 @@ function assertPatchChanges(patch: RowPatch, index: number): void {
|
|
|
879
1070
|
fail(`${path}.badges`, `supports at most ${MAX_BADGES} badges`);
|
|
880
1071
|
}
|
|
881
1072
|
break;
|
|
1073
|
+
case 'market':
|
|
1074
|
+
assertMarketRow(
|
|
1075
|
+
{
|
|
1076
|
+
type: 'market',
|
|
1077
|
+
key: patch.key,
|
|
1078
|
+
variant: 'token',
|
|
1079
|
+
leading: { kind: 'icon', name: 'placeholder' },
|
|
1080
|
+
title: '',
|
|
1081
|
+
price: '',
|
|
1082
|
+
change: { text: '', tone: 'neutral' },
|
|
1083
|
+
...patch.changes,
|
|
1084
|
+
} as MarketRow,
|
|
1085
|
+
path
|
|
1086
|
+
);
|
|
1087
|
+
break;
|
|
882
1088
|
case 'mediaTile':
|
|
883
1089
|
assertImage(patch.changes.image, `${path}.image`);
|
|
884
1090
|
if (
|