'use client'; /** * IPTVChannelGrid - Displays IPTV channels grouped by category with search * Uses pagination to handle large playlists without freezing. */ import { useState, useMemo } from 'react'; import { Icons } from '@/components/ui/Icon'; import type { M3UChannel } from '@/lib/utils/m3u-parser'; import type { IPTVSource } from '@/lib/store/iptv-store'; const PAGE_SIZE = 100; interface IPTVChannelGridProps { channels: M3UChannel[]; groups: string[]; onSelect: (channel: M3UChannel) => void; activeChannel?: M3UChannel | null; channelsBySource?: Record; sources?: IPTVSource[]; } export function IPTVChannelGrid({ channels, groups, onSelect, activeChannel, channelsBySource, sources }: IPTVChannelGridProps) { const [selectedGroup, setSelectedGroup] = useState(null); const [selectedSourceId, setSelectedSourceId] = useState(null); const [search, setSearch] = useState(''); const [visibleCount, setVisibleCount] = useState(PAGE_SIZE); const hasMultipleSources = sources && sources.length > 1 && channelsBySource; // Get effective channels and groups based on selected source const { effectiveChannels, effectiveGroups } = useMemo(() => { if (!hasMultipleSources || !selectedSourceId) { return { effectiveChannels: channels, effectiveGroups: groups }; } const sourceData = channelsBySource[selectedSourceId]; if (!sourceData) return { effectiveChannels: channels, effectiveGroups: groups }; return { effectiveChannels: sourceData.channels, effectiveGroups: sourceData.groups }; }, [channels, groups, hasMultipleSources, selectedSourceId, channelsBySource]); const filteredChannels = useMemo(() => { let result = effectiveChannels; if (selectedGroup) { result = result.filter((c) => c.group === selectedGroup); } if (search.trim()) { const q = search.toLowerCase().trim(); result = result.filter((c) => c.name.toLowerCase().includes(q)); } return result; }, [effectiveChannels, selectedGroup, search]); // Reset visible count when filter changes const filterKey = `${selectedSourceId}-${selectedGroup}-${search}`; const [lastFilterKey, setLastFilterKey] = useState(filterKey); if (filterKey !== lastFilterKey) { setLastFilterKey(filterKey); setVisibleCount(PAGE_SIZE); } // Reset group when source changes const [lastSourceId, setLastSourceId] = useState(selectedSourceId); if (selectedSourceId !== lastSourceId) { setLastSourceId(selectedSourceId); setSelectedGroup(null); } const visibleChannels = filteredChannels.slice(0, visibleCount); const hasMore = visibleCount < filteredChannels.length; const orderedSources = useMemo(() => { if (!sources || !selectedSourceId) return sources || []; const selected = sources.find((source) => source.id === selectedSourceId); return selected ? [selected, ...sources.filter((source) => source.id !== selectedSourceId)] : sources; }, [sources, selectedSourceId]); const orderedGroups = useMemo(() => { if (!selectedGroup) return effectiveGroups; return [selectedGroup, ...effectiveGroups.filter((group) => group !== selectedGroup)]; }, [effectiveGroups, selectedGroup]); if (channels.length === 0) { return (

暂无频道

请先添加 M3U 直播源

); } return (
setSearch(e.target.value)} className="w-full pl-9 pr-3 py-2 bg-[var(--glass-bg)] border border-[var(--glass-border)] rounded-[var(--radius-2xl)] text-sm text-[var(--text-color)] placeholder:text-[var(--text-color-secondary)]/50 focus:outline-none focus:border-[var(--accent-color)]" />
{filteredChannels.length === effectiveChannels.length ? `共 ${effectiveChannels.length} 个频道` : `${filteredChannels.length} / ${effectiveChannels.length} 个频道`}
{hasMultipleSources && sources && (
{orderedSources.map((source) => { const sourceData = channelsBySource![source.id]; if (!sourceData) return null; return ( ); })}
)} {effectiveGroups.length > 0 && (
{orderedGroups.map((group) => { const count = effectiveChannels.filter((channel) => channel.group === group).length; return ( ); })}
)}
{/* Channel Grid */}
{visibleChannels.map((channel, index) => ( ))}
{/* Load More */} {hasMore && (
)} {filteredChannels.length === 0 && (
未找到匹配的频道
)}
); }