@xhub-reel/feed 0.1.0
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/LICENSE +22 -0
- package/README.md +462 -0
- package/dist/index.d.mts +652 -0
- package/dist/index.d.ts +652 -0
- package/dist/index.js +1855 -0
- package/dist/index.mjs +1829 -0
- package/package.json +72 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { CSSProperties, ReactNode, HTMLAttributes, RefObject } from 'react';
|
|
3
|
+
import { Video, XHubReelConfig, VideoFetchParams } from '@xhub-reel/core';
|
|
4
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
6
|
+
export { UsePreloadOptions, UsePreloadReturn, usePreload } from '@xhub-reel/player';
|
|
7
|
+
|
|
8
|
+
interface VideoFeedProps {
|
|
9
|
+
/** Videos to display */
|
|
10
|
+
videos: Video[];
|
|
11
|
+
/** Initial video index */
|
|
12
|
+
initialIndex?: number;
|
|
13
|
+
/** Called when more videos should be loaded */
|
|
14
|
+
onLoadMore?: () => void | Promise<void>;
|
|
15
|
+
/** Called when the current video changes */
|
|
16
|
+
onVideoChange?: (video: Video, index: number) => void;
|
|
17
|
+
/** Called when a video is liked */
|
|
18
|
+
onLike?: (video: Video) => void;
|
|
19
|
+
/** Called when comments should be shown */
|
|
20
|
+
onComment?: (video: Video) => void;
|
|
21
|
+
/** Called when share sheet should be shown */
|
|
22
|
+
onShare?: (video: Video) => void;
|
|
23
|
+
/** Called when author profile should be shown */
|
|
24
|
+
onAuthorClick?: (video: Video) => void;
|
|
25
|
+
/** Loading state */
|
|
26
|
+
isLoading?: boolean;
|
|
27
|
+
/** Has more videos to load */
|
|
28
|
+
hasMore?: boolean;
|
|
29
|
+
/** Threshold to trigger load more (videos before end) */
|
|
30
|
+
loadMoreThreshold?: number;
|
|
31
|
+
/** Animation duration in ms */
|
|
32
|
+
transitionDuration?: number;
|
|
33
|
+
/** Swipe threshold in pixels */
|
|
34
|
+
swipeThreshold?: number;
|
|
35
|
+
/** Swipe velocity threshold in px/ms */
|
|
36
|
+
velocityThreshold?: number;
|
|
37
|
+
/** Disable swipe gestures */
|
|
38
|
+
gesturesDisabled?: boolean;
|
|
39
|
+
/** Enable haptic feedback on swipe */
|
|
40
|
+
hapticEnabled?: boolean;
|
|
41
|
+
/** Custom styles override */
|
|
42
|
+
style?: CSSProperties;
|
|
43
|
+
/** Custom className (for external CSS if needed) */
|
|
44
|
+
className?: string;
|
|
45
|
+
}
|
|
46
|
+
interface VideoFeedRef {
|
|
47
|
+
slideTo: (index: number, animated?: boolean) => void;
|
|
48
|
+
slideNext: (animated?: boolean) => void;
|
|
49
|
+
slidePrev: (animated?: boolean) => void;
|
|
50
|
+
activeIndex: number;
|
|
51
|
+
totalSlides: number;
|
|
52
|
+
isBeginning: boolean;
|
|
53
|
+
isEnd: boolean;
|
|
54
|
+
}
|
|
55
|
+
declare const VideoFeed: react.ForwardRefExoticComponent<VideoFeedProps & react.RefAttributes<VideoFeedRef>>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* usePreloader - Feed-specific video preloading layer
|
|
59
|
+
*
|
|
60
|
+
* ARCHITECTURE:
|
|
61
|
+
* This module re-exports core preload functionality from @xhub-reel/player-core
|
|
62
|
+
* and adds FEED-SPECIFIC abstractions:
|
|
63
|
+
* - PreloadPriority type (high/medium/low/metadata/none)
|
|
64
|
+
* - Priority calculation helpers based on video index distance
|
|
65
|
+
*
|
|
66
|
+
* LAYERED DESIGN (Big Tech Pattern):
|
|
67
|
+
* ┌─────────────────────────────────────────┐
|
|
68
|
+
* │ @xhub-reel/feed (Domain Layer) │
|
|
69
|
+
* │ - Feed-specific priority enum │
|
|
70
|
+
* │ - Distance-based priority calculation │
|
|
71
|
+
* └──────────────┬──────────────────────────┘
|
|
72
|
+
* │ re-exports + extends
|
|
73
|
+
* ↓
|
|
74
|
+
* ┌─────────────────────────────────────────┐
|
|
75
|
+
* │ @xhub-reel/player (UI Layer) │
|
|
76
|
+
* │ - Re-exports core + UI components │
|
|
77
|
+
* └──────────────┬──────────────────────────┘
|
|
78
|
+
* │ re-exports
|
|
79
|
+
* ↓
|
|
80
|
+
* ┌─────────────────────────────────────────┐
|
|
81
|
+
* │ @xhub-reel/player-core (Core Layer) │
|
|
82
|
+
* │ - usePreload hook │
|
|
83
|
+
* │ - PreloadManager service │
|
|
84
|
+
* │ - Generic, reusable logic │
|
|
85
|
+
* └─────────────────────────────────────────┘
|
|
86
|
+
*
|
|
87
|
+
* FEED PRELOAD STRATEGY:
|
|
88
|
+
* - Current - 1: Keep in memory, paused
|
|
89
|
+
* - Current: Playing
|
|
90
|
+
* - Current + 1: Pre-load first 3 segments (high)
|
|
91
|
+
* - Current + 2: Pre-load first segment (medium)
|
|
92
|
+
* - Current + 3: Fetch metadata only (low)
|
|
93
|
+
* - Current ± 4+: Dispose (none)
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Priority levels for feed preloading
|
|
98
|
+
*/
|
|
99
|
+
type PreloadPriority = 'high' | 'medium' | 'low' | 'metadata' | 'none';
|
|
100
|
+
/**
|
|
101
|
+
* Calculate preload priority based on distance from current video
|
|
102
|
+
*
|
|
103
|
+
* @param index - Video index to check
|
|
104
|
+
* @param currentIndex - Current active video index
|
|
105
|
+
* @returns Numeric priority (1 = highest, 10 = none/dispose)
|
|
106
|
+
*/
|
|
107
|
+
declare function getPreloadPriorityForFeed(index: number, currentIndex: number): number;
|
|
108
|
+
/**
|
|
109
|
+
* Map PreloadPriority enum to numeric priority
|
|
110
|
+
*
|
|
111
|
+
* @param priority - Priority enum
|
|
112
|
+
* @returns Numeric priority for PreloadManager
|
|
113
|
+
*/
|
|
114
|
+
declare function mapPriorityToNumeric(priority: PreloadPriority): number;
|
|
115
|
+
/**
|
|
116
|
+
* Get PreloadPriority enum based on distance from current
|
|
117
|
+
*
|
|
118
|
+
* @param index - Video index to check
|
|
119
|
+
* @param currentIndex - Current active video index
|
|
120
|
+
* @returns PreloadPriority enum
|
|
121
|
+
*/
|
|
122
|
+
declare function getPreloadPriority(index: number, currentIndex: number): PreloadPriority;
|
|
123
|
+
|
|
124
|
+
interface VideoFeedItemProps {
|
|
125
|
+
/** Video data */
|
|
126
|
+
video: Video;
|
|
127
|
+
/** Whether this is the currently active video */
|
|
128
|
+
isActive?: boolean;
|
|
129
|
+
/** Preload priority */
|
|
130
|
+
priority?: PreloadPriority;
|
|
131
|
+
/** Show timeline (default: true, only used with default children) */
|
|
132
|
+
showTimeline?: boolean;
|
|
133
|
+
/** Called when video is liked */
|
|
134
|
+
onLike?: () => void;
|
|
135
|
+
/** Called when comments button is pressed */
|
|
136
|
+
onComment?: () => void;
|
|
137
|
+
/** Called when share button is pressed */
|
|
138
|
+
onShare?: () => void;
|
|
139
|
+
/** Called when author is clicked */
|
|
140
|
+
onAuthorClick?: () => void;
|
|
141
|
+
/** Custom styles override */
|
|
142
|
+
style?: CSSProperties;
|
|
143
|
+
/** Custom className (for external CSS if needed) */
|
|
144
|
+
className?: string;
|
|
145
|
+
/** Custom children for compound component pattern */
|
|
146
|
+
children?: ReactNode;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* VideoFeedItem - Root container for video feed items
|
|
150
|
+
*
|
|
151
|
+
* Provides context for all child components.
|
|
152
|
+
* Use with VideoFeedItemPlayer, VideoFeedItemActions, etc.
|
|
153
|
+
*/
|
|
154
|
+
declare const VideoFeedItem: react.ForwardRefExoticComponent<VideoFeedItemProps & react.RefAttributes<HTMLDivElement>>;
|
|
155
|
+
|
|
156
|
+
interface VideoFeedItemPlayerProps extends HTMLAttributes<HTMLDivElement> {
|
|
157
|
+
/** Custom placeholder element */
|
|
158
|
+
placeholder?: React.ReactNode;
|
|
159
|
+
}
|
|
160
|
+
declare const VideoFeedItemPlayer: react.ForwardRefExoticComponent<VideoFeedItemPlayerProps & react.RefAttributes<HTMLVideoElement>>;
|
|
161
|
+
|
|
162
|
+
interface VideoFeedItemActionsProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onShare'> {
|
|
163
|
+
/** Override like callback from context */
|
|
164
|
+
onLike?: () => void;
|
|
165
|
+
/** Override comment callback from context */
|
|
166
|
+
onComment?: () => void;
|
|
167
|
+
/** Override share callback from context */
|
|
168
|
+
onShare?: () => void;
|
|
169
|
+
}
|
|
170
|
+
declare const VideoFeedItemActions: react.ForwardRefExoticComponent<VideoFeedItemActionsProps & react.RefAttributes<HTMLDivElement>>;
|
|
171
|
+
|
|
172
|
+
interface VideoFeedItemTimelineProps extends HTMLAttributes<HTMLDivElement> {
|
|
173
|
+
/** Override expanded state */
|
|
174
|
+
expanded?: boolean;
|
|
175
|
+
}
|
|
176
|
+
declare const VideoFeedItemTimeline: react.ForwardRefExoticComponent<VideoFeedItemTimelineProps & react.RefAttributes<HTMLDivElement>>;
|
|
177
|
+
|
|
178
|
+
interface VideoFeedItemOverlayProps extends HTMLAttributes<HTMLDivElement> {
|
|
179
|
+
/** Show play/pause overlay. Default: true */
|
|
180
|
+
showPlayPause?: boolean;
|
|
181
|
+
/** Show heart animation on double tap. Default: true */
|
|
182
|
+
showDoubleTapHeart?: boolean;
|
|
183
|
+
/** Show video info overlay (author, description). Default: true */
|
|
184
|
+
showVideoInfo?: boolean;
|
|
185
|
+
}
|
|
186
|
+
declare const VideoFeedItemOverlay: react.ForwardRefExoticComponent<VideoFeedItemOverlayProps & react.RefAttributes<HTMLDivElement>>;
|
|
187
|
+
|
|
188
|
+
interface VideoFeedItemContextValue {
|
|
189
|
+
video: Video;
|
|
190
|
+
isActive: boolean;
|
|
191
|
+
shouldRenderVideo: boolean;
|
|
192
|
+
preload: '' | 'none' | 'metadata' | 'auto';
|
|
193
|
+
/** Video has been preloaded and first frame is ready */
|
|
194
|
+
isPreloaded: boolean;
|
|
195
|
+
containerRef: RefObject<HTMLDivElement | null>;
|
|
196
|
+
videoRef: RefObject<HTMLVideoElement | null>;
|
|
197
|
+
isPlaying: boolean;
|
|
198
|
+
showPauseOverlay: boolean;
|
|
199
|
+
timelineExpanded: boolean;
|
|
200
|
+
play: () => Promise<void>;
|
|
201
|
+
pause: () => void;
|
|
202
|
+
seek: (time: number) => void;
|
|
203
|
+
setShowPauseOverlay: (show: boolean) => void;
|
|
204
|
+
setTimelineExpanded: (expanded: boolean) => void;
|
|
205
|
+
gestureBindings: () => Record<string, unknown>;
|
|
206
|
+
showHeart: boolean;
|
|
207
|
+
heartPosition: {
|
|
208
|
+
x: number;
|
|
209
|
+
y: number;
|
|
210
|
+
};
|
|
211
|
+
triggerHeart: (x: number, y: number) => void;
|
|
212
|
+
onLike?: () => void;
|
|
213
|
+
onComment?: () => void;
|
|
214
|
+
onShare?: () => void;
|
|
215
|
+
onAuthorClick?: () => void;
|
|
216
|
+
handleSeekStart: () => void;
|
|
217
|
+
handleSeekEnd: (time: number) => void;
|
|
218
|
+
}
|
|
219
|
+
declare function useVideoFeedItemContext(): VideoFeedItemContextValue;
|
|
220
|
+
|
|
221
|
+
interface VideoOverlayProps {
|
|
222
|
+
/** Video data */
|
|
223
|
+
video: Video;
|
|
224
|
+
/** Called when author is clicked */
|
|
225
|
+
onAuthorClick?: () => void;
|
|
226
|
+
/** Whether timeline is expanded (affects bottom padding) */
|
|
227
|
+
timelineExpanded?: boolean;
|
|
228
|
+
/** Custom styles override */
|
|
229
|
+
style?: CSSProperties;
|
|
230
|
+
/** Custom className */
|
|
231
|
+
className?: string;
|
|
232
|
+
}
|
|
233
|
+
declare function VideoOverlay({ video, timelineExpanded, style, className, }: VideoOverlayProps): react_jsx_runtime.JSX.Element;
|
|
234
|
+
|
|
235
|
+
interface ConnectedVideoFeedProps extends Omit<VideoFeedProps, 'videos' | 'isLoading' | 'hasMore' | 'onLoadMore'> {
|
|
236
|
+
/**
|
|
237
|
+
* XHubReelConfig for API connection
|
|
238
|
+
* Optional if wrapped in XHubReelProvider with config
|
|
239
|
+
*/
|
|
240
|
+
config?: XHubReelConfig;
|
|
241
|
+
/**
|
|
242
|
+
* User ID for user-specific feed
|
|
243
|
+
*/
|
|
244
|
+
userId?: string;
|
|
245
|
+
/**
|
|
246
|
+
* Tag/hashtag filter
|
|
247
|
+
*/
|
|
248
|
+
tag?: string;
|
|
249
|
+
/**
|
|
250
|
+
* Search query
|
|
251
|
+
*/
|
|
252
|
+
searchQuery?: string;
|
|
253
|
+
/**
|
|
254
|
+
* Number of videos per page
|
|
255
|
+
* @default 10
|
|
256
|
+
*/
|
|
257
|
+
pageSize?: number;
|
|
258
|
+
/**
|
|
259
|
+
* Initial videos to show while loading (optional)
|
|
260
|
+
*/
|
|
261
|
+
initialVideos?: Video[];
|
|
262
|
+
/**
|
|
263
|
+
* Called when videos are successfully fetched
|
|
264
|
+
*/
|
|
265
|
+
onFetchSuccess?: (videos: Video[]) => void;
|
|
266
|
+
/**
|
|
267
|
+
* Called when fetch fails
|
|
268
|
+
*/
|
|
269
|
+
onFetchError?: (error: Error) => void;
|
|
270
|
+
/**
|
|
271
|
+
* Render custom loading state
|
|
272
|
+
*/
|
|
273
|
+
renderLoading?: () => React.ReactNode;
|
|
274
|
+
/**
|
|
275
|
+
* Render custom error state
|
|
276
|
+
*/
|
|
277
|
+
renderError?: (error: Error, retry: () => void) => React.ReactNode;
|
|
278
|
+
/**
|
|
279
|
+
* Render custom empty state
|
|
280
|
+
*/
|
|
281
|
+
renderEmpty?: () => React.ReactNode;
|
|
282
|
+
}
|
|
283
|
+
declare const ConnectedVideoFeed: react.ForwardRefExoticComponent<ConnectedVideoFeedProps & react.RefAttributes<VideoFeedRef>>;
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* useVideoVisibility - Track video visibility using IntersectionObserver
|
|
287
|
+
*/
|
|
288
|
+
|
|
289
|
+
interface UseVideoVisibilityOptions {
|
|
290
|
+
/** Element to observe */
|
|
291
|
+
elementRef: RefObject<HTMLElement | null>;
|
|
292
|
+
/** Threshold to activate (default: 50%) */
|
|
293
|
+
activateThreshold?: number;
|
|
294
|
+
/** Threshold to deactivate (default: 30%) */
|
|
295
|
+
deactivateThreshold?: number;
|
|
296
|
+
/** Root margin */
|
|
297
|
+
rootMargin?: string;
|
|
298
|
+
/** Callback when visibility changes */
|
|
299
|
+
onVisibilityChange?: (isVisible: boolean, ratio: number) => void;
|
|
300
|
+
}
|
|
301
|
+
interface UseVideoVisibilityReturn {
|
|
302
|
+
isVisible: boolean;
|
|
303
|
+
isActive: boolean;
|
|
304
|
+
visibilityRatio: number;
|
|
305
|
+
}
|
|
306
|
+
declare function useVideoVisibility({ elementRef, activateThreshold, deactivateThreshold, rootMargin, onVisibilityChange, }: UseVideoVisibilityOptions): UseVideoVisibilityReturn;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* useVideoActivation - Control video play/pause based on isCurrentVideo prop
|
|
310
|
+
*
|
|
311
|
+
* For carousel/swipe feeds: Uses index-based activation from VideoFeed (no IntersectionObserver)
|
|
312
|
+
* For scroll feeds: Enable `trackVisibility` to get viewport-based analytics
|
|
313
|
+
*
|
|
314
|
+
* Note: For infinite scroll loading, use `useInfiniteScroll` instead.
|
|
315
|
+
* For standalone visibility tracking, use `useVideoVisibility` directly.
|
|
316
|
+
*/
|
|
317
|
+
|
|
318
|
+
interface UseVideoActivationOptions {
|
|
319
|
+
/** Video container element ref (required if trackVisibility is true) */
|
|
320
|
+
containerRef?: RefObject<HTMLElement | null>;
|
|
321
|
+
/** Video element ref */
|
|
322
|
+
videoRef: RefObject<HTMLVideoElement | null>;
|
|
323
|
+
/** Whether this video is the current active one (from parent feed) */
|
|
324
|
+
isCurrentVideo?: boolean;
|
|
325
|
+
/** Callback when video should activate */
|
|
326
|
+
onActivate?: () => void;
|
|
327
|
+
/** Callback when video should deactivate */
|
|
328
|
+
onDeactivate?: () => void;
|
|
329
|
+
/** Whether auto-activation is enabled */
|
|
330
|
+
autoActivate?: boolean;
|
|
331
|
+
/**
|
|
332
|
+
* Enable visibility tracking via IntersectionObserver
|
|
333
|
+
* Use this for:
|
|
334
|
+
* - Analytics (track how much of video is visible)
|
|
335
|
+
* - Scroll-based feeds (non-carousel)
|
|
336
|
+
* - Lazy loading based on viewport
|
|
337
|
+
* Default: false (carousel mode)
|
|
338
|
+
*/
|
|
339
|
+
trackVisibility?: boolean;
|
|
340
|
+
/** Callback for visibility changes (requires trackVisibility: true) */
|
|
341
|
+
onVisibilityChange?: (isVisible: boolean, ratio: number) => void;
|
|
342
|
+
}
|
|
343
|
+
interface UseVideoActivationReturn {
|
|
344
|
+
/** Whether this video is currently active */
|
|
345
|
+
isActive: boolean;
|
|
346
|
+
/** Whether video is visible in viewport (only when trackVisibility: true) */
|
|
347
|
+
isVisible: boolean;
|
|
348
|
+
/** Visibility ratio 0-1 (only when trackVisibility: true) */
|
|
349
|
+
visibilityRatio: number;
|
|
350
|
+
/** Manually activate the video */
|
|
351
|
+
activate: () => void;
|
|
352
|
+
/** Manually deactivate the video */
|
|
353
|
+
deactivate: () => void;
|
|
354
|
+
}
|
|
355
|
+
declare function useVideoActivation({ containerRef, videoRef, isCurrentVideo, onActivate, onDeactivate, autoActivate, trackVisibility, onVisibilityChange, }: UseVideoActivationOptions): UseVideoActivationReturn;
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Memory Manager - Control memory usage for video feed
|
|
359
|
+
*
|
|
360
|
+
* Limits:
|
|
361
|
+
* - Max 5 videos in DOM
|
|
362
|
+
* - Max 3 videos with decoded frames
|
|
363
|
+
* - Total memory cap 150MB
|
|
364
|
+
*/
|
|
365
|
+
interface MemoryState {
|
|
366
|
+
videosInDom: number;
|
|
367
|
+
decodedVideos: number;
|
|
368
|
+
estimatedMemoryMB: number;
|
|
369
|
+
isLowMemory: boolean;
|
|
370
|
+
}
|
|
371
|
+
interface VideoMemoryEntry {
|
|
372
|
+
videoId: string;
|
|
373
|
+
inDom: boolean;
|
|
374
|
+
hasDecodedFrames: boolean;
|
|
375
|
+
estimatedSizeMB: number;
|
|
376
|
+
lastAccessed: number;
|
|
377
|
+
}
|
|
378
|
+
declare class MemoryManager {
|
|
379
|
+
private entries;
|
|
380
|
+
private listeners;
|
|
381
|
+
private memoryWarningThreshold;
|
|
382
|
+
constructor();
|
|
383
|
+
/**
|
|
384
|
+
* Register a video
|
|
385
|
+
*/
|
|
386
|
+
register(videoId: string, estimatedSizeMB?: number): void;
|
|
387
|
+
/**
|
|
388
|
+
* Unregister a video
|
|
389
|
+
*/
|
|
390
|
+
unregister(videoId: string): void;
|
|
391
|
+
/**
|
|
392
|
+
* Mark video as in DOM
|
|
393
|
+
*/
|
|
394
|
+
setInDom(videoId: string, inDom: boolean): void;
|
|
395
|
+
/**
|
|
396
|
+
* Mark video as having decoded frames
|
|
397
|
+
*/
|
|
398
|
+
setHasDecodedFrames(videoId: string, hasFrames: boolean): void;
|
|
399
|
+
/**
|
|
400
|
+
* Get current memory state
|
|
401
|
+
*/
|
|
402
|
+
getState(): MemoryState;
|
|
403
|
+
/**
|
|
404
|
+
* Get videos that should be disposed (LRU)
|
|
405
|
+
*/
|
|
406
|
+
getVideosToDispose(): string[];
|
|
407
|
+
/**
|
|
408
|
+
* Subscribe to memory state changes
|
|
409
|
+
*/
|
|
410
|
+
subscribe(listener: (state: MemoryState) => void): () => void;
|
|
411
|
+
/**
|
|
412
|
+
* Force cleanup
|
|
413
|
+
*/
|
|
414
|
+
forceCleanup(): string[];
|
|
415
|
+
private notifyListeners;
|
|
416
|
+
private setupMemoryPressureListener;
|
|
417
|
+
}
|
|
418
|
+
declare const memoryManager: MemoryManager;
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* useMemoryManager - Hook for memory management in feed
|
|
422
|
+
*/
|
|
423
|
+
|
|
424
|
+
interface UseMemoryManagerOptions {
|
|
425
|
+
/** Video ID to track */
|
|
426
|
+
videoId: string;
|
|
427
|
+
/** Estimated video size in MB */
|
|
428
|
+
estimatedSizeMB?: number;
|
|
429
|
+
/** Callback when video should be disposed due to memory pressure */
|
|
430
|
+
onShouldDispose?: () => void;
|
|
431
|
+
}
|
|
432
|
+
interface UseMemoryManagerReturn {
|
|
433
|
+
memoryState: MemoryState;
|
|
434
|
+
setInDom: (inDom: boolean) => void;
|
|
435
|
+
setHasDecodedFrames: (hasFrames: boolean) => void;
|
|
436
|
+
shouldDispose: boolean;
|
|
437
|
+
}
|
|
438
|
+
declare function useMemoryManager({ videoId, estimatedSizeMB, onShouldDispose, }: UseMemoryManagerOptions): UseMemoryManagerReturn;
|
|
439
|
+
/**
|
|
440
|
+
* useGlobalMemoryState - Get global memory state without tracking a specific video
|
|
441
|
+
*/
|
|
442
|
+
declare function useGlobalMemoryState(): MemoryState;
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* useFeedScroll - Scroll management hook with snap behavior
|
|
446
|
+
*/
|
|
447
|
+
|
|
448
|
+
interface UseFeedScrollOptions {
|
|
449
|
+
/** Scroll container ref */
|
|
450
|
+
scrollRef: RefObject<HTMLElement | null>;
|
|
451
|
+
/** Total number of items */
|
|
452
|
+
itemCount: number;
|
|
453
|
+
/** Height of each item (default: viewport height) */
|
|
454
|
+
itemHeight?: number;
|
|
455
|
+
/** Callback when scroll position changes */
|
|
456
|
+
onScrollChange?: (scrollTop: number, velocity: number) => void;
|
|
457
|
+
/** Callback when current index changes */
|
|
458
|
+
onIndexChange?: (index: number) => void;
|
|
459
|
+
}
|
|
460
|
+
interface UseFeedScrollReturn {
|
|
461
|
+
currentIndex: number;
|
|
462
|
+
scrollVelocity: number;
|
|
463
|
+
isScrolling: boolean;
|
|
464
|
+
scrollToIndex: (index: number, smooth?: boolean) => void;
|
|
465
|
+
scrollToNext: () => void;
|
|
466
|
+
scrollToPrev: () => void;
|
|
467
|
+
}
|
|
468
|
+
declare function useFeedScroll({ scrollRef, itemCount, itemHeight, onScrollChange, onIndexChange, }: UseFeedScrollOptions): UseFeedScrollReturn;
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* useInfiniteScroll - Hook for infinite scroll loading
|
|
472
|
+
*/
|
|
473
|
+
interface UseInfiniteScrollOptions {
|
|
474
|
+
/** Called when more content should be loaded */
|
|
475
|
+
onLoadMore?: () => void | Promise<void>;
|
|
476
|
+
/** Whether currently loading */
|
|
477
|
+
isLoading?: boolean;
|
|
478
|
+
/** Whether there's more content to load */
|
|
479
|
+
hasMore?: boolean;
|
|
480
|
+
/** Number of items from end to trigger load */
|
|
481
|
+
threshold?: number;
|
|
482
|
+
/** Root margin for intersection observer */
|
|
483
|
+
rootMargin?: string;
|
|
484
|
+
}
|
|
485
|
+
interface UseInfiniteScrollReturn {
|
|
486
|
+
/** Ref to attach to sentinel element */
|
|
487
|
+
sentinelRef: (element: HTMLElement | null) => void;
|
|
488
|
+
/** Whether currently in loading state */
|
|
489
|
+
isLoadingMore: boolean;
|
|
490
|
+
}
|
|
491
|
+
declare function useInfiniteScroll({ onLoadMore, isLoading, hasMore, threshold: _threshold, rootMargin, }: UseInfiniteScrollOptions): UseInfiniteScrollReturn;
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* useVideoFeed - Hook for fetching videos from API with infinite scroll support
|
|
495
|
+
*
|
|
496
|
+
* Uses XHubReelProvider config to fetch videos. Falls back to manual mode if no config.
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* ```tsx
|
|
500
|
+
* // API Mode (requires XHubReelProvider with config)
|
|
501
|
+
* function FeedPage() {
|
|
502
|
+
* const {
|
|
503
|
+
* videos,
|
|
504
|
+
* isLoading,
|
|
505
|
+
* hasMore,
|
|
506
|
+
* fetchNextPage,
|
|
507
|
+
* error,
|
|
508
|
+
* } = useVideoFeed()
|
|
509
|
+
*
|
|
510
|
+
* return <VideoFeed videos={videos} onLoadMore={fetchNextPage} />
|
|
511
|
+
* }
|
|
512
|
+
*
|
|
513
|
+
* // Manual Mode (pass videos directly)
|
|
514
|
+
* function FeedPage() {
|
|
515
|
+
* const localVideos = [...]
|
|
516
|
+
* return <VideoFeed videos={localVideos} />
|
|
517
|
+
* }
|
|
518
|
+
* ```
|
|
519
|
+
*/
|
|
520
|
+
|
|
521
|
+
interface UseVideoFeedOptions {
|
|
522
|
+
/**
|
|
523
|
+
* XHubReelConfig - required for API mode
|
|
524
|
+
* If not provided, the hook returns empty data (use manual mode)
|
|
525
|
+
*/
|
|
526
|
+
config?: XHubReelConfig;
|
|
527
|
+
/**
|
|
528
|
+
* Enable/disable the query (default: true)
|
|
529
|
+
*/
|
|
530
|
+
enabled?: boolean;
|
|
531
|
+
/**
|
|
532
|
+
* Initial videos to show while loading
|
|
533
|
+
*/
|
|
534
|
+
initialVideos?: Video[];
|
|
535
|
+
/**
|
|
536
|
+
* Stale time in ms (default: 5 minutes)
|
|
537
|
+
*/
|
|
538
|
+
staleTime?: number;
|
|
539
|
+
/**
|
|
540
|
+
* Refetch on window focus (default: false)
|
|
541
|
+
*/
|
|
542
|
+
refetchOnWindowFocus?: boolean;
|
|
543
|
+
/**
|
|
544
|
+
* Callback when videos are fetched
|
|
545
|
+
*/
|
|
546
|
+
onSuccess?: (videos: Video[]) => void;
|
|
547
|
+
/**
|
|
548
|
+
* Callback on error
|
|
549
|
+
*/
|
|
550
|
+
onError?: (error: Error) => void;
|
|
551
|
+
/** User ID filter */
|
|
552
|
+
userId?: string;
|
|
553
|
+
/** Tag/hashtag filter */
|
|
554
|
+
tag?: string;
|
|
555
|
+
/** Search query */
|
|
556
|
+
searchQuery?: string;
|
|
557
|
+
/** Number of videos per page */
|
|
558
|
+
limit?: number;
|
|
559
|
+
/** Pagination cursor */
|
|
560
|
+
cursor?: string;
|
|
561
|
+
}
|
|
562
|
+
interface UseVideoFeedReturn {
|
|
563
|
+
/** Flattened array of all fetched videos */
|
|
564
|
+
videos: Video[];
|
|
565
|
+
/** Loading state for initial fetch */
|
|
566
|
+
isLoading: boolean;
|
|
567
|
+
/** Loading state for fetching more */
|
|
568
|
+
isFetchingMore: boolean;
|
|
569
|
+
/** Whether there are more videos to load */
|
|
570
|
+
hasMore: boolean;
|
|
571
|
+
/** Fetch next page of videos */
|
|
572
|
+
fetchNextPage: () => Promise<void>;
|
|
573
|
+
/** Refetch all videos */
|
|
574
|
+
refetch: () => Promise<void>;
|
|
575
|
+
/** Error if any */
|
|
576
|
+
error: Error | null;
|
|
577
|
+
/** Whether API mode is active */
|
|
578
|
+
isApiMode: boolean;
|
|
579
|
+
/** Total count of videos (if provided by API) */
|
|
580
|
+
totalCount?: number;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* useVideoFeed - Fetch videos with infinite scroll support
|
|
584
|
+
*/
|
|
585
|
+
declare function useVideoFeed(options?: UseVideoFeedOptions): UseVideoFeedReturn;
|
|
586
|
+
/**
|
|
587
|
+
* Prefetch videos for a feed type
|
|
588
|
+
* Call this in a Server Component or before navigation to pre-warm cache
|
|
589
|
+
*
|
|
590
|
+
* @example
|
|
591
|
+
* ```tsx
|
|
592
|
+
* // In a page component
|
|
593
|
+
* export async function generateStaticParams() {
|
|
594
|
+
* const queryClient = new QueryClient()
|
|
595
|
+
* await prefetchVideoFeed(queryClient, config)
|
|
596
|
+
* return { props: { dehydratedState: dehydrate(queryClient) } }
|
|
597
|
+
* }
|
|
598
|
+
* ```
|
|
599
|
+
*/
|
|
600
|
+
declare function prefetchVideoFeed(queryClient: ReturnType<typeof useQueryClient>, config: XHubReelConfig, params?: VideoFetchParams): Promise<void>;
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* useSwipeAnimation - High-performance swipe animation hook
|
|
604
|
+
*
|
|
605
|
+
* Features:
|
|
606
|
+
* - Direct DOM manipulation (bypasses React re-renders)
|
|
607
|
+
* - RAF batching (max 1 update per frame)
|
|
608
|
+
* - Cached viewport height (no layout thrashing)
|
|
609
|
+
* - transitionend event (no setTimeout race conditions)
|
|
610
|
+
* - Design system easing curve
|
|
611
|
+
*
|
|
612
|
+
* @example
|
|
613
|
+
* ```tsx
|
|
614
|
+
* const { setTranslateY, animateTo, snapBack, viewportHeight } = useSwipeAnimation({
|
|
615
|
+
* trackRef,
|
|
616
|
+
* transitionDuration: 300,
|
|
617
|
+
* })
|
|
618
|
+
*
|
|
619
|
+
* // During swipe (hot path - no React re-render)
|
|
620
|
+
* setTranslateY(movement)
|
|
621
|
+
*
|
|
622
|
+
* // Complete swipe with animation
|
|
623
|
+
* await animateTo(-viewportHeight)
|
|
624
|
+
* ```
|
|
625
|
+
*/
|
|
626
|
+
interface UseSwipeAnimationOptions {
|
|
627
|
+
/** Ref to the track element that will be animated */
|
|
628
|
+
trackRef: React.RefObject<HTMLDivElement | null>;
|
|
629
|
+
/** Transition duration in ms. Default: 300 */
|
|
630
|
+
transitionDuration?: number;
|
|
631
|
+
/** CSS easing function. Default: design system spring */
|
|
632
|
+
easing?: string;
|
|
633
|
+
/** Called when any transition completes */
|
|
634
|
+
onTransitionEnd?: () => void;
|
|
635
|
+
}
|
|
636
|
+
interface UseSwipeAnimationReturn {
|
|
637
|
+
/** Set translateY directly (no React, RAF batched) - use during swipe */
|
|
638
|
+
setTranslateY: (y: number) => void;
|
|
639
|
+
/** Animate to target Y with CSS transition - returns Promise */
|
|
640
|
+
animateTo: (y: number) => Promise<void>;
|
|
641
|
+
/** Snap back to 0 with transition */
|
|
642
|
+
snapBack: () => Promise<void>;
|
|
643
|
+
/** Get current translateY value (sync, no DOM read) */
|
|
644
|
+
getCurrentY: () => number;
|
|
645
|
+
/** Cached viewport height - use this instead of window.innerHeight */
|
|
646
|
+
viewportHeight: number;
|
|
647
|
+
/** Whether currently animating */
|
|
648
|
+
isAnimating: boolean;
|
|
649
|
+
}
|
|
650
|
+
declare function useSwipeAnimation({ trackRef, transitionDuration, easing, onTransitionEnd, }: UseSwipeAnimationOptions): UseSwipeAnimationReturn;
|
|
651
|
+
|
|
652
|
+
export { ConnectedVideoFeed, type ConnectedVideoFeedProps, type MemoryState, type PreloadPriority, type UseFeedScrollOptions, type UseFeedScrollReturn, type UseInfiniteScrollOptions, type UseInfiniteScrollReturn, type UseMemoryManagerOptions, type UseMemoryManagerReturn, type UseSwipeAnimationOptions, type UseSwipeAnimationReturn, type UseVideoActivationOptions, type UseVideoActivationReturn, type UseVideoFeedOptions, type UseVideoFeedReturn, type UseVideoVisibilityOptions, type UseVideoVisibilityReturn, VideoFeed, VideoFeedItem, VideoFeedItemActions, type VideoFeedItemActionsProps, type VideoFeedItemContextValue, VideoFeedItemOverlay, type VideoFeedItemOverlayProps, VideoFeedItemPlayer, type VideoFeedItemPlayerProps, type VideoFeedItemProps, VideoFeedItemTimeline, type VideoFeedItemTimelineProps, type VideoFeedProps, type VideoFeedRef, type VideoMemoryEntry, VideoOverlay, type VideoOverlayProps, getPreloadPriority, getPreloadPriorityForFeed, mapPriorityToNumeric, memoryManager, prefetchVideoFeed, useFeedScroll, useGlobalMemoryState, useInfiniteScroll, useMemoryManager, useSwipeAnimation, useVideoActivation, useVideoFeed, useVideoFeedItemContext, useVideoVisibility };
|