@onekeyfe/react-native-native-list 3.0.105 → 3.0.107
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/NativeListAdapter.kt +16 -3
- package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +6 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +671 -52
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +282 -54
- package/ios/HybridNativeList.swift +8 -2
- package/ios/NativeListCell.swift +630 -31
- package/ios/NativeListDesignAssets.swift +24 -1
- package/ios/RNCNativeListView.swift +216 -42
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_account_error_custom.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_account_error_custom.imageset/icon.svg +14 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/icon.svg +12 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_apple_brand.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_apple_brand.imageset/icon.svg +6 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_bot_illus.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_bot_illus.imageset/icon.svg +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_crossed_small_solid.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_crossed_small_solid.imageset/icon.svg +3 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_globus_outline.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_globus_outline.imageset/icon.svg +7 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_google_illus.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_google_illus.imageset/icon.svg +18 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_lock_solid.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_lock_solid.imageset/icon.svg +7 -0
- package/lib/module/NativeList.js +102 -8
- package/lib/module/NativeList.web.js +20 -2
- package/lib/module/avatarPrefetch.js +174 -0
- package/lib/module/validation.js +45 -2
- package/lib/module/web/NativeListAvatarWorker.js +542 -0
- package/lib/module/web/NativeListWebAvatarCache.js +267 -0
- package/lib/module/web/NativeListWebEngine.js +958 -47
- package/lib/typescript/src/NativeList.web.d.ts +2 -2
- package/lib/typescript/src/avatarPrefetch.d.ts +28 -0
- package/lib/typescript/src/models.d.ts +72 -4
- package/lib/typescript/src/web/NativeListWebAvatarCache.d.ts +6 -0
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +10 -1
- package/package.json +2 -2
- package/src/NativeList.tsx +149 -13
- package/src/NativeList.web.tsx +30 -3
- package/src/avatarPrefetch.ts +236 -0
- package/src/models.ts +98 -7
- package/src/validation.ts +131 -2
- package/src/web/NativeListAvatarWorker.js +567 -0
- package/src/web/NativeListWebAvatarCache.ts +314 -0
- package/src/web/NativeListWebEngine.ts +1409 -60
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { NativeListProps } from './NativeList.types';
|
|
3
|
-
import type { NativeListSnapshot } from './models';
|
|
3
|
+
import type { NativeListSnapshot, RowPatch } from './models';
|
|
4
4
|
export type { NativeListProps, NativeListRef } from './NativeList.types';
|
|
5
5
|
export type { ActionAnchorState, ScrollAlignment, ScrollPositionOptions, ScrollToEndParams, ScrollToIndexFailedInfo, ScrollToIndexParams, ScrollToItemParams, ScrollToKeyParams, ScrollToLocationParams, ScrollToOffsetParams, } from './NativeList.types';
|
|
6
6
|
export declare const NativeList: React.ForwardRefExoticComponent<NativeListProps & React.RefAttributes<Readonly<{
|
|
7
7
|
applySnapshot(snapshot: NativeListSnapshot): void;
|
|
8
|
-
applyPatches(patches: readonly
|
|
8
|
+
applyPatches(patches: readonly RowPatch[]): void;
|
|
9
9
|
reconcileSelection(selectedKeys: readonly string[]): void;
|
|
10
10
|
scrollToKey: (paramsOrKey: import("./NativeList.types").ScrollToKeyParams | string, animated?: boolean, alignment?: import("./NativeList.types").ScrollAlignment) => void;
|
|
11
11
|
scrollToIndex: (paramsOrIndex: import("./NativeList.types").ScrollToIndexParams | number, animated?: boolean, alignment?: import("./NativeList.types").ScrollAlignment) => void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ImageSource, RowModel, RowPatch } from './models';
|
|
2
|
+
export type AvatarCandidate = Readonly<{
|
|
3
|
+
source: ImageSource;
|
|
4
|
+
priority: 0 | 1 | 2;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function avatarPrefetchWindow(rows: readonly RowModel[], first: number, last: number, direction: number, resolveRow?: (row: RowModel) => RowModel): AvatarCandidate[];
|
|
7
|
+
export declare class NativeAvatarPrefetchModel {
|
|
8
|
+
private rows;
|
|
9
|
+
private rowByKey;
|
|
10
|
+
private readonly imageRows;
|
|
11
|
+
constructor(rows: readonly RowModel[]);
|
|
12
|
+
replaceSnapshot(rows: readonly RowModel[]): void;
|
|
13
|
+
applyPatches(patches: readonly RowPatch[], dispatch?: () => void): boolean;
|
|
14
|
+
window(first: number, last: number, direction: number): AvatarCandidate[];
|
|
15
|
+
}
|
|
16
|
+
export declare class NativeAvatarPrefetchQueue {
|
|
17
|
+
private readonly preload;
|
|
18
|
+
private pending;
|
|
19
|
+
private activeUri;
|
|
20
|
+
private timer;
|
|
21
|
+
private disposed;
|
|
22
|
+
private readonly recent;
|
|
23
|
+
constructor(preload: (source: ImageSource) => Promise<boolean>);
|
|
24
|
+
update(candidates: readonly AvatarCandidate[]): void;
|
|
25
|
+
dispose(): void;
|
|
26
|
+
private schedule;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=avatarPrefetch.d.ts.map
|
|
@@ -20,13 +20,43 @@ export type ImageSource = Readonly<{
|
|
|
20
20
|
optimizeTos?: boolean;
|
|
21
21
|
overscan?: number;
|
|
22
22
|
loadingStrategy?: ImageLoadingStrategy;
|
|
23
|
+
fallbackUri?: string;
|
|
24
|
+
retryTimes?: number;
|
|
23
25
|
}>;
|
|
24
26
|
export type BadgeModel = Readonly<{
|
|
25
27
|
key: string;
|
|
26
28
|
text: string;
|
|
27
29
|
tone?: 'neutral' | 'info' | 'success' | 'warning' | 'danger';
|
|
28
30
|
}>;
|
|
31
|
+
export type SelectorTextSegment = Readonly<{
|
|
32
|
+
text: string;
|
|
33
|
+
textSegments?: readonly ValueTextSegment[];
|
|
34
|
+
tone?: TextTone | 'disabled' | 'caution';
|
|
35
|
+
separatorBefore?: boolean;
|
|
36
|
+
}>;
|
|
37
|
+
export type VisualOverlay = Readonly<{
|
|
38
|
+
position: 'topLeft' | 'bottomRight';
|
|
39
|
+
size?: number;
|
|
40
|
+
width?: number;
|
|
41
|
+
height?: number;
|
|
42
|
+
padding?: number;
|
|
43
|
+
offset?: number;
|
|
44
|
+
offsetX?: number;
|
|
45
|
+
offsetY?: number;
|
|
46
|
+
image?: ImageSource;
|
|
47
|
+
name?: string;
|
|
48
|
+
text?: string;
|
|
49
|
+
tintColor?: string;
|
|
50
|
+
backgroundColor?: string;
|
|
51
|
+
}>;
|
|
29
52
|
type VisualWithImage = Readonly<{
|
|
53
|
+
fallbackIcon?: Readonly<{
|
|
54
|
+
name: string;
|
|
55
|
+
tintColor?: string;
|
|
56
|
+
}>;
|
|
57
|
+
overlays?: readonly VisualOverlay[];
|
|
58
|
+
borderStyle?: 'dashed';
|
|
59
|
+
borderColor?: string;
|
|
30
60
|
image?: ImageSource;
|
|
31
61
|
fallbackText?: string;
|
|
32
62
|
backgroundColor?: string;
|
|
@@ -67,10 +97,15 @@ export type SelectionTarget = Readonly<{
|
|
|
67
97
|
}> | Readonly<{
|
|
68
98
|
scope: 'list';
|
|
69
99
|
}>;
|
|
100
|
+
export type ValueTextSegment = Readonly<{
|
|
101
|
+
text: string;
|
|
102
|
+
style?: 'subscript';
|
|
103
|
+
}>;
|
|
70
104
|
export type TrailingAccessory = Readonly<{
|
|
71
105
|
kind: 'value';
|
|
72
106
|
text: string;
|
|
73
107
|
secondary?: boolean;
|
|
108
|
+
textSegments?: readonly ValueTextSegment[];
|
|
74
109
|
}> | Readonly<{
|
|
75
110
|
kind: 'valuePair';
|
|
76
111
|
primary: string;
|
|
@@ -109,6 +144,9 @@ export type TrailingAccessory = Readonly<{
|
|
|
109
144
|
tintColor?: string;
|
|
110
145
|
disabled?: boolean;
|
|
111
146
|
actionKey?: string;
|
|
147
|
+
testID?: string;
|
|
148
|
+
hoverActionKey?: string;
|
|
149
|
+
accessibilityLabel?: string;
|
|
112
150
|
}> | Readonly<{
|
|
113
151
|
kind: 'spinner';
|
|
114
152
|
}> | Readonly<{
|
|
@@ -122,6 +160,13 @@ export type FooterAction = Readonly<{
|
|
|
122
160
|
disabled?: boolean;
|
|
123
161
|
}>;
|
|
124
162
|
export type RowBase = Readonly<{
|
|
163
|
+
height?: number;
|
|
164
|
+
heightRounding?: 'floor' | 'nearest';
|
|
165
|
+
testID?: string;
|
|
166
|
+
opacity?: number;
|
|
167
|
+
backgroundColor?: string;
|
|
168
|
+
backgroundFullWidth?: boolean;
|
|
169
|
+
pressDisabled?: boolean;
|
|
125
170
|
key: string;
|
|
126
171
|
revision?: number;
|
|
127
172
|
sectionKey?: string;
|
|
@@ -136,6 +181,13 @@ export type RowBase = Readonly<{
|
|
|
136
181
|
}>;
|
|
137
182
|
export type IdentityRow = RowBase & Readonly<{
|
|
138
183
|
type: 'identity';
|
|
184
|
+
titleMatch?: readonly Readonly<{
|
|
185
|
+
start: number;
|
|
186
|
+
end: number;
|
|
187
|
+
}>[];
|
|
188
|
+
titleActionKey?: string;
|
|
189
|
+
titleActionOnHover?: boolean;
|
|
190
|
+
subtitleSegments?: readonly SelectorTextSegment[];
|
|
139
191
|
presentation?: 'walletSidebar' | 'accountSelector' | 'networkSelector';
|
|
140
192
|
leading: LeadingVisual;
|
|
141
193
|
leadingAction?: Extract<TrailingAccessory, {
|
|
@@ -243,6 +295,11 @@ export type MetricCardRow = RowBase & Readonly<{
|
|
|
243
295
|
}>;
|
|
244
296
|
export type SectionHeaderRow = RowBase & Readonly<{
|
|
245
297
|
type: 'sectionHeader';
|
|
298
|
+
sticky?: boolean;
|
|
299
|
+
valueActionTestID?: string;
|
|
300
|
+
valueSegments?: readonly ValueTextSegment[];
|
|
301
|
+
titleActionKey?: string;
|
|
302
|
+
titleActionOnHover?: boolean;
|
|
246
303
|
sectionKey: string;
|
|
247
304
|
presentation?: 'networkSelector';
|
|
248
305
|
indexTitle?: string;
|
|
@@ -284,6 +341,12 @@ export type SystemRow = RowBase & (Readonly<{
|
|
|
284
341
|
variant: 'retry';
|
|
285
342
|
message: string;
|
|
286
343
|
actionKey: string;
|
|
344
|
+
}> | Readonly<{
|
|
345
|
+
type: 'system';
|
|
346
|
+
variant: 'warning';
|
|
347
|
+
title: string;
|
|
348
|
+
message: string;
|
|
349
|
+
borderColor?: string;
|
|
287
350
|
}> | Readonly<{
|
|
288
351
|
type: 'system';
|
|
289
352
|
variant: 'noMatch';
|
|
@@ -299,6 +362,10 @@ export type SystemRow = RowBase & (Readonly<{
|
|
|
299
362
|
}>);
|
|
300
363
|
export type RowModel = IdentityRow | WalletGroupRow | RailRow | ActivityRow | MessageRow | DataRow | MediaTileRow | MetricCardRow | SectionHeaderRow | ActionRow | SystemRow;
|
|
301
364
|
export type NativeListTheme = Readonly<{
|
|
365
|
+
checkboxBackground?: string;
|
|
366
|
+
checkboxBorder?: string;
|
|
367
|
+
checkboxIcon?: string;
|
|
368
|
+
cautionBackground?: string;
|
|
302
369
|
background: string;
|
|
303
370
|
rowBackground: string;
|
|
304
371
|
rowSelectedBackground: string;
|
|
@@ -318,6 +385,7 @@ export type NativeListTheme = Readonly<{
|
|
|
318
385
|
inverseBackground?: string;
|
|
319
386
|
inverseText?: string;
|
|
320
387
|
info?: string;
|
|
388
|
+
caution?: string;
|
|
321
389
|
}>;
|
|
322
390
|
export type SectionIndexConfig = Readonly<{
|
|
323
391
|
enabled: boolean;
|
|
@@ -355,11 +423,11 @@ export type NativeListSnapshot = Readonly<{
|
|
|
355
423
|
fixedFooter?: ActionRow | SystemRow;
|
|
356
424
|
theme?: NativeListTheme;
|
|
357
425
|
}>;
|
|
358
|
-
type CommonPatchFields = 'revision' | 'disabled' | 'selected' | 'separator';
|
|
426
|
+
type CommonPatchFields = 'revision' | 'disabled' | 'selected' | 'separator' | 'height' | 'heightRounding' | 'opacity' | 'pressDisabled' | 'accessibilityLabel';
|
|
359
427
|
export type RowPatch = Readonly<{
|
|
360
428
|
type: 'identity';
|
|
361
429
|
key: string;
|
|
362
|
-
changes: Partial<Pick<IdentityRow, CommonPatchFields | 'title' | 'subtitle' | 'tertiary' | 'tertiaryTone' | 'badges' | 'trailing' | 'leading' | 'leadingAction'>>;
|
|
430
|
+
changes: Partial<Pick<IdentityRow, CommonPatchFields | 'title' | 'subtitle' | 'tertiary' | 'tertiaryTone' | 'badges' | 'trailing' | 'leading' | 'leadingAction' | 'titleMatch' | 'titleActionKey' | 'titleActionOnHover' | 'subtitleSegments'>>;
|
|
363
431
|
}> | Readonly<{
|
|
364
432
|
type: 'rail';
|
|
365
433
|
key: string;
|
|
@@ -387,7 +455,7 @@ export type RowPatch = Readonly<{
|
|
|
387
455
|
}> | Readonly<{
|
|
388
456
|
type: 'sectionHeader';
|
|
389
457
|
key: string;
|
|
390
|
-
changes: Partial<Pick<SectionHeaderRow, CommonPatchFields | 'variant' | 'title' | 'subtitle' | 'value' | 'valueActionKey' | 'titleIcon' | 'valueIcon' | 'checkbox'>>;
|
|
458
|
+
changes: Partial<Pick<SectionHeaderRow, CommonPatchFields | 'variant' | 'title' | 'subtitle' | 'value' | 'valueActionKey' | 'titleActionKey' | 'titleActionOnHover' | 'titleIcon' | 'valueIcon' | 'checkbox'>>;
|
|
391
459
|
}> | Readonly<{
|
|
392
460
|
type: 'action';
|
|
393
461
|
key: string;
|
|
@@ -424,7 +492,7 @@ export type RowActionEvent = Readonly<{
|
|
|
424
492
|
sectionKey?: string;
|
|
425
493
|
anchor?: NativeListActionAnchor;
|
|
426
494
|
}>;
|
|
427
|
-
export type ActionAnchorInvalidationReason = 'scroll' | 'rebind' | 'snapshot' | 'layout' | 'destroy';
|
|
495
|
+
export type ActionAnchorInvalidationReason = 'pointerLeave' | 'scroll' | 'rebind' | 'snapshot' | 'layout' | 'destroy';
|
|
428
496
|
export type ActionAnchorInvalidatedEvent = Readonly<{
|
|
429
497
|
token: string;
|
|
430
498
|
reason: ActionAnchorInvalidationReason;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type AvatarLease = (() => void) & {
|
|
2
|
+
setPriority: (priority: number) => void;
|
|
3
|
+
};
|
|
4
|
+
export declare function canonicalNativeListAvatarUri(uri: string): string | undefined;
|
|
5
|
+
export declare function acquireNativeListAvatar(document: Document, uri: string, resolve: (url: string) => void, reject: () => void, priority?: number): AvatarLease;
|
|
6
|
+
//# sourceMappingURL=NativeListWebAvatarCache.d.ts.map
|
|
@@ -77,9 +77,13 @@ export declare class NativeListWebEngine {
|
|
|
77
77
|
private resizeObserver;
|
|
78
78
|
private pendingScroll;
|
|
79
79
|
private lastVisibleSignature;
|
|
80
|
+
private readonly avatarLeases;
|
|
81
|
+
private avatarOffset;
|
|
82
|
+
private avatarDirection;
|
|
80
83
|
private reachedGeneration;
|
|
81
84
|
private stickyKey;
|
|
82
85
|
private previewTimer;
|
|
86
|
+
private sectionIndexEntries;
|
|
83
87
|
private pointerReorder;
|
|
84
88
|
private keyboardReorder;
|
|
85
89
|
private reorderMoveFrame;
|
|
@@ -100,6 +104,7 @@ export declare class NativeListWebEngine {
|
|
|
100
104
|
private lastViewportWidth;
|
|
101
105
|
private lastViewportHeight;
|
|
102
106
|
private destroyed;
|
|
107
|
+
private measuredWarningHeights;
|
|
103
108
|
constructor(host: HTMLElement, snapshot: NativeListSnapshot, callbacks: NativeListWebCallbacks, virtualizationEnabled?: boolean);
|
|
104
109
|
updateCallbacks(callbacks: NativeListWebCallbacks): void;
|
|
105
110
|
setVirtualizationEnabled(enabled: boolean): void;
|
|
@@ -117,10 +122,12 @@ export declare class NativeListWebEngine {
|
|
|
117
122
|
private setSnapshot;
|
|
118
123
|
private applyTheme;
|
|
119
124
|
private recomputeLayout;
|
|
125
|
+
private updateAvatarWindow;
|
|
120
126
|
private renderWindow;
|
|
121
127
|
private positionElement;
|
|
122
128
|
private renderElement;
|
|
123
129
|
private renderFooter;
|
|
130
|
+
private sectionIndexVisibleEntryIndices;
|
|
124
131
|
private renderSectionIndex;
|
|
125
132
|
private updateVisibleSelection;
|
|
126
133
|
private updateVisibleState;
|
|
@@ -145,6 +152,8 @@ export declare class NativeListWebEngine {
|
|
|
145
152
|
private invalidateActionAnchor;
|
|
146
153
|
private emitActionAnchorInvalidated;
|
|
147
154
|
private handleRowPress;
|
|
155
|
+
private handleTitlePointerOver;
|
|
156
|
+
private handleTitlePointerOut;
|
|
148
157
|
private handleClick;
|
|
149
158
|
private handleKeyDown;
|
|
150
159
|
private startKeyboardReorder;
|
|
@@ -157,7 +166,7 @@ export declare class NativeListWebEngine {
|
|
|
157
166
|
private requestFrame;
|
|
158
167
|
private cancelFrame;
|
|
159
168
|
private selectIndexPosition;
|
|
160
|
-
private
|
|
169
|
+
private sectionIndexEntryAtEvent;
|
|
161
170
|
private handleIndexPointer;
|
|
162
171
|
private handleIndexClick;
|
|
163
172
|
private handlePullStart;
|
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.107",
|
|
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.107",
|
|
86
86
|
"react": "*",
|
|
87
87
|
"react-native": "*",
|
|
88
88
|
"react-native-nitro-modules": "0.37.0"
|
package/src/NativeList.tsx
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useEffect,
|
|
4
|
+
useImperativeHandle,
|
|
5
|
+
useMemo,
|
|
6
|
+
useRef,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import {
|
|
9
|
+
OneKeyImageCache,
|
|
10
|
+
OneKeyImageCachePolicy,
|
|
11
|
+
} from '@onekeyfe/react-native-image';
|
|
12
|
+
import {
|
|
13
|
+
NativeAvatarPrefetchModel,
|
|
14
|
+
NativeAvatarPrefetchQueue,
|
|
15
|
+
} from './avatarPrefetch';
|
|
2
16
|
import { callback, getHostComponent } from 'react-native-nitro-modules';
|
|
3
17
|
import type {
|
|
4
18
|
NativeListMethods,
|
|
@@ -13,6 +27,7 @@ import type {
|
|
|
13
27
|
VisibleRangeChangedEvent,
|
|
14
28
|
} from './models';
|
|
15
29
|
import type { NativeListProps, NativeListRef } from './NativeList.types';
|
|
30
|
+
import { isSelectableRow } from './selection';
|
|
16
31
|
import {
|
|
17
32
|
normalizeIndexScroll,
|
|
18
33
|
normalizeKeyScroll,
|
|
@@ -22,7 +37,11 @@ import {
|
|
|
22
37
|
validateOffset,
|
|
23
38
|
type NormalizedPositionScroll,
|
|
24
39
|
} from './scrolling';
|
|
25
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
applyRowPatches,
|
|
42
|
+
serializePatches,
|
|
43
|
+
serializeSnapshot,
|
|
44
|
+
} from './validation';
|
|
26
45
|
|
|
27
46
|
export type { NativeListProps, NativeListRef } from './NativeList.types';
|
|
28
47
|
export type {
|
|
@@ -91,8 +110,74 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
91
110
|
onRefresh,
|
|
92
111
|
onScrollToIndexFailed,
|
|
93
112
|
};
|
|
113
|
+
const snapshotJson = useMemo(() => serializeSnapshot(snapshot), [snapshot]);
|
|
94
114
|
const snapshotRef = useRef(snapshot);
|
|
95
|
-
|
|
115
|
+
const previousSnapshotJsonRef = useRef(snapshotJson);
|
|
116
|
+
const snapshotChanged = previousSnapshotJsonRef.current !== snapshotJson;
|
|
117
|
+
if (snapshotChanged) {
|
|
118
|
+
snapshotRef.current = snapshot;
|
|
119
|
+
previousSnapshotJsonRef.current = snapshotJson;
|
|
120
|
+
}
|
|
121
|
+
// OneKey patch: range events already cross the bridge only when row indices
|
|
122
|
+
// change. Prefetch is optional work; scrolling and visible loads stay native.
|
|
123
|
+
const avatarQueueRef = useRef<NativeAvatarPrefetchQueue | null>(null);
|
|
124
|
+
const avatarRangeRef = useRef<
|
|
125
|
+
{ first: number; last: number; direction: number } | undefined
|
|
126
|
+
>(undefined);
|
|
127
|
+
// OneKey patch: a changed prop is a complete snapshot; unrelated renders must
|
|
128
|
+
// not erase image patches already dispatched through the imperative handle.
|
|
129
|
+
// const avatarPrefetchPaused = useRef(false);
|
|
130
|
+
const avatarModelRef = useRef<NativeAvatarPrefetchModel | null>(null);
|
|
131
|
+
const avatarLifecycle = useRef<'pending' | 'mounted' | 'unmounted'>(
|
|
132
|
+
'pending'
|
|
133
|
+
);
|
|
134
|
+
if (!avatarModelRef.current)
|
|
135
|
+
avatarModelRef.current = new NativeAvatarPrefetchModel(snapshot.rows);
|
|
136
|
+
else if (snapshotChanged)
|
|
137
|
+
avatarModelRef.current.replaceSnapshot(snapshot.rows);
|
|
138
|
+
const updateAvatarPrefetch = () => {
|
|
139
|
+
const range = avatarRangeRef.current;
|
|
140
|
+
if (!range) return;
|
|
141
|
+
avatarQueueRef.current?.update(
|
|
142
|
+
avatarModelRef.current?.window(
|
|
143
|
+
range.first,
|
|
144
|
+
range.last,
|
|
145
|
+
range.direction
|
|
146
|
+
) ?? []
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
const updateAvatarPrefetchRef = useRef(updateAvatarPrefetch);
|
|
150
|
+
updateAvatarPrefetchRef.current = updateAvatarPrefetch;
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
avatarLifecycle.current = 'mounted';
|
|
153
|
+
const queue = new NativeAvatarPrefetchQueue((source) =>
|
|
154
|
+
OneKeyImageCache.preload([
|
|
155
|
+
{
|
|
156
|
+
uri: source.uri,
|
|
157
|
+
headers: source.headers,
|
|
158
|
+
resizeWidth: source.width,
|
|
159
|
+
resizeHeight: source.height,
|
|
160
|
+
optimizeTos: false,
|
|
161
|
+
cachePolicy:
|
|
162
|
+
source.cachePolicy === 'memory'
|
|
163
|
+
? OneKeyImageCachePolicy.MEMORY
|
|
164
|
+
: source.cachePolicy === 'disk'
|
|
165
|
+
? OneKeyImageCachePolicy.DISK
|
|
166
|
+
: OneKeyImageCachePolicy.MEMORY_DISK,
|
|
167
|
+
},
|
|
168
|
+
])
|
|
169
|
+
);
|
|
170
|
+
avatarQueueRef.current = queue;
|
|
171
|
+
updateAvatarPrefetchRef.current();
|
|
172
|
+
return () => {
|
|
173
|
+
queue.dispose();
|
|
174
|
+
avatarQueueRef.current = null;
|
|
175
|
+
avatarLifecycle.current = 'unmounted';
|
|
176
|
+
};
|
|
177
|
+
}, []);
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
updateAvatarPrefetchRef.current();
|
|
180
|
+
}, [snapshot]);
|
|
96
181
|
const initialScrollRef = useRef<
|
|
97
182
|
| Readonly<{
|
|
98
183
|
index?: number;
|
|
@@ -123,7 +208,6 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
123
208
|
}
|
|
124
209
|
);
|
|
125
210
|
const didApplyInitialScroll = useRef(false);
|
|
126
|
-
const snapshotJson = useMemo(() => serializeSnapshot(snapshot), [snapshot]);
|
|
127
211
|
|
|
128
212
|
const emitIndexFailure = (
|
|
129
213
|
index: number,
|
|
@@ -179,15 +263,46 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
179
263
|
|
|
180
264
|
useImperativeHandle(forwardedRef, () => ({
|
|
181
265
|
applySnapshot(nextSnapshot) {
|
|
266
|
+
const nextSnapshotJson = serializeSnapshot(nextSnapshot);
|
|
267
|
+
nativeRef.current?.applySnapshot(nextSnapshotJson);
|
|
182
268
|
snapshotRef.current = nextSnapshot;
|
|
183
|
-
nativeRef.current
|
|
269
|
+
if (nativeRef.current && avatarLifecycle.current !== 'unmounted') {
|
|
270
|
+
avatarModelRef.current?.replaceSnapshot(nextSnapshot.rows);
|
|
271
|
+
updateAvatarPrefetchRef.current();
|
|
272
|
+
}
|
|
184
273
|
},
|
|
185
274
|
applyPatches(patches) {
|
|
186
|
-
if (patches.length
|
|
187
|
-
|
|
275
|
+
if (patches.length === 0) return;
|
|
276
|
+
const nextSnapshot = applyRowPatches(snapshotRef.current, patches);
|
|
277
|
+
const native = nativeRef.current;
|
|
278
|
+
const dispatch = native
|
|
279
|
+
? () => {
|
|
280
|
+
native.applyPatches(serializePatches(patches));
|
|
281
|
+
snapshotRef.current = nextSnapshot;
|
|
282
|
+
}
|
|
283
|
+
: undefined;
|
|
284
|
+
if (avatarLifecycle.current === 'unmounted') {
|
|
285
|
+
dispatch?.();
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (avatarModelRef.current?.applyPatches(patches, dispatch))
|
|
289
|
+
updateAvatarPrefetchRef.current();
|
|
188
290
|
},
|
|
189
291
|
reconcileSelection(selectedKeys) {
|
|
190
292
|
nativeRef.current?.reconcileSelection(JSON.stringify(selectedKeys));
|
|
293
|
+
const current = snapshotRef.current;
|
|
294
|
+
if (
|
|
295
|
+
current.selection &&
|
|
296
|
+
(current.selection.mode !== 'single' || selectedKeys.length <= 1) &&
|
|
297
|
+
selectedKeys.every((key) =>
|
|
298
|
+
current.rows.some((row) => row.key === key && isSelectableRow(row))
|
|
299
|
+
)
|
|
300
|
+
) {
|
|
301
|
+
snapshotRef.current = {
|
|
302
|
+
...current,
|
|
303
|
+
selection: { ...current.selection, selectedKeys },
|
|
304
|
+
};
|
|
305
|
+
}
|
|
191
306
|
},
|
|
192
307
|
scrollToKey(paramsOrKey, animated, alignment) {
|
|
193
308
|
const { key, scroll } = normalizeKeyScroll(
|
|
@@ -264,9 +379,21 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
264
379
|
);
|
|
265
380
|
}),
|
|
266
381
|
onSelectionDelta: callback((payloadJson: string) => {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
)
|
|
382
|
+
const payload = parsePayload<SelectionDeltaEvent>(payloadJson);
|
|
383
|
+
const current = snapshotRef.current;
|
|
384
|
+
if (current.selection) {
|
|
385
|
+
const selectedKeys = new Set(current.selection.selectedKeys);
|
|
386
|
+
payload.removedKeys.forEach((key) => selectedKeys.delete(key));
|
|
387
|
+
payload.addedKeys.forEach((key) => selectedKeys.add(key));
|
|
388
|
+
snapshotRef.current = {
|
|
389
|
+
...current,
|
|
390
|
+
selection: {
|
|
391
|
+
...current.selection,
|
|
392
|
+
selectedKeys: [...selectedKeys],
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
callbacksRef.current.onSelectionDelta?.(payload);
|
|
270
397
|
}),
|
|
271
398
|
onReorder: callback((payloadJson: string) => {
|
|
272
399
|
callbacksRef.current.onReorder?.(
|
|
@@ -279,9 +406,18 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
279
406
|
);
|
|
280
407
|
}),
|
|
281
408
|
onVisibleRangeChanged: callback((payloadJson: string) => {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
409
|
+
const payload = parsePayload<VisibleRangeChangedEvent>(payloadJson);
|
|
410
|
+
const previous = avatarRangeRef.current;
|
|
411
|
+
avatarRangeRef.current = {
|
|
412
|
+
first: payload.firstIndex,
|
|
413
|
+
last: payload.lastIndex,
|
|
414
|
+
direction:
|
|
415
|
+
previous && payload.firstIndex !== previous.first
|
|
416
|
+
? Math.sign(payload.firstIndex - previous.first)
|
|
417
|
+
: previous?.direction ?? 1,
|
|
418
|
+
};
|
|
419
|
+
updateAvatarPrefetchRef.current();
|
|
420
|
+
callbacksRef.current.onVisibleRangeChanged?.(payload);
|
|
285
421
|
}),
|
|
286
422
|
}),
|
|
287
423
|
[]
|
package/src/NativeList.web.tsx
CHANGED
|
@@ -9,7 +9,7 @@ import React, {
|
|
|
9
9
|
} from 'react';
|
|
10
10
|
import { View } from 'react-native';
|
|
11
11
|
import type { NativeListProps, NativeListRef } from './NativeList.types';
|
|
12
|
-
import type { NativeListSnapshot } from './models';
|
|
12
|
+
import type { NativeListSnapshot, RowPatch } from './models';
|
|
13
13
|
import {
|
|
14
14
|
normalizeIndexScroll,
|
|
15
15
|
normalizeKeyScroll,
|
|
@@ -69,7 +69,20 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
69
69
|
[snapshot]
|
|
70
70
|
);
|
|
71
71
|
const snapshotRef = useRef(validatedSnapshot);
|
|
72
|
-
|
|
72
|
+
const snapshotPropRef = useRef(validatedSnapshot);
|
|
73
|
+
// An imperative snapshot survives host mounting until the snapshot prop changes.
|
|
74
|
+
if (snapshotPropRef.current !== validatedSnapshot) {
|
|
75
|
+
snapshotPropRef.current = validatedSnapshot;
|
|
76
|
+
snapshotRef.current = validatedSnapshot;
|
|
77
|
+
}
|
|
78
|
+
// OneKey patch: React refs can be ready before the DOM engine is mounted.
|
|
79
|
+
const pendingPatchesRef = useRef<
|
|
80
|
+
| {
|
|
81
|
+
snapshot: NativeListSnapshot;
|
|
82
|
+
batches: Array<readonly RowPatch[]>;
|
|
83
|
+
}
|
|
84
|
+
| undefined
|
|
85
|
+
>(undefined);
|
|
73
86
|
const appliedSnapshotRef = useRef<NativeListSnapshot | undefined>(
|
|
74
87
|
undefined
|
|
75
88
|
);
|
|
@@ -125,6 +138,10 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
125
138
|
);
|
|
126
139
|
engineRef.current = engine;
|
|
127
140
|
appliedSnapshotRef.current = snapshotRef.current;
|
|
141
|
+
const pending = pendingPatchesRef.current;
|
|
142
|
+
pendingPatchesRef.current = undefined;
|
|
143
|
+
if (pending?.snapshot === snapshotRef.current)
|
|
144
|
+
pending.batches.forEach((patches) => engine.applyPatches(patches));
|
|
128
145
|
const initial = initialScrollRef.current;
|
|
129
146
|
if (initial && !didApplyInitialScroll.current) {
|
|
130
147
|
didApplyInitialScroll.current = true;
|
|
@@ -166,6 +183,7 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
166
183
|
useImperativeHandle(forwardedRef, () => ({
|
|
167
184
|
applySnapshot(nextSnapshot) {
|
|
168
185
|
const next = validateSnapshot(nextSnapshot);
|
|
186
|
+
pendingPatchesRef.current = undefined;
|
|
169
187
|
snapshotRef.current = next;
|
|
170
188
|
appliedSnapshotRef.current = next;
|
|
171
189
|
engineRef.current?.applySnapshot(next);
|
|
@@ -173,7 +191,16 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
173
191
|
applyPatches(patches) {
|
|
174
192
|
if (patches.length > 0) {
|
|
175
193
|
serializePatches(patches);
|
|
176
|
-
engineRef.current?.applyPatches(patches);
|
|
194
|
+
// engineRef.current?.applyPatches(patches);
|
|
195
|
+
if (engineRef.current) engineRef.current.applyPatches(patches);
|
|
196
|
+
else {
|
|
197
|
+
if (pendingPatchesRef.current?.snapshot !== snapshotRef.current)
|
|
198
|
+
pendingPatchesRef.current = {
|
|
199
|
+
snapshot: snapshotRef.current,
|
|
200
|
+
batches: [],
|
|
201
|
+
};
|
|
202
|
+
pendingPatchesRef.current.batches.push(patches);
|
|
203
|
+
}
|
|
177
204
|
}
|
|
178
205
|
},
|
|
179
206
|
reconcileSelection(selectedKeys) {
|