'use client'; import Link from 'next/link'; import { useState, useEffect, useCallback } from 'react'; const DEFAULT_THUMBNAIL = 'https://i.ytimg.com/vi/default/hqdefault.jpg'; interface VideoData { id: string; title: string; uploader: string; channel_id: string; thumbnail: string; view_count: number; duration: string; uploaded_date?: string; } interface Subscription { id: number; channel_id: string; channel_name: string; channel_avatar: string; } interface ChannelVideos { subscription: Subscription; videos: VideoData[]; } const INITIAL_ROWS = 2; const VIDEOS_PER_ROW = 5; const MAX_ROWS = 5; function formatViews(views: number): string { if (views >= 1000000) return (views / 1000000).toFixed(1) + 'M'; if (views >= 1000) return (views / 1000).toFixed(1) + 'K'; return views.toString(); } function getRelativeTime(id: string): string { const times = ['2 hours ago', '5 hours ago', '1 day ago', '3 days ago', '1 week ago', '2 weeks ago', '1 month ago']; const index = (id.charCodeAt(0) || 0) % times.length; return times[index]; } function ChannelSection({ channelVideos, defaultExpanded = false }: { channelVideos: ChannelVideos; defaultExpanded?: boolean }) { const { subscription, videos } = channelVideos; const [expanded, setExpanded] = useState(defaultExpanded); const handleImageError = useCallback((e: React.SyntheticEvent) => { const img = e.target as HTMLImageElement; if (img.src !== DEFAULT_THUMBNAIL) { img.src = DEFAULT_THUMBNAIL; } }, []); if (videos.length === 0) return null; const initialCount = INITIAL_ROWS * VIDEOS_PER_ROW; const maxCount = MAX_ROWS * VIDEOS_PER_ROW; const displayedVideos = expanded ? videos.slice(0, maxCount) : videos.slice(0, initialCount); const hasMore = videos.length > initialCount; return (
{subscription.channel_name ? subscription.channel_name[0].toUpperCase() : '?'}

{subscription.channel_name || subscription.channel_id}

{displayedVideos.map((video) => { const relativeTime = video.uploaded_date || getRelativeTime(video.id); const destination = `/watch?v=${video.id}`; const thumbnailSrc = video.thumbnail || DEFAULT_THUMBNAIL; return (
{video.title} {video.duration && (
{video.duration}
)}

{video.title}

{formatViews(video.view_count)} views • {relativeTime}

); })}
{hasMore && (
)}
); } export default function SubscriptionsPage() { const [channelsVideos, setChannelsVideos] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { async function fetchData() { try { const subsRes = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'}/api/subscriptions`, { cache: 'no-store' }); const subsData = await subsRes.json(); const subs = Array.isArray(subsData) ? subsData : []; const channelVideos: ChannelVideos[] = []; for (const sub of subs) { const videosRes = await fetch( `${process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'}/api/channel/videos?id=${sub.channel_id}&limit=${MAX_ROWS * VIDEOS_PER_ROW}`, { cache: 'no-store' } ); const videosData = await videosRes.json(); const videos = Array.isArray(videosData) ? videosData : []; if (videos.length > 0) { channelVideos.push({ subscription: sub, videos: videos.map((v: VideoData) => ({ ...v, uploader: sub.channel_name })) }); } } setChannelsVideos(channelVideos); } catch (err) { console.error('Failed to fetch subscriptions:', err); } finally { setLoading(false); } } fetchData(); }, []); if (loading) { return (
); } if (channelsVideos.length === 0) { return (

No subscriptions yet

Subscribe to channels to see their latest videos here

); } return (

Subscriptions

{channelsVideos.map((channelData) => ( ))}
); }