@xhub-short/core 0.1.0-beta.0 → 0.1.0-beta.2
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/dist/index.d.ts +456 -5
- package/dist/index.js +1001 -11
- package/package.json +5 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VideoItem, IDataSource, IAnalytics, ILogger, ISessionStorage, SessionSnapshot, INetworkAdapter, IVideoLoader, IPosterLoader, VideoSource, IInteraction } from '@xhub-short/contracts';
|
|
1
|
+
import { VideoItem, IDataSource, IStorage, PrefetchCacheData, IAnalytics, ILogger, ISessionStorage, SessionSnapshot, INetworkAdapter, IVideoLoader, IPosterLoader, VideoSource, IInteraction, CommentItem, ReplyItem, ICommentAdapter, InternalLogger, CommentAuthor } from '@xhub-short/contracts';
|
|
2
2
|
export { SessionSnapshot } from '@xhub-short/contracts';
|
|
3
3
|
import { StoreApi } from 'zustand/vanilla';
|
|
4
4
|
|
|
@@ -68,6 +68,58 @@ interface FeedConfig {
|
|
|
68
68
|
* Default feed configuration
|
|
69
69
|
*/
|
|
70
70
|
declare const DEFAULT_FEED_CONFIG: Required<FeedConfig>;
|
|
71
|
+
/**
|
|
72
|
+
* Configuration for prefetch cache feature
|
|
73
|
+
*
|
|
74
|
+
* Enables instant feed loading on fresh app open by caching
|
|
75
|
+
* the last N videos and showing them immediately while
|
|
76
|
+
* fresh data is fetched in the background.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const prefetchConfig: PrefetchCacheConfig = {
|
|
81
|
+
* enabled: true,
|
|
82
|
+
* maxVideos: 15,
|
|
83
|
+
* enableDynamicEviction: true,
|
|
84
|
+
* evictionThrottleMs: 2000,
|
|
85
|
+
* };
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
interface PrefetchCacheConfig {
|
|
89
|
+
/**
|
|
90
|
+
* Enable prefetch cache for instant loading
|
|
91
|
+
* @default true
|
|
92
|
+
*/
|
|
93
|
+
enabled: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Maximum number of videos to cache
|
|
96
|
+
* Higher = more instant content, but more storage
|
|
97
|
+
* @default 10
|
|
98
|
+
*/
|
|
99
|
+
maxVideos: number;
|
|
100
|
+
/**
|
|
101
|
+
* Storage key for prefetch cache
|
|
102
|
+
* @default 'sv-prefetch-cache'
|
|
103
|
+
*/
|
|
104
|
+
storageKey: string;
|
|
105
|
+
/**
|
|
106
|
+
* Enable dynamic cache eviction
|
|
107
|
+
* When user scrolls past cached videos, remove them from cache
|
|
108
|
+
* Prevents user from rewatching same videos on reload
|
|
109
|
+
* @default true
|
|
110
|
+
*/
|
|
111
|
+
enableDynamicEviction: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Throttle duration (ms) for dynamic eviction
|
|
114
|
+
* Reduces storage writes when user scrolls quickly
|
|
115
|
+
* @default 2000
|
|
116
|
+
*/
|
|
117
|
+
evictionThrottleMs: number;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Default prefetch cache configuration
|
|
121
|
+
*/
|
|
122
|
+
declare const DEFAULT_PREFETCH_CACHE_CONFIG: PrefetchCacheConfig;
|
|
71
123
|
|
|
72
124
|
/**
|
|
73
125
|
* FeedManager - Manages video feed data with zustand/vanilla store
|
|
@@ -101,6 +153,10 @@ declare class FeedManager {
|
|
|
101
153
|
readonly store: StoreApi<FeedState>;
|
|
102
154
|
/** Resolved configuration */
|
|
103
155
|
private readonly config;
|
|
156
|
+
/** Prefetch cache configuration */
|
|
157
|
+
private readonly prefetchConfig;
|
|
158
|
+
/** Storage adapter for prefetch cache (optional) */
|
|
159
|
+
private readonly storage;
|
|
104
160
|
/** Abort controller for cancelling in-flight requests */
|
|
105
161
|
private abortController;
|
|
106
162
|
/**
|
|
@@ -113,7 +169,7 @@ declare class FeedManager {
|
|
|
113
169
|
* Used for garbage collection
|
|
114
170
|
*/
|
|
115
171
|
private accessOrder;
|
|
116
|
-
constructor(dataSource: IDataSource, config?: FeedConfig);
|
|
172
|
+
constructor(dataSource: IDataSource, config?: FeedConfig, storage?: IStorage, prefetchConfig?: Partial<PrefetchCacheConfig>);
|
|
117
173
|
/**
|
|
118
174
|
* Load initial feed data
|
|
119
175
|
*
|
|
@@ -179,6 +235,61 @@ declare class FeedManager {
|
|
|
179
235
|
* Destroy the manager and cleanup
|
|
180
236
|
*/
|
|
181
237
|
destroy(): void;
|
|
238
|
+
/**
|
|
239
|
+
* Hydrate feed from a session snapshot
|
|
240
|
+
*
|
|
241
|
+
* Used by LifecycleManager to restore state without API call.
|
|
242
|
+
* This bypasses normal data flow for state restoration.
|
|
243
|
+
*
|
|
244
|
+
* @param items - Video items from snapshot
|
|
245
|
+
* @param cursor - Pagination cursor from snapshot
|
|
246
|
+
* @param options - Additional hydration options
|
|
247
|
+
*/
|
|
248
|
+
hydrateFromSnapshot(items: VideoItem[], cursor: string | null, options?: {
|
|
249
|
+
/** Whether to mark data as stale for background revalidation */
|
|
250
|
+
markAsStale?: boolean;
|
|
251
|
+
}): void;
|
|
252
|
+
/**
|
|
253
|
+
* Update prefetch cache with current feed tail
|
|
254
|
+
* Called automatically after loadInitial() and loadMore()
|
|
255
|
+
*
|
|
256
|
+
* Strategy: Cache the LAST N videos (tail of feed)
|
|
257
|
+
* These are videos user hasn't seen yet, perfect for instant display
|
|
258
|
+
*/
|
|
259
|
+
updatePrefetchCache(): void;
|
|
260
|
+
/**
|
|
261
|
+
* Save prefetch cache to storage (async, non-blocking)
|
|
262
|
+
*/
|
|
263
|
+
private savePrefetchCacheAsync;
|
|
264
|
+
/**
|
|
265
|
+
* Load prefetch cache from storage
|
|
266
|
+
* Returns null if no cache, disabled, or storage error
|
|
267
|
+
*/
|
|
268
|
+
loadPrefetchCache(): Promise<PrefetchCacheData | null>;
|
|
269
|
+
/**
|
|
270
|
+
* Hydrate feed from prefetch cache for instant display
|
|
271
|
+
* Marks data as stale to trigger background revalidation
|
|
272
|
+
*/
|
|
273
|
+
hydrateFromPrefetchCache(cache: PrefetchCacheData): void;
|
|
274
|
+
/**
|
|
275
|
+
* Clear prefetch cache
|
|
276
|
+
* Call when user logs out or data should be invalidated
|
|
277
|
+
*/
|
|
278
|
+
clearPrefetchCache(): Promise<void>;
|
|
279
|
+
/**
|
|
280
|
+
* Evict videos that user has scrolled past from prefetch cache
|
|
281
|
+
* Called when user's focusedIndex changes
|
|
282
|
+
*
|
|
283
|
+
* Strategy: Remove all videos at or before current position
|
|
284
|
+
* This ensures user doesn't rewatch videos on reload
|
|
285
|
+
*
|
|
286
|
+
* @param currentIndex - Current focused video index in feed
|
|
287
|
+
*/
|
|
288
|
+
evictViewedVideosFromCache(currentIndex: number): Promise<void>;
|
|
289
|
+
/**
|
|
290
|
+
* Get prefetch cache configuration (for external access)
|
|
291
|
+
*/
|
|
292
|
+
getPrefetchConfig(): PrefetchCacheConfig;
|
|
182
293
|
/**
|
|
183
294
|
* Fetch with exponential backoff retry
|
|
184
295
|
*/
|
|
@@ -274,6 +385,8 @@ interface PlayerState {
|
|
|
274
385
|
status: PlayerStatus;
|
|
275
386
|
/** Currently loaded video */
|
|
276
387
|
currentVideo: VideoItem | null;
|
|
388
|
+
/** Current video ID (convenience getter for currentVideo?.id) */
|
|
389
|
+
currentVideoId: string | null;
|
|
277
390
|
/** Current playback time in seconds */
|
|
278
391
|
currentTime: number;
|
|
279
392
|
/** Video duration in seconds */
|
|
@@ -294,6 +407,29 @@ interface PlayerState {
|
|
|
294
407
|
error: PlayerError | null;
|
|
295
408
|
/** Whether video has ended */
|
|
296
409
|
ended: boolean;
|
|
410
|
+
/**
|
|
411
|
+
* Pending restore position for session restore.
|
|
412
|
+
*
|
|
413
|
+
* When set, the next video with matching `pendingRestoreVideoId`
|
|
414
|
+
* should start at this position (using #t= or hls.startPosition).
|
|
415
|
+
*
|
|
416
|
+
* Cleared after being consumed by VideoPlayer.
|
|
417
|
+
*/
|
|
418
|
+
pendingRestoreTime: number | null;
|
|
419
|
+
/**
|
|
420
|
+
* Video ID for pending restore position.
|
|
421
|
+
*
|
|
422
|
+
* Used to match the correct video when restoring session.
|
|
423
|
+
* Only the video with this ID should use `pendingRestoreTime`.
|
|
424
|
+
*/
|
|
425
|
+
pendingRestoreVideoId: string | null;
|
|
426
|
+
/**
|
|
427
|
+
* Captured video frame at restore position (base64 JPEG).
|
|
428
|
+
*
|
|
429
|
+
* Displayed as instant preview while video loads.
|
|
430
|
+
* Cleared after being consumed by VideoPlayer/VideoSlot.
|
|
431
|
+
*/
|
|
432
|
+
pendingRestoreFrame: string | null;
|
|
297
433
|
}
|
|
298
434
|
/**
|
|
299
435
|
* PlayerEngine configuration
|
|
@@ -537,6 +673,60 @@ declare class PlayerEngine {
|
|
|
537
673
|
* Handle video error
|
|
538
674
|
*/
|
|
539
675
|
onError(error: Error, mediaError?: MediaError): void;
|
|
676
|
+
/**
|
|
677
|
+
* Set restore position for session restore.
|
|
678
|
+
*
|
|
679
|
+
* When the video with matching ID loads, VideoPlayer should
|
|
680
|
+
* use this position as startTime (via #t= or hls.startPosition).
|
|
681
|
+
*
|
|
682
|
+
* @param videoId - Video ID that should use this restore position
|
|
683
|
+
* @param time - Playback position in seconds
|
|
684
|
+
*
|
|
685
|
+
* @example
|
|
686
|
+
* ```typescript
|
|
687
|
+
* // During session restore
|
|
688
|
+
* if (result.playbackTime && result.currentVideoId) {
|
|
689
|
+
* playerEngine.setRestorePosition(result.currentVideoId, result.playbackTime, result.restoreFrame);
|
|
690
|
+
* }
|
|
691
|
+
* ```
|
|
692
|
+
*/
|
|
693
|
+
setRestorePosition(videoId: string, time: number, frame?: string): void;
|
|
694
|
+
/**
|
|
695
|
+
* Get and clear restore position for a video.
|
|
696
|
+
*
|
|
697
|
+
* VideoPlayer calls this when rendering to get startTime.
|
|
698
|
+
* Position is cleared after being read to prevent reuse.
|
|
699
|
+
* Note: restoreFrame is NOT cleared here - use consumeRestoreFrame() separately.
|
|
700
|
+
*
|
|
701
|
+
* @param videoId - Video ID to check
|
|
702
|
+
* @returns Restore time in seconds, or null if no pending restore
|
|
703
|
+
*/
|
|
704
|
+
consumeRestorePosition(videoId: string): number | null;
|
|
705
|
+
/**
|
|
706
|
+
* Get and clear restore frame for a video.
|
|
707
|
+
*
|
|
708
|
+
* VideoSlot calls this to display instant preview while video loads.
|
|
709
|
+
* Frame is cleared after being read to free memory.
|
|
710
|
+
*
|
|
711
|
+
* @param videoId - Video ID to check
|
|
712
|
+
* @returns Base64 JPEG data URL, or null if no pending frame
|
|
713
|
+
*/
|
|
714
|
+
consumeRestoreFrame(videoId: string): string | null;
|
|
715
|
+
/**
|
|
716
|
+
* Get restore frame without consuming (peek).
|
|
717
|
+
* Used to display preview while keeping it available.
|
|
718
|
+
*
|
|
719
|
+
* @param videoId - Video ID to check
|
|
720
|
+
* @returns Base64 JPEG data URL, or null if no pending frame
|
|
721
|
+
*/
|
|
722
|
+
peekRestoreFrame(videoId: string): string | null;
|
|
723
|
+
/**
|
|
724
|
+
* Check if there's a pending restore position for a video.
|
|
725
|
+
*
|
|
726
|
+
* @param videoId - Video ID to check
|
|
727
|
+
* @returns True if there's a pending restore position
|
|
728
|
+
*/
|
|
729
|
+
hasPendingRestorePosition(videoId: string): boolean;
|
|
540
730
|
/**
|
|
541
731
|
* Reset player to initial state
|
|
542
732
|
*/
|
|
@@ -697,6 +887,8 @@ interface RestoreResult {
|
|
|
697
887
|
playbackTime?: number;
|
|
698
888
|
/** Video ID that was playing (only present if restorePlaybackPosition config is enabled) */
|
|
699
889
|
currentVideoId?: string;
|
|
890
|
+
/** Captured video frame at playback position (base64 JPEG, only present if restorePlaybackPosition is enabled) */
|
|
891
|
+
restoreFrame?: string;
|
|
700
892
|
}
|
|
701
893
|
/**
|
|
702
894
|
* LifecycleManager configuration
|
|
@@ -819,6 +1011,7 @@ declare class LifecycleManager {
|
|
|
819
1011
|
* @param data - Snapshot data to save
|
|
820
1012
|
* @param data.playbackTime - Current video playback position (only saved if restorePlaybackPosition config is enabled)
|
|
821
1013
|
* @param data.currentVideoId - Current video ID (only saved if restorePlaybackPosition config is enabled)
|
|
1014
|
+
* @param data.restoreFrame - Captured video frame at playback position (only saved if restorePlaybackPosition is enabled)
|
|
822
1015
|
*/
|
|
823
1016
|
saveSnapshot(data: {
|
|
824
1017
|
items: VideoItem[];
|
|
@@ -829,6 +1022,8 @@ declare class LifecycleManager {
|
|
|
829
1022
|
playbackTime?: number;
|
|
830
1023
|
/** Current video ID (only used when restorePlaybackPosition is enabled) */
|
|
831
1024
|
currentVideoId?: string;
|
|
1025
|
+
/** Captured video frame at playback position (only used when restorePlaybackPosition is enabled) */
|
|
1026
|
+
restoreFrame?: string;
|
|
832
1027
|
}): Promise<boolean>;
|
|
833
1028
|
/**
|
|
834
1029
|
* Clear saved snapshot
|
|
@@ -1352,6 +1547,10 @@ declare class OptimisticManager {
|
|
|
1352
1547
|
private readonly eventListeners;
|
|
1353
1548
|
/** Retry timer */
|
|
1354
1549
|
private retryTimer;
|
|
1550
|
+
/** Debounce timers for like/unlike per video */
|
|
1551
|
+
private readonly likeDebounceTimers;
|
|
1552
|
+
/** Debounce delay in ms */
|
|
1553
|
+
private readonly debounceDelay;
|
|
1355
1554
|
constructor(config?: OptimisticConfig);
|
|
1356
1555
|
/**
|
|
1357
1556
|
* Like a video with optimistic update
|
|
@@ -1362,9 +1561,26 @@ declare class OptimisticManager {
|
|
|
1362
1561
|
*/
|
|
1363
1562
|
unlike(videoId: string): Promise<boolean>;
|
|
1364
1563
|
/**
|
|
1365
|
-
* Toggle like state (like if not liked, unlike if liked)
|
|
1564
|
+
* Toggle like state with DEBOUNCE (like if not liked, unlike if liked)
|
|
1565
|
+
*
|
|
1566
|
+
* This method:
|
|
1567
|
+
* 1. Updates UI immediately (optimistic)
|
|
1568
|
+
* 2. Debounces API call - only sends after user stops clicking
|
|
1569
|
+
* 3. Sends final state to API after debounce delay
|
|
1570
|
+
*
|
|
1571
|
+
* Perfect for rapid tapping like TikTok/Instagram behavior.
|
|
1366
1572
|
*/
|
|
1367
|
-
toggleLike(videoId: string):
|
|
1573
|
+
toggleLike(videoId: string): void;
|
|
1574
|
+
/**
|
|
1575
|
+
* Execute the actual API call after debounce
|
|
1576
|
+
* Reads current state from FeedManager to get final intended state
|
|
1577
|
+
*/
|
|
1578
|
+
private executeDebouncedLikeApi;
|
|
1579
|
+
/**
|
|
1580
|
+
* @deprecated Use toggleLike() instead - it now includes debounce
|
|
1581
|
+
* Legacy toggle that waits for API response
|
|
1582
|
+
*/
|
|
1583
|
+
toggleLikeSync(videoId: string): Promise<boolean>;
|
|
1368
1584
|
/**
|
|
1369
1585
|
* Follow a video author with optimistic update
|
|
1370
1586
|
*/
|
|
@@ -1383,6 +1599,7 @@ declare class OptimisticManager {
|
|
|
1383
1599
|
getPendingActions(): PendingAction[];
|
|
1384
1600
|
/**
|
|
1385
1601
|
* Check if there's a pending action for a video
|
|
1602
|
+
* Only returns true for actions with status 'pending' (not 'failed')
|
|
1386
1603
|
*/
|
|
1387
1604
|
hasPendingAction(videoId: string, type?: ActionType): boolean;
|
|
1388
1605
|
/**
|
|
@@ -1463,4 +1680,238 @@ declare class OptimisticManager {
|
|
|
1463
1680
|
private emitEvent;
|
|
1464
1681
|
}
|
|
1465
1682
|
|
|
1466
|
-
|
|
1683
|
+
/**
|
|
1684
|
+
* Comment state for a single video in zustand store
|
|
1685
|
+
*/
|
|
1686
|
+
interface VideoCommentState {
|
|
1687
|
+
/** Normalized comments - Map for O(1) lookup */
|
|
1688
|
+
commentsById: Map<string, CommentItem>;
|
|
1689
|
+
/** Ordered list of comment IDs for rendering */
|
|
1690
|
+
displayOrder: string[];
|
|
1691
|
+
/** Total comment count from API */
|
|
1692
|
+
totalCount: number;
|
|
1693
|
+
/** Cursor for pagination */
|
|
1694
|
+
cursor: string | null;
|
|
1695
|
+
/** Whether more comments can be loaded */
|
|
1696
|
+
hasMore: boolean;
|
|
1697
|
+
/** Initial loading state */
|
|
1698
|
+
loading: boolean;
|
|
1699
|
+
/** Loading more (pagination) state */
|
|
1700
|
+
loadingMore: boolean;
|
|
1701
|
+
/** Error state */
|
|
1702
|
+
error: CommentError | null;
|
|
1703
|
+
/** Cache timestamp for TTL */
|
|
1704
|
+
cachedAt: number;
|
|
1705
|
+
/** Whether data is stale */
|
|
1706
|
+
isStale: boolean;
|
|
1707
|
+
}
|
|
1708
|
+
/**
|
|
1709
|
+
* Global comment manager state
|
|
1710
|
+
*/
|
|
1711
|
+
interface CommentManagerState {
|
|
1712
|
+
/** Comments by video ID */
|
|
1713
|
+
byVideoId: Map<string, VideoCommentState>;
|
|
1714
|
+
/** Currently active video ID (for comment sheet) */
|
|
1715
|
+
activeVideoId: string | null;
|
|
1716
|
+
/** Posting state */
|
|
1717
|
+
isPosting: boolean;
|
|
1718
|
+
/** Posting error */
|
|
1719
|
+
postError: CommentError | null;
|
|
1720
|
+
/** Deleting comment ID (for optimistic UI) */
|
|
1721
|
+
deletingId: string | null;
|
|
1722
|
+
}
|
|
1723
|
+
/**
|
|
1724
|
+
* Comment error with additional context
|
|
1725
|
+
*/
|
|
1726
|
+
interface CommentError {
|
|
1727
|
+
/** Error message */
|
|
1728
|
+
message: string;
|
|
1729
|
+
/** Error code for programmatic handling */
|
|
1730
|
+
code: 'NETWORK_ERROR' | 'TIMEOUT' | 'SERVER_ERROR' | 'NOT_FOUND' | 'FORBIDDEN' | 'RATE_LIMITED' | 'UNKNOWN';
|
|
1731
|
+
/** Number of retry attempts made */
|
|
1732
|
+
retryCount: number;
|
|
1733
|
+
/** Whether error is recoverable */
|
|
1734
|
+
recoverable: boolean;
|
|
1735
|
+
}
|
|
1736
|
+
/**
|
|
1737
|
+
* CommentManager configuration
|
|
1738
|
+
*/
|
|
1739
|
+
interface CommentManagerConfig {
|
|
1740
|
+
/** Comments per page (default: 20) */
|
|
1741
|
+
pageSize?: number;
|
|
1742
|
+
/** Replies per load (default: 10) */
|
|
1743
|
+
repliesPageSize?: number;
|
|
1744
|
+
/** Cache TTL in ms (default: 5 minutes) */
|
|
1745
|
+
cacheTTL?: number;
|
|
1746
|
+
/** Maximum retry attempts (default: 2) */
|
|
1747
|
+
maxRetries?: number;
|
|
1748
|
+
/** Auto-expand threshold for replies (default: 1) */
|
|
1749
|
+
repliesAutoExpandThreshold?: number;
|
|
1750
|
+
/** Replies collapse limit (default: 3) */
|
|
1751
|
+
repliesCollapseLimit?: number;
|
|
1752
|
+
/** Comment text max lines before collapse (default: 3) */
|
|
1753
|
+
textMaxLines?: number;
|
|
1754
|
+
/** Enable optimistic UI (default: true) */
|
|
1755
|
+
enableOptimistic?: boolean;
|
|
1756
|
+
}
|
|
1757
|
+
/**
|
|
1758
|
+
* Default comment manager configuration
|
|
1759
|
+
*/
|
|
1760
|
+
declare const DEFAULT_COMMENT_MANAGER_CONFIG: Required<CommentManagerConfig>;
|
|
1761
|
+
/**
|
|
1762
|
+
* Create initial state for a video's comments
|
|
1763
|
+
*/
|
|
1764
|
+
declare const createInitialVideoCommentState: () => VideoCommentState;
|
|
1765
|
+
/**
|
|
1766
|
+
* Create initial global comment state
|
|
1767
|
+
*/
|
|
1768
|
+
declare const createInitialCommentState: () => CommentManagerState;
|
|
1769
|
+
/**
|
|
1770
|
+
* Optimistic comment for immediate UI feedback
|
|
1771
|
+
*/
|
|
1772
|
+
interface OptimisticComment extends Omit<CommentItem, 'id'> {
|
|
1773
|
+
/** Temporary optimistic ID (prefixed with 'optimistic_') */
|
|
1774
|
+
id: string;
|
|
1775
|
+
/** Flag to identify optimistic items */
|
|
1776
|
+
isPending: true;
|
|
1777
|
+
}
|
|
1778
|
+
/**
|
|
1779
|
+
* Optimistic reply for immediate UI feedback
|
|
1780
|
+
*/
|
|
1781
|
+
interface OptimisticReply extends Omit<ReplyItem, 'id'> {
|
|
1782
|
+
/** Temporary optimistic ID */
|
|
1783
|
+
id: string;
|
|
1784
|
+
/** Flag to identify optimistic items */
|
|
1785
|
+
isPending: true;
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
/**
|
|
1789
|
+
* CommentManager - Manages comment data with zustand/vanilla store
|
|
1790
|
+
*
|
|
1791
|
+
* Features:
|
|
1792
|
+
* - Multi-video comment caching (by videoId)
|
|
1793
|
+
* - Pagination with cursor
|
|
1794
|
+
* - Nested replies support (1 level)
|
|
1795
|
+
* - Optimistic UI for post/delete
|
|
1796
|
+
* - Cache TTL for stale data detection
|
|
1797
|
+
* - Request deduplication
|
|
1798
|
+
*
|
|
1799
|
+
* Architecture: Hexagonal (Port = ICommentAdapter)
|
|
1800
|
+
*
|
|
1801
|
+
* @example
|
|
1802
|
+
* ```typescript
|
|
1803
|
+
* const commentManager = new CommentManager(commentAdapter);
|
|
1804
|
+
*
|
|
1805
|
+
* // Subscribe to state changes
|
|
1806
|
+
* commentManager.store.subscribe((state) => console.log(state));
|
|
1807
|
+
*
|
|
1808
|
+
* // Load comments for a video
|
|
1809
|
+
* await commentManager.loadComments('video-123');
|
|
1810
|
+
*
|
|
1811
|
+
* // Post a comment
|
|
1812
|
+
* await commentManager.postComment('video-123', 'Great video!');
|
|
1813
|
+
* ```
|
|
1814
|
+
*/
|
|
1815
|
+
declare class CommentManager {
|
|
1816
|
+
private readonly adapter;
|
|
1817
|
+
/** Zustand vanilla store - Single Source of Truth */
|
|
1818
|
+
readonly store: StoreApi<CommentManagerState>;
|
|
1819
|
+
/** Resolved configuration */
|
|
1820
|
+
private readonly config;
|
|
1821
|
+
/** Logger (optional) */
|
|
1822
|
+
private readonly logger?;
|
|
1823
|
+
/** Request deduplication: Map of key → in-flight Promise */
|
|
1824
|
+
private inFlightRequests;
|
|
1825
|
+
/** Optimistic ID counter */
|
|
1826
|
+
private optimisticIdCounter;
|
|
1827
|
+
constructor(adapter: ICommentAdapter, config?: CommentManagerConfig, logger?: InternalLogger);
|
|
1828
|
+
/**
|
|
1829
|
+
* Load comments for a video
|
|
1830
|
+
*
|
|
1831
|
+
* Features:
|
|
1832
|
+
* - Cache check with TTL
|
|
1833
|
+
* - Request deduplication
|
|
1834
|
+
* - Stale data detection
|
|
1835
|
+
*/
|
|
1836
|
+
loadComments(videoId: string, forceRefresh?: boolean): Promise<void>;
|
|
1837
|
+
/**
|
|
1838
|
+
* Load more comments (pagination)
|
|
1839
|
+
*/
|
|
1840
|
+
loadMore(videoId: string): Promise<void>;
|
|
1841
|
+
/**
|
|
1842
|
+
* Load replies for a comment
|
|
1843
|
+
*/
|
|
1844
|
+
loadReplies(commentId: string): Promise<void>;
|
|
1845
|
+
/**
|
|
1846
|
+
* Post a new comment with optimistic UI
|
|
1847
|
+
*/
|
|
1848
|
+
postComment(videoId: string, content: string, currentUser: CommentAuthor): Promise<CommentItem | null>;
|
|
1849
|
+
/**
|
|
1850
|
+
* Post a reply with optimistic UI
|
|
1851
|
+
*/
|
|
1852
|
+
postReply(videoId: string, parentId: string, content: string, currentUser: CommentAuthor, replyTo?: CommentAuthor): Promise<ReplyItem | null>;
|
|
1853
|
+
/**
|
|
1854
|
+
* Delete a comment with optimistic UI
|
|
1855
|
+
*/
|
|
1856
|
+
deleteComment(videoId: string, commentId: string, isReply?: boolean, parentId?: string): Promise<boolean>;
|
|
1857
|
+
/**
|
|
1858
|
+
* Like a comment/reply with optimistic UI
|
|
1859
|
+
*/
|
|
1860
|
+
likeComment(videoId: string, commentId: string, isReply?: boolean, parentId?: string): Promise<void>;
|
|
1861
|
+
/**
|
|
1862
|
+
* Unlike a comment/reply with optimistic UI
|
|
1863
|
+
*/
|
|
1864
|
+
unlikeComment(videoId: string, commentId: string, isReply?: boolean, parentId?: string): Promise<void>;
|
|
1865
|
+
/**
|
|
1866
|
+
* Get comments for a video
|
|
1867
|
+
*/
|
|
1868
|
+
getComments(videoId: string): CommentItem[];
|
|
1869
|
+
/**
|
|
1870
|
+
* Get a single comment
|
|
1871
|
+
*/
|
|
1872
|
+
getComment(videoId: string, commentId: string): CommentItem | undefined;
|
|
1873
|
+
/**
|
|
1874
|
+
* Get a single reply
|
|
1875
|
+
*/
|
|
1876
|
+
getReply(videoId: string, parentId: string, replyId: string): ReplyItem | undefined;
|
|
1877
|
+
/**
|
|
1878
|
+
* Get video comment state
|
|
1879
|
+
*/
|
|
1880
|
+
getVideoState(videoId: string): VideoCommentState | undefined;
|
|
1881
|
+
/**
|
|
1882
|
+
* Set active video ID (for comment sheet)
|
|
1883
|
+
*/
|
|
1884
|
+
setActiveVideo(videoId: string | null): void;
|
|
1885
|
+
/**
|
|
1886
|
+
* Get config
|
|
1887
|
+
*/
|
|
1888
|
+
getConfig(): Required<CommentManagerConfig>;
|
|
1889
|
+
/**
|
|
1890
|
+
* Clear comments for a video
|
|
1891
|
+
*/
|
|
1892
|
+
clearVideoComments(videoId: string): void;
|
|
1893
|
+
/**
|
|
1894
|
+
* Clear all comments
|
|
1895
|
+
*/
|
|
1896
|
+
clearAll(): void;
|
|
1897
|
+
private executeLoadComments;
|
|
1898
|
+
private executeLoadMore;
|
|
1899
|
+
private executeLoadReplies;
|
|
1900
|
+
private addOptimisticComment;
|
|
1901
|
+
private removeOptimisticComment;
|
|
1902
|
+
private replaceOptimisticComment;
|
|
1903
|
+
private addOptimisticReply;
|
|
1904
|
+
private removeOptimisticReply;
|
|
1905
|
+
private replaceOptimisticReply;
|
|
1906
|
+
private removeCommentFromStore;
|
|
1907
|
+
private removeReplyFromStore;
|
|
1908
|
+
private restoreComment;
|
|
1909
|
+
private restoreReply;
|
|
1910
|
+
private updateLikeState;
|
|
1911
|
+
private updateVideoState;
|
|
1912
|
+
private isCacheStale;
|
|
1913
|
+
private generateOptimisticId;
|
|
1914
|
+
private createError;
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
export { type ActionType, type AllocationResult, type CommentError, CommentManager, type CommentManagerConfig, type CommentManagerState, DEFAULT_COMMENT_MANAGER_CONFIG, DEFAULT_FEED_CONFIG, DEFAULT_LIFECYCLE_CONFIG, DEFAULT_OPTIMISTIC_CONFIG, DEFAULT_PLAYER_CONFIG, DEFAULT_PREFETCH_CACHE_CONFIG, DEFAULT_PREFETCH_CONFIG, DEFAULT_RESOURCE_CONFIG, type FeedConfig, type FeedError, FeedManager, type FeedState, type LifecycleConfig, type LifecycleEvent, type LifecycleEventListener, LifecycleManager, type LifecycleState, type NetworkType, type OptimisticComment, type OptimisticConfig, type OptimisticEvent, type OptimisticEventListener, OptimisticManager, type OptimisticReply, type OptimisticState, type PendingAction, type PlayerConfig, PlayerEngine, type PlayerError, type PlayerEvent, type PlayerEventListener, type PlayerState, PlayerStatus, type PrefetchCacheConfig, type PrefetchConfig, type ResourceConfig, type ResourceEvent, type ResourceEventListener, ResourceGovernor, type ResourceState, type RestoreResult, type VideoCommentState, calculatePrefetchIndices, calculateWindowIndices, canPause, canPlay, canSeek, computeAllocationChanges, createInitialCommentState, createInitialVideoCommentState, isActiveState, isValidTransition, mapNetworkType };
|