191 lines
3.4 KiB
Markdown

# Media Viewer Page
## Overview
**File Path:** `admin/src/pages/public/MediaViewerPage.tsx` (306 lines)
**Route:** `/media/:id`
**Role Requirements:** Public access (locked videos require login)
**Purpose:** Individual video player page with metadata, reactions, comments, and related videos.
**Key Features:**
- Back button to gallery
- VideoPlayer component with time tracking
- Metadata display (views, upvotes, category, quality tags)
- Upvote button (toggleable, session-based)
- ReactionButtons component (6 emojis)
- CommentSection component
- Related videos grid (3 cards)
- Locked video modal (redirect to login)
---
## Features
### 1. Video Player
```tsx
<VideoPlayer
videoUrl={video.videoUrl}
onTimeUpdate={(currentTime) => {
// Track view progress
if (currentTime > lastTrackedTime + 30) {
trackView(video.id, currentTime);
setLastTrackedTime(currentTime);
}
}}
/>
```
### 2. Metadata Display
```tsx
<Space size={16}>
<Text type="secondary">
<EyeOutlined /> {video.viewCount} views
</Text>
<Text type="secondary">
<LikeOutlined /> {video.upvotes} upvotes
</Text>
<Tag color="blue">{video.category}</Tag>
{video.quality && <Tag color="green">{video.quality}p</Tag>}
</Space>
```
### 3. Upvote Button
```tsx
<Button
type={hasUpvoted ? 'primary' : 'default'}
icon={hasUpvoted ? <LikeFilled /> : <LikeOutlined />}
onClick={handleUpvote}
size="large"
>
Upvote ({video.upvotes})
</Button>
```
### 4. Reaction Buttons
6 emoji reactions:
- 👍 Like
- ❤️ Love
- 😂 Haha
- 😮 Wow
- 😢 Sad
- 😡 Angry
```tsx
<ReactionButtons
videoId={video.id}
reactions={video.reactions}
onReact={handleReact}
/>
```
### 5. Comment Section
```tsx
<CommentSection
videoId={video.id}
comments={comments}
onSubmit={handleCommentSubmit}
/>
```
### 6. Related Videos
```tsx
<Title level={4}>Related Videos</Title>
<Row gutter={[16, 16]}>
{relatedVideos.slice(0, 3).map(video => (
<Col xs={24} sm={8} key={video.id}>
<PublicVideoCard video={video} />
</Col>
))}
</Row>
```
### 7. Locked Video Handling
```tsx
{video.isLocked && !user && (
<Modal
title="Login Required"
open={true}
footer={
<Button type="primary" onClick={() => navigate('/login')}>
Go to Login
</Button>
}
>
This video requires login to view.
</Modal>
)}
```
---
## API Integration
### Endpoints
#### 1. Get Video
```http
GET /api/media/public/:id
```
#### 2. Track View
```http
POST /api/media/public/:id/view
Content-Type: application/json
{
"currentTime": 67.5
}
```
#### 3. Toggle Upvote
```http
POST /api/media/public/:id/upvote
```
#### 4. Add Reaction
```http
POST /api/media/public/:id/react
Content-Type: application/json
{
"reactionType": "love"
}
```
---
## Performance Considerations
1. **View Tracking**: Throttled to 30-second intervals
2. **Related Videos**: Limited to 3 (prevents over-fetching)
3. **Lazy Comments**: Loaded separately after video metadata
4. **Video Preload**: `preload="metadata"` for faster initial render
---
## Accessibility
- **Keyboard Controls**: Native video player controls
- **Captions**: Support for WebVTT subtitle files
- **Screen Reader**: All buttons have aria-labels
- **Focus Management**: Reaction buttons keyboard navigable
---
## Related Documentation
- [Media Gallery Page](./media-gallery-page.md)
- [Video Upload](../../components/media/upload-video-modal.md)
- [Media API](../../../api/modules/media/routes.md)