3.4 KiB

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

<VideoPlayer
  videoUrl={video.videoUrl}
  onTimeUpdate={(currentTime) => {
    // Track view progress
    if (currentTime > lastTrackedTime + 30) {
      trackView(video.id, currentTime);
      setLastTrackedTime(currentTime);
    }
  }}
/>

2. Metadata Display

<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

<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
<ReactionButtons
  videoId={video.id}
  reactions={video.reactions}
  onReact={handleReact}
/>

5. Comment Section

<CommentSection
  videoId={video.id}
  comments={comments}
  onSubmit={handleCommentSubmit}
/>
<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

{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

GET /api/media/public/:id

2. Track View

POST /api/media/public/:id/view
Content-Type: application/json

{
  "currentTime": 67.5
}

3. Toggle Upvote

POST /api/media/public/:id/upvote

4. Add Reaction

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