'use client'; import './style.scss'; import { useEffect, useState, useCallback } from 'react'; import { useRouter } from 'next/navigation'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSun, faMoon, faGlobe, faBookmark } from '@fortawesome/free-solid-svg-icons'; import { fetchApi } from '@/lib/utils/client'; import { useSignalRContext } from '@/contexts/signalrProvider'; import { ChannelListItem, ChannelStatusUpdate } from '@/types/channel'; import { ChannelListResponse } from '@/types/response/channel/list'; import useTheme from '@/hooks/useTheme'; export default function ChannelSidebar() { const [channels, setChannels] = useState([]); const [loading, setLoading] = useState(true); const [langOpen, setLangOpen] = useState(false); const router = useRouter(); const { chatConnection } = useSignalRContext(); const { toggleTheme, isDark } = useTheme(); const sortChannels = useCallback((list: ChannelListItem[]) => { return [...list].sort((a, b) => { if (a.isLive && !b.isLive) return -1; if (!a.isLive && b.isLive) return 1; if (a.isLive && b.isLive) return b.viewerCount - a.viewerCount; return a.name.localeCompare(b.name); }); }, []); const loadChannels = useCallback(async () => { try { const res = await fetchApi('/api/channel/list'); if (res.data?.channels) { setChannels(sortChannels(res.data.channels)); } } catch {} setLoading(false); }, [sortChannels]); useEffect(() => { loadChannels(); }, [loadChannels]); // SignalR: 채널 상태 실시간 업데이트 useEffect(() => { if (!chatConnection) return; const handler = (status: ChannelStatusUpdate) => { setChannels(prev => { const updated = prev.map(ch => ch.channelSID === status.channelSID ? { ...ch, isLive: status.isLive, viewerCount: status.viewerCount, videoId: status.videoId } : ch ); return sortChannels(updated); }); }; chatConnection.on('ReceiveChannelStatus', handler); return () => { chatConnection.off('ReceiveChannelStatus', handler); }; }, [chatConnection, sortChannels]); const handleClick = (ch: ChannelListItem) => { if (ch.isLive && ch.videoId) { router.push(`/watch/${ch.channelSID}`); } else { router.push(`/channel/${ch.channelSID}`); } }; const formatCount = (n: number) => { if (n >= 10000) return `${(n / 10000).toFixed(1)}만`; if (n >= 1000) return `${(n / 1000).toFixed(1)}K`; return n.toString(); }; const getThumbnailUrl = (ch: ChannelListItem) => { if (ch.thumbnailUrl) return ch.thumbnailUrl; return `https://yt3.googleusercontent.com/a/${ch.channelSID}=s48-c-k-c0x00ffffff-no-rj`; }; if (loading) { return (
채널
로딩 중...
); } return (
채널
{channels.length === 0 && (
등록된 채널이 없습니다
)} {channels.map(ch => (
handleClick(ch)}>
{ch.name}
{ch.name}
{ch.subscriberCount > 0 ? `구독자 ${formatCount(ch.subscriberCount)}` : ch.handle || ''}
{ch.isLive && ( 👁 {formatCount(ch.viewerCount)} )}
))}
{/* 하단: 다크모드 / 다국어 / 즐겨찾기 */}
{langOpen && (
)}
); }