@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/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 XHubReel Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
# @xhub-reel/feed
|
|
2
|
+
|
|
3
|
+
> Virtualized video feed for XHubReel - TikTok-style infinite scroll
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @xhub-reel/feed @xhub-reel/core @xhub-reel/player @xhub-reel/ui
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @xhub-reel/feed @xhub-reel/core @xhub-reel/player @xhub-reel/ui
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- π **3-Node Carousel** - Only renders prev/current/next videos (minimal DOM)
|
|
16
|
+
- βΎοΈ **Infinite Scroll** - Automatic loading of more content
|
|
17
|
+
- π― **Video Activation** - Smart play/pause based on viewport
|
|
18
|
+
- β‘ **Preloading** - Preloads next videos for instant playback via @xhub-reel/player-core
|
|
19
|
+
- π **Pull to Refresh** - Native-feeling refresh gesture
|
|
20
|
+
- πΎ **Memory Efficient** - Max 5 videos in DOM at once
|
|
21
|
+
- π **Two Modes** - Manual (pass videos) or API (automatic fetching)
|
|
22
|
+
- π¨ **Design System** - Uses tokens from @xhub-reel/core
|
|
23
|
+
- π§© **Composable** - VideoOverlay and ActionBar from @xhub-reel/ui
|
|
24
|
+
|
|
25
|
+
## Breaking Changes in v0.0.1
|
|
26
|
+
|
|
27
|
+
### usePreloader Hook
|
|
28
|
+
|
|
29
|
+
`usePreloader` now re-exports `usePreload` from @xhub-reel/player-core:
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
// Before (v0.0.0)
|
|
33
|
+
const { preloadStates, preloadVideo } = usePreloader({ videos, currentIndex })
|
|
34
|
+
|
|
35
|
+
// After (v0.0.1)
|
|
36
|
+
import { usePreload, getPreloadPriorityForFeed } from '@xhub-reel/feed'
|
|
37
|
+
|
|
38
|
+
const { preload, statuses, isPreloaded } = usePreload({ enabled: true })
|
|
39
|
+
const priority = getPreloadPriorityForFeed(index, currentIndex)
|
|
40
|
+
preload(video.url, priority, 'segment')
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### ActionBar Integration
|
|
44
|
+
|
|
45
|
+
VideoFeedItem now uses ActionBar from @xhub-reel/ui internally. No API changes, but custom styling may behave differently.
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
### Manual Mode (Pass Videos Directly)
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { VideoFeed } from '@xhub-reel/feed'
|
|
53
|
+
import type { Video } from '@xhub-reel/core'
|
|
54
|
+
|
|
55
|
+
function App() {
|
|
56
|
+
const videos: Video[] = [...]
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<VideoFeed
|
|
60
|
+
videos={videos}
|
|
61
|
+
onVideoChange={(video, index) => {
|
|
62
|
+
console.log('Now playing:', video.id)
|
|
63
|
+
}}
|
|
64
|
+
onLike={() => console.log('Liked!')}
|
|
65
|
+
onComment={() => console.log('Comment!')}
|
|
66
|
+
onShare={() => console.log('Share!')}
|
|
67
|
+
/>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### API Mode (Automatic Fetching)
|
|
73
|
+
|
|
74
|
+
Use `ConnectedVideoFeed` for automatic data fetching from your backend:
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import { XHubReelProvider } from '@xhub-reel/core/api'
|
|
78
|
+
import { ConnectedVideoFeed } from '@xhub-reel/feed'
|
|
79
|
+
|
|
80
|
+
function App() {
|
|
81
|
+
return (
|
|
82
|
+
<XHubReelProvider
|
|
83
|
+
config={{
|
|
84
|
+
baseUrl: 'https://api.yoursite.com/v1',
|
|
85
|
+
auth: {
|
|
86
|
+
accessToken: userToken,
|
|
87
|
+
onTokenExpired: async () => {
|
|
88
|
+
const newToken = await refreshToken()
|
|
89
|
+
return { accessToken: newToken }
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
<ConnectedVideoFeed
|
|
95
|
+
userId="user123"
|
|
96
|
+
tag="funny"
|
|
97
|
+
onLike={handleLike}
|
|
98
|
+
onComment={handleComment}
|
|
99
|
+
onShare={handleShare}
|
|
100
|
+
/>
|
|
101
|
+
</XHubReelProvider>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Using Individual Components
|
|
107
|
+
|
|
108
|
+
VideoFeedItem now exports VideoOverlay separately for custom layouts:
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
import { VideoFeedItem, VideoOverlay } from '@xhub-reel/feed'
|
|
112
|
+
import { ActionBar } from '@xhub-reel/ui'
|
|
113
|
+
|
|
114
|
+
function CustomVideoItem({ video }) {
|
|
115
|
+
return (
|
|
116
|
+
<div className="custom-container">
|
|
117
|
+
<video src={video.url} />
|
|
118
|
+
|
|
119
|
+
{/* Use VideoOverlay */}
|
|
120
|
+
<VideoOverlay
|
|
121
|
+
video={video}
|
|
122
|
+
onAuthorClick={() => navigate(`/user/${video.author.id}`)}
|
|
123
|
+
timelineExpanded={false}
|
|
124
|
+
/>
|
|
125
|
+
|
|
126
|
+
{/* Or use ActionBar directly */}
|
|
127
|
+
<ActionBar
|
|
128
|
+
likeCount={video.stats.likes}
|
|
129
|
+
commentCount={video.stats.comments}
|
|
130
|
+
shareCount={video.stats.shares}
|
|
131
|
+
isLiked={video.isLiked}
|
|
132
|
+
onLike={() => likeVideo(video.id)}
|
|
133
|
+
onComment={() => openComments(video.id)}
|
|
134
|
+
onShare={() => shareVideo(video.id)}
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Using useVideoFeed Hook
|
|
142
|
+
|
|
143
|
+
For custom implementations, use the `useVideoFeed` hook:
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
import { useVideoFeed } from '@xhub-reel/feed'
|
|
147
|
+
import { VideoFeed } from '@xhub-reel/feed'
|
|
148
|
+
|
|
149
|
+
function CustomFeedPage() {
|
|
150
|
+
const {
|
|
151
|
+
videos,
|
|
152
|
+
isLoading,
|
|
153
|
+
hasMore,
|
|
154
|
+
fetchNextPage,
|
|
155
|
+
error,
|
|
156
|
+
refetch,
|
|
157
|
+
} = useVideoFeed({
|
|
158
|
+
config: {
|
|
159
|
+
baseUrl: 'https://api.yoursite.com',
|
|
160
|
+
auth: { accessToken: token },
|
|
161
|
+
},
|
|
162
|
+
userId: 'user123',
|
|
163
|
+
limit: 10,
|
|
164
|
+
onSuccess: (videos) => console.log('Fetched', videos.length),
|
|
165
|
+
onError: (error) => console.error('Error:', error),
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
if (isLoading) return <LoadingSpinner />
|
|
169
|
+
if (error) return <ErrorMessage error={error} onRetry={refetch} />
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<VideoFeed
|
|
173
|
+
videos={videos}
|
|
174
|
+
isLoading={isLoading}
|
|
175
|
+
hasMore={hasMore}
|
|
176
|
+
onLoadMore={fetchNextPage}
|
|
177
|
+
/>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### With Actions
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
<VideoFeed
|
|
186
|
+
videos={videos}
|
|
187
|
+
onLike={() => likeVideo()}
|
|
188
|
+
onComment={() => openComments()}
|
|
189
|
+
onShare={() => shareVideo()}
|
|
190
|
+
onAuthorClick={() => viewProfile()}
|
|
191
|
+
/>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Components
|
|
195
|
+
|
|
196
|
+
### VideoFeed
|
|
197
|
+
|
|
198
|
+
Main feed component with swipe gestures and virtualization.
|
|
199
|
+
|
|
200
|
+
| Prop | Type | Default | Description |
|
|
201
|
+
|------|------|---------|-------------|
|
|
202
|
+
| `videos` | `Video[]` | `[]` | Array of video objects |
|
|
203
|
+
| `initialIndex` | `number` | `0` | Starting video index |
|
|
204
|
+
| `onVideoChange` | `(video, index) => void` | - | Called when active video changes |
|
|
205
|
+
| `onLoadMore` | `() => void \| Promise<void>` | - | Called when scrolling near end |
|
|
206
|
+
| `onLike` | `(video) => void` | - | Called when like button pressed |
|
|
207
|
+
| `onComment` | `(video) => void` | - | Called when comment button pressed |
|
|
208
|
+
| `onShare` | `(video) => void` | - | Called when share button pressed |
|
|
209
|
+
| `onAuthorClick` | `(video) => void` | - | Called when author clicked |
|
|
210
|
+
| `loadMoreThreshold` | `number` | `3` | Videos from end to trigger load |
|
|
211
|
+
| `transitionDuration` | `number` | `300` | Swipe animation duration (ms) |
|
|
212
|
+
| `swipeThreshold` | `number` | `50` | Swipe threshold (px) |
|
|
213
|
+
| `velocityThreshold` | `number` | `0.3` | Velocity threshold (px/ms) |
|
|
214
|
+
| `gesturesDisabled` | `boolean` | `false` | Disable swipe gestures |
|
|
215
|
+
| `hapticEnabled` | `boolean` | `true` | Enable haptic feedback |
|
|
216
|
+
|
|
217
|
+
### VideoFeedItem
|
|
218
|
+
|
|
219
|
+
Individual video item with built-in controls.
|
|
220
|
+
|
|
221
|
+
| Prop | Type | Default | Description |
|
|
222
|
+
|------|------|---------|-------------|
|
|
223
|
+
| `video` | `Video` | **required** | Video object |
|
|
224
|
+
| `isActive` | `boolean` | `false` | Whether currently active |
|
|
225
|
+
| `priority` | `PreloadPriority` | `'none'` | Preload priority |
|
|
226
|
+
| `showTimeline` | `boolean` | `true` | Show timeline/seekbar |
|
|
227
|
+
| `onLike` | `() => void` | - | Like handler |
|
|
228
|
+
| `onComment` | `() => void` | - | Comment handler |
|
|
229
|
+
| `onShare` | `() => void` | - | Share handler |
|
|
230
|
+
| `onAuthorClick` | `() => void` | - | Author click handler |
|
|
231
|
+
|
|
232
|
+
### VideoOverlay
|
|
233
|
+
|
|
234
|
+
Info overlay component (author, caption, hashtags).
|
|
235
|
+
|
|
236
|
+
| Prop | Type | Default | Description |
|
|
237
|
+
|------|------|---------|-------------|
|
|
238
|
+
| `video` | `Video` | **required** | Video object |
|
|
239
|
+
| `onAuthorClick` | `() => void` | - | Author click handler |
|
|
240
|
+
| `timelineExpanded` | `boolean` | `false` | Adjust padding for timeline |
|
|
241
|
+
|
|
242
|
+
### ConnectedVideoFeed (API Mode)
|
|
243
|
+
|
|
244
|
+
| Prop | Type | Default | Description |
|
|
245
|
+
|------|------|---------|-------------|
|
|
246
|
+
| `config` | `XHubReelConfig` | - | API configuration (optional if using XHubReelProvider) |
|
|
247
|
+
| `userId` | `string` | - | User ID for user-specific feed |
|
|
248
|
+
| `tag` | `string` | - | Tag/hashtag filter |
|
|
249
|
+
| `searchQuery` | `string` | - | Search query |
|
|
250
|
+
| `pageSize` | `number` | `10` | Videos per page |
|
|
251
|
+
| `initialVideos` | `Video[]` | - | Initial videos while loading |
|
|
252
|
+
| `onFetchSuccess` | `(videos) => void` | - | Success callback |
|
|
253
|
+
| `onFetchError` | `(error) => void` | - | Error callback |
|
|
254
|
+
| `renderLoading` | `() => ReactNode` | - | Custom loading UI |
|
|
255
|
+
| `renderError` | `(error, retry) => ReactNode` | - | Custom error UI |
|
|
256
|
+
| `renderEmpty` | `() => ReactNode` | - | Custom empty UI |
|
|
257
|
+
|
|
258
|
+
> **Note:** `PullToRefresh` component ΔΓ£ Δược chuyα»n sang `@xhub-reel/ui` package. Import tα»« `@xhub-reel/ui` thay vΓ¬ `@xhub-reel/feed`.
|
|
259
|
+
|
|
260
|
+
## Hooks
|
|
261
|
+
|
|
262
|
+
### useVideoFeed
|
|
263
|
+
|
|
264
|
+
Fetch videos with infinite scroll support.
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
const {
|
|
268
|
+
videos, // Flattened videos array
|
|
269
|
+
isLoading, // Initial loading state
|
|
270
|
+
isFetchingMore, // Loading more state
|
|
271
|
+
hasMore, // Has more to load
|
|
272
|
+
fetchNextPage, // Load next page
|
|
273
|
+
refetch, // Refetch all
|
|
274
|
+
error, // Error if any
|
|
275
|
+
isApiMode, // Whether API mode active
|
|
276
|
+
totalCount, // Total count (if provided by API)
|
|
277
|
+
} = useVideoFeed({
|
|
278
|
+
config, // XHubReelConfig (required for API mode)
|
|
279
|
+
userId, // User ID filter
|
|
280
|
+
tag, // Tag filter
|
|
281
|
+
searchQuery, // Search query
|
|
282
|
+
limit, // Page size
|
|
283
|
+
enabled, // Enable/disable
|
|
284
|
+
initialVideos, // Initial data
|
|
285
|
+
staleTime, // Cache time
|
|
286
|
+
onSuccess, // Success callback
|
|
287
|
+
onError, // Error callback
|
|
288
|
+
})
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### useVideoActivation
|
|
292
|
+
|
|
293
|
+
Control video activation based on visibility.
|
|
294
|
+
|
|
295
|
+
```tsx
|
|
296
|
+
const {
|
|
297
|
+
isActive, // Whether video is active
|
|
298
|
+
isVisible, // Whether video is visible
|
|
299
|
+
visibilityRatio, // Visibility ratio (0-1)
|
|
300
|
+
activate, // Manual activate
|
|
301
|
+
deactivate, // Manual deactivate
|
|
302
|
+
} = useVideoActivation({
|
|
303
|
+
containerRef, // Container element ref
|
|
304
|
+
videoRef, // Video element ref
|
|
305
|
+
isCurrentVideo, // Whether current in feed
|
|
306
|
+
onActivate, // Activate callback
|
|
307
|
+
onDeactivate, // Deactivate callback
|
|
308
|
+
autoActivate, // Enable auto-activation
|
|
309
|
+
})
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### usePreload (from @xhub-reel/player-core)
|
|
313
|
+
|
|
314
|
+
Preload videos with priority queue.
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
import { usePreload, getPreloadPriorityForFeed } from '@xhub-reel/feed'
|
|
318
|
+
|
|
319
|
+
const {
|
|
320
|
+
preload, // Enqueue preload
|
|
321
|
+
preloadMany, // Enqueue multiple
|
|
322
|
+
cancel, // Cancel preload
|
|
323
|
+
cancelAll, // Cancel all
|
|
324
|
+
setPaused, // Pause/resume
|
|
325
|
+
handleScrollVelocity, // Handle scroll
|
|
326
|
+
isPreloaded, // Check if preloaded
|
|
327
|
+
getStatus, // Get status
|
|
328
|
+
preloadedUrls, // Preloaded URLs
|
|
329
|
+
statuses, // All statuses
|
|
330
|
+
isPaused, // Paused state
|
|
331
|
+
manager, // PreloadManager instance
|
|
332
|
+
} = usePreload({
|
|
333
|
+
enabled: true,
|
|
334
|
+
maxConcurrent: 2,
|
|
335
|
+
maxPreloaded: 5,
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
// Get priority for feed
|
|
339
|
+
const priority = getPreloadPriorityForFeed(index, currentIndex)
|
|
340
|
+
preload(video.url, priority, 'segment')
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Helper Functions
|
|
344
|
+
|
|
345
|
+
```tsx
|
|
346
|
+
import {
|
|
347
|
+
getPreloadPriorityForFeed, // Get numeric priority
|
|
348
|
+
mapPriorityToNumeric, // Map enum to number
|
|
349
|
+
getPreloadPriority, // Get PreloadPriority enum
|
|
350
|
+
preloadThumbnail, // Preload thumbnail
|
|
351
|
+
} from '@xhub-reel/feed'
|
|
352
|
+
|
|
353
|
+
// Get priority based on distance from current
|
|
354
|
+
const priority = getPreloadPriorityForFeed(videoIndex, currentIndex)
|
|
355
|
+
// Returns: 1 (current), 3 (adjacent), 5 (near), 7 (far), 10 (dispose)
|
|
356
|
+
|
|
357
|
+
// Map priority enum to number
|
|
358
|
+
const numPriority = mapPriorityToNumeric('high') // 1
|
|
359
|
+
|
|
360
|
+
// Get enum priority
|
|
361
|
+
const enumPriority = getPreloadPriority(videoIndex, currentIndex)
|
|
362
|
+
// Returns: 'high' | 'medium' | 'low' | 'metadata' | 'none'
|
|
363
|
+
|
|
364
|
+
// Preload thumbnail
|
|
365
|
+
preloadThumbnail('https://example.com/thumbnail.jpg')
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Video Activation Rules
|
|
369
|
+
|
|
370
|
+
Videos are activated based on viewport visibility:
|
|
371
|
+
|
|
372
|
+
| Condition | Action |
|
|
373
|
+
|-----------|--------|
|
|
374
|
+
| > 50% visible | Play |
|
|
375
|
+
| < 30% visible | Pause + Reset |
|
|
376
|
+
| Scroll velocity > 2000px/s | Skip activation |
|
|
377
|
+
| Scroll stopped > 300ms | Activate nearest |
|
|
378
|
+
|
|
379
|
+
## Memory Management
|
|
380
|
+
|
|
381
|
+
The feed automatically manages memory:
|
|
382
|
+
|
|
383
|
+
- **Max 5 videos** in DOM at once
|
|
384
|
+
- **Max 3 decoded** video frames
|
|
385
|
+
- **Aggressive cleanup** on scroll
|
|
386
|
+
- **< 150MB** total memory usage
|
|
387
|
+
|
|
388
|
+
## Performance Tips
|
|
389
|
+
|
|
390
|
+
1. **Use video.id as key** - Ensures proper virtualization
|
|
391
|
+
2. **Preload thumbnails** - Use blur placeholders
|
|
392
|
+
3. **Let the system handle preloading** - usePreload manages queue automatically
|
|
393
|
+
4. **Dispose properly** - Memory manager handles cleanup
|
|
394
|
+
5. **Use design tokens** - All components use @xhub-reel/core tokens
|
|
395
|
+
|
|
396
|
+
## Design System Integration
|
|
397
|
+
|
|
398
|
+
All components use design tokens from @xhub-reel/core:
|
|
399
|
+
|
|
400
|
+
```tsx
|
|
401
|
+
import {
|
|
402
|
+
colors, // colors.background, colors.accent, etc.
|
|
403
|
+
spacing, // spacing[1] - spacing[8]
|
|
404
|
+
fontSizes, // fontSizes.xs, sm, md, lg
|
|
405
|
+
fontWeights, // fontWeights.medium, semibold, bold
|
|
406
|
+
radii, // radii.sm, md, lg, full
|
|
407
|
+
zIndices, // zIndices.base, sticky, overlay
|
|
408
|
+
durations, // durations.fast, normal, slow
|
|
409
|
+
easings, // easings.xhubReel (cubic-bezier)
|
|
410
|
+
} from '@xhub-reel/core'
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
This ensures consistent styling across all packages.
|
|
414
|
+
|
|
415
|
+
## Migration Guide
|
|
416
|
+
|
|
417
|
+
### From v0.0.0 to v0.0.1
|
|
418
|
+
|
|
419
|
+
#### 1. Update usePreloader
|
|
420
|
+
|
|
421
|
+
```tsx
|
|
422
|
+
// Before
|
|
423
|
+
import { usePreloader } from '@xhub-reel/feed'
|
|
424
|
+
const { preloadStates } = usePreloader({ videos, currentIndex })
|
|
425
|
+
|
|
426
|
+
// After
|
|
427
|
+
import { usePreload, getPreloadPriorityForFeed } from '@xhub-reel/feed'
|
|
428
|
+
const { statuses } = usePreload()
|
|
429
|
+
const priority = getPreloadPriorityForFeed(index, currentIndex)
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### 2. ActionBar Styling
|
|
433
|
+
|
|
434
|
+
If you were overriding ActionBar styles, they may not work anymore since VideoFeedItem now uses @xhub-reel/ui ActionBar component. Use ActionBar directly for custom styling:
|
|
435
|
+
|
|
436
|
+
```tsx
|
|
437
|
+
import { ActionBar } from '@xhub-reel/ui'
|
|
438
|
+
|
|
439
|
+
<ActionBar
|
|
440
|
+
likeCount={video.stats.likes}
|
|
441
|
+
// ... props with custom styling
|
|
442
|
+
style={{ right: 24 }}
|
|
443
|
+
/>
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
#### 3. VideoOverlay
|
|
447
|
+
|
|
448
|
+
If you were accessing internal overlay elements, use the new VideoOverlay component:
|
|
449
|
+
|
|
450
|
+
```tsx
|
|
451
|
+
import { VideoOverlay } from '@xhub-reel/feed'
|
|
452
|
+
|
|
453
|
+
<VideoOverlay
|
|
454
|
+
video={video}
|
|
455
|
+
onAuthorClick={handleAuthorClick}
|
|
456
|
+
timelineExpanded={timelineExpanded}
|
|
457
|
+
/>
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## License
|
|
461
|
+
|
|
462
|
+
MIT
|