@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/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 (
|