@onekeyfe/react-native-native-list 3.0.114 → 3.0.116
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 +47 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +795 -30
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +445 -91
- package/ios/NativeListCell.swift +655 -20
- package/ios/NativeListDesignAssets.swift +8 -0
- package/ios/RNCNativeListView.swift +275 -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 +145 -1
- package/lib/module/web/NativeListWebEngine.js +461 -51
- package/lib/typescript/src/models.d.ts +108 -2
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +37 -3
- package/package.json +4 -2
- package/src/models.ts +144 -3
- package/src/validation.ts +250 -0
- package/src/web/NativeListWebEngine.ts +766 -91
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,216 @@ 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
|
+
'contentTrailingGap',
|
|
166
|
+
'subtitleTrailingPadding',
|
|
167
|
+
] as const) {
|
|
168
|
+
assertBoundedStyleNumber(style[field], `${path}.${field}`, 0, 64);
|
|
169
|
+
}
|
|
170
|
+
// OneKey patch: validate the opt-in Market badge layout.
|
|
171
|
+
if (
|
|
172
|
+
style.titleBadgeLayout !== undefined &&
|
|
173
|
+
style.titleBadgeLayout !== 'inline'
|
|
174
|
+
) {
|
|
175
|
+
fail(`${path}.titleBadgeLayout`, 'must be inline when provided');
|
|
176
|
+
}
|
|
177
|
+
assertBoundedStyleNumber(style.lineGap, `${path}.lineGap`, 0, 16);
|
|
178
|
+
assertBoundedStyleNumber(style.changeWidth, `${path}.changeWidth`, 1, 160);
|
|
179
|
+
assertBoundedStyleNumber(style.changeHeight, `${path}.changeHeight`, 1, 160);
|
|
180
|
+
assertBoundedStyleNumber(
|
|
181
|
+
style.changeCornerRadius,
|
|
182
|
+
`${path}.changeCornerRadius`,
|
|
183
|
+
0,
|
|
184
|
+
80
|
|
185
|
+
);
|
|
186
|
+
if (style.image) {
|
|
187
|
+
assertBoundedStyleNumber(style.image.width, `${path}.image.width`, 1, 160);
|
|
188
|
+
assertBoundedStyleNumber(
|
|
189
|
+
style.image.height,
|
|
190
|
+
`${path}.image.height`,
|
|
191
|
+
1,
|
|
192
|
+
160
|
|
193
|
+
);
|
|
194
|
+
assertBoundedStyleNumber(
|
|
195
|
+
style.image.cornerRadius,
|
|
196
|
+
`${path}.image.cornerRadius`,
|
|
197
|
+
0,
|
|
198
|
+
80
|
|
199
|
+
);
|
|
200
|
+
assertVisualShape(style.image.shape, `${path}.image.shape`);
|
|
201
|
+
if (
|
|
202
|
+
style.image.contentFit !== undefined &&
|
|
203
|
+
!['cover', 'contain', 'fill', 'center'].includes(style.image.contentFit)
|
|
204
|
+
) {
|
|
205
|
+
fail(
|
|
206
|
+
`${path}.image.contentFit`,
|
|
207
|
+
'must be cover, contain, fill, or center'
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
assertMarketTextStyle(style.title, `${path}.title`);
|
|
212
|
+
assertMarketTextStyle(style.subtitle, `${path}.subtitle`);
|
|
213
|
+
assertMarketTextStyle(style.price, `${path}.price`);
|
|
214
|
+
assertMarketTextStyle(style.change, `${path}.change`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function assertMarketRow(row: MarketRow, path: string): void {
|
|
218
|
+
if (!['token', 'stock', 'perp'].includes(row.variant)) {
|
|
219
|
+
fail(`${path}.variant`, 'must be token, stock, or perp');
|
|
220
|
+
}
|
|
221
|
+
assertText(row.title, `${path}.title`);
|
|
222
|
+
assertText(row.subtitle, `${path}.subtitle`);
|
|
223
|
+
// OneKey patch: validate the independently laid out Market name.
|
|
224
|
+
if (row.subtitlePrefix) {
|
|
225
|
+
assertText(row.subtitlePrefix.text, `${path}.subtitlePrefix.text`);
|
|
226
|
+
assertBoundedStyleNumber(
|
|
227
|
+
row.subtitlePrefix.gap,
|
|
228
|
+
`${path}.subtitlePrefix.gap`,
|
|
229
|
+
0,
|
|
230
|
+
64
|
|
231
|
+
);
|
|
232
|
+
assertBoundedStyleNumber(
|
|
233
|
+
row.subtitlePrefix.maxWidth,
|
|
234
|
+
`${path}.subtitlePrefix.maxWidth`,
|
|
235
|
+
1,
|
|
236
|
+
320
|
|
237
|
+
);
|
|
238
|
+
assertMarketTextStyle(
|
|
239
|
+
row.subtitlePrefix.style,
|
|
240
|
+
`${path}.subtitlePrefix.style`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
assertText(row.price, `${path}.price`);
|
|
244
|
+
assertText(row.change.text, `${path}.change.text`);
|
|
245
|
+
assertLeadingVisual(row.leading, `${path}.leading`);
|
|
246
|
+
const assertSegments = (
|
|
247
|
+
segments: MarketRow['priceSegments'],
|
|
248
|
+
segmentPath: string
|
|
249
|
+
) => {
|
|
250
|
+
segments?.forEach((segment, index) => {
|
|
251
|
+
assertText(segment.text, `${segmentPath}[${index}].text`);
|
|
252
|
+
if (segment.style !== undefined && segment.style !== 'subscript') {
|
|
253
|
+
fail(
|
|
254
|
+
`${segmentPath}[${index}].style`,
|
|
255
|
+
'must be subscript when provided'
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
};
|
|
260
|
+
assertSegments(row.subtitleSegments, `${path}.subtitleSegments`);
|
|
261
|
+
assertSegments(row.priceSegments, `${path}.priceSegments`);
|
|
262
|
+
assertSegments(row.change.textSegments, `${path}.change.textSegments`);
|
|
263
|
+
if (!['positive', 'negative', 'neutral'].includes(row.change.tone)) {
|
|
264
|
+
fail(`${path}.change.tone`, 'must be positive, negative, or neutral');
|
|
265
|
+
}
|
|
266
|
+
if ((row.badges?.length ?? 0) > MAX_MARKET_BADGES) {
|
|
267
|
+
fail(`${path}.badges`, `supports at most ${MAX_MARKET_BADGES} badges`);
|
|
268
|
+
}
|
|
269
|
+
const badgeKeys = new Set<string>();
|
|
270
|
+
row.badges?.forEach((badge, index) => {
|
|
271
|
+
const badgePath = `${path}.badges[${index}]`;
|
|
272
|
+
assertKey(badge.key, `${badgePath}.key`);
|
|
273
|
+
if (badgeKeys.has(badge.key)) {
|
|
274
|
+
fail(`${path}.badges`, `duplicate badge key "${badge.key}"`);
|
|
275
|
+
}
|
|
276
|
+
badgeKeys.add(badge.key);
|
|
277
|
+
assertText(badge.text, `${badgePath}.text`);
|
|
278
|
+
// OneKey patch: share typography bounds with Market text styles.
|
|
279
|
+
assertMarketTextStyle(badge.style, `${badgePath}.style`);
|
|
280
|
+
assertBoundedStyleNumber(
|
|
281
|
+
badge.style?.height,
|
|
282
|
+
`${badgePath}.style.height`,
|
|
283
|
+
1,
|
|
284
|
+
64
|
|
285
|
+
);
|
|
286
|
+
assertBoundedStyleNumber(
|
|
287
|
+
badge.style?.horizontalPadding,
|
|
288
|
+
`${badgePath}.style.horizontalPadding`,
|
|
289
|
+
0,
|
|
290
|
+
32
|
|
291
|
+
);
|
|
292
|
+
if (badge.iconName !== undefined && badge.iconName !== 'verified') {
|
|
293
|
+
fail(`${badgePath}.iconName`, 'must be verified when provided');
|
|
294
|
+
}
|
|
295
|
+
if (badge.iconName !== undefined && badge.icon !== undefined) {
|
|
296
|
+
fail(`${badgePath}.icon`, 'cannot be combined with iconName');
|
|
297
|
+
}
|
|
298
|
+
assertImage(badge.icon, `${badgePath}.icon`);
|
|
299
|
+
if (
|
|
300
|
+
badge.tone !== undefined &&
|
|
301
|
+
!['neutral', 'info', 'success', 'warning', 'danger'].includes(badge.tone)
|
|
302
|
+
) {
|
|
303
|
+
fail(
|
|
304
|
+
`${badgePath}.tone`,
|
|
305
|
+
'must be neutral, info, success, warning, or danger'
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
if (!badge.text && !badge.icon && !badge.iconName) {
|
|
309
|
+
fail(badgePath, 'requires text, icon, or iconName');
|
|
310
|
+
}
|
|
311
|
+
if (badge.actionKey !== undefined) {
|
|
312
|
+
assertKey(badge.actionKey, `${badgePath}.actionKey`);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
for (const [key, value] of [
|
|
316
|
+
['pressActionKey', row.pressActionKey],
|
|
317
|
+
['pressInActionKey', row.pressInActionKey],
|
|
318
|
+
['longPressActionKey', row.longPressActionKey],
|
|
319
|
+
['diagnostics.imageBindActionKey', row.diagnostics?.imageBindActionKey],
|
|
320
|
+
] as const) {
|
|
321
|
+
if (value !== undefined) assertKey(value, `${path}.${key}`);
|
|
322
|
+
}
|
|
323
|
+
assertMarketStyle(row.style, `${path}.style`);
|
|
324
|
+
}
|
|
325
|
+
|
|
112
326
|
function assertSectionHeaderVariant(
|
|
113
327
|
variant: string | undefined,
|
|
114
328
|
path: string
|
|
@@ -295,6 +509,8 @@ function assertVisual(row: RowModel, path: string): void {
|
|
|
295
509
|
? [row.leading]
|
|
296
510
|
: row.type === 'dataRow'
|
|
297
511
|
? [row.leading]
|
|
512
|
+
: row.type === 'market'
|
|
513
|
+
? [row.leading]
|
|
298
514
|
: row.type === 'metricCard'
|
|
299
515
|
? [row.visual]
|
|
300
516
|
: [];
|
|
@@ -488,6 +704,9 @@ function assertRow(
|
|
|
488
704
|
fail(`${path}.badges`, `supports at most ${MAX_BADGES} badges`);
|
|
489
705
|
}
|
|
490
706
|
break;
|
|
707
|
+
case 'market':
|
|
708
|
+
assertMarketRow(row, path);
|
|
709
|
+
break;
|
|
491
710
|
case 'mediaTile':
|
|
492
711
|
assertImage(row.image, `${path}.image`);
|
|
493
712
|
if (
|
|
@@ -583,6 +802,10 @@ function assertRow(
|
|
|
583
802
|
assertTrailingAccessories(row.trailing, `${path}.trailing`);
|
|
584
803
|
break;
|
|
585
804
|
case 'system':
|
|
805
|
+
const presentation = 'presentation' in row ? row.presentation : undefined;
|
|
806
|
+
if (presentation !== undefined && presentation !== 'market') {
|
|
807
|
+
fail(`${path}.presentation`, 'must be market when provided');
|
|
808
|
+
}
|
|
586
809
|
if (
|
|
587
810
|
// OneKey patch: deprecated-wallet warnings retain the original scrolling semantics.
|
|
588
811
|
!['loading', 'retry', 'noMatch', 'end', 'spacer', 'warning'].includes(
|
|
@@ -595,6 +818,17 @@ function assertRow(
|
|
|
595
818
|
);
|
|
596
819
|
}
|
|
597
820
|
if (row.variant === 'warning') assertText(row.title, `${path}.title`);
|
|
821
|
+
if (
|
|
822
|
+
row.variant === 'loading' &&
|
|
823
|
+
row.loadingStyle !== undefined &&
|
|
824
|
+
row.loadingStyle !== 'skeleton' &&
|
|
825
|
+
row.loadingStyle !== 'spinner'
|
|
826
|
+
) {
|
|
827
|
+
fail(
|
|
828
|
+
`${path}.loadingStyle`,
|
|
829
|
+
'must be skeleton or spinner when provided'
|
|
830
|
+
);
|
|
831
|
+
}
|
|
598
832
|
if (row.variant !== 'spacer') {
|
|
599
833
|
assertText(row.message, `${path}.message`);
|
|
600
834
|
}
|
|
@@ -606,6 +840,7 @@ function assertRow(
|
|
|
606
840
|
}
|
|
607
841
|
if (row.variant === 'retry') {
|
|
608
842
|
assertKey(row.actionKey, `${path}.actionKey`);
|
|
843
|
+
assertText(row.actionText, `${path}.actionText`);
|
|
609
844
|
}
|
|
610
845
|
break;
|
|
611
846
|
}
|
|
@@ -879,6 +1114,21 @@ function assertPatchChanges(patch: RowPatch, index: number): void {
|
|
|
879
1114
|
fail(`${path}.badges`, `supports at most ${MAX_BADGES} badges`);
|
|
880
1115
|
}
|
|
881
1116
|
break;
|
|
1117
|
+
case 'market':
|
|
1118
|
+
assertMarketRow(
|
|
1119
|
+
{
|
|
1120
|
+
type: 'market',
|
|
1121
|
+
key: patch.key,
|
|
1122
|
+
variant: 'token',
|
|
1123
|
+
leading: { kind: 'icon', name: 'placeholder' },
|
|
1124
|
+
title: '',
|
|
1125
|
+
price: '',
|
|
1126
|
+
change: { text: '', tone: 'neutral' },
|
|
1127
|
+
...patch.changes,
|
|
1128
|
+
} as MarketRow,
|
|
1129
|
+
path
|
|
1130
|
+
);
|
|
1131
|
+
break;
|
|
882
1132
|
case 'mediaTile':
|
|
883
1133
|
assertImage(patch.changes.image, `${path}.image`);
|
|
884
1134
|
if (
|