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