import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Link } from 'react-router-dom' import { Library as LibraryIcon, RefreshCw, Sparkles } from 'lucide-react' import { EpisodeArtworkToggle } from '../components/EpisodeArtworkToggle' import { HomeLibrariesSection, HomeLibraryRowSection } from './HomePageSections' import type { LibraryPreview } from './librariesPageModel' export function LibrariesHeader({ previewCount, total, repairMsg, repairEpisodeArtwork, repairing, onRepairEpisodeArtworkChange, onRepairRescrape, onManageLibraries, }: { previewCount: number total: number repairMsg: string repairEpisodeArtwork: boolean repairing: boolean onRepairEpisodeArtworkChange: (value: boolean) => void onRepairRescrape: () => void onManageLibraries: () => void }) { return (

媒体库

共 {previewCount} 个目录 · {total.toLocaleString()} 个条目。每个目录直接展示最新入库内容。

{repairMsg && {repairMsg}} 刮削队列
) } export function LibrariesEmptyState() { return (

暂无媒体库,请到管理后台添加目录。

) } export function LibrariesContent({ previews, pinnedIds, onTogglePin, onNeedPreviews, }: { previews: LibraryPreview[] pinnedIds: string[] onTogglePin: (libraryId: string) => void onNeedPreviews?: (ids: string[], limit?: number) => void }) { // 下方媒体库货架继续按需解锁:首屏先展示前 3 个,滚动接近底部再加载 2 个。 const INITIAL_SHELVES = 3 const STEP_SHELVES = 2 const [visibleCount, setVisibleCount] = useState(INITIAL_SHELVES) const sentinelRef = useRef(null) const userScrolledRef = useRef(false) const revealMoreShelves = useCallback(() => { setVisibleCount((prev) => { if (prev >= previews.length) return prev return Math.min(prev + STEP_SHELVES, previews.length) }) }, [previews.length]) useEffect(() => { const currentTargets = previews.slice(0, visibleCount).map((preview) => preview.library.id) onNeedPreviews?.(currentTargets, 10) }, [previews, visibleCount, onNeedPreviews]) useEffect(() => { const scrollParent = document.getElementById('app-main-scroll') if (!scrollParent) return const handleCheckBottom = () => { if (scrollParent.scrollTop <= 0) return userScrolledRef.current = true const remaining = scrollParent.scrollHeight - scrollParent.scrollTop - scrollParent.clientHeight if (remaining < 600) { revealMoreShelves() } } scrollParent.addEventListener('scroll', handleCheckBottom, { passive: true }) const sentinel = sentinelRef.current let observer: IntersectionObserver | null = null if (sentinel) { observer = new IntersectionObserver( (entries) => { const [entry] = entries if (entry?.isIntersecting && userScrolledRef.current) { revealMoreShelves() } }, { root: scrollParent, rootMargin: '600px 0px', threshold: 0, }, ) observer.observe(sentinel) } return () => { scrollParent.removeEventListener('scroll', handleCheckBottom) if (observer) observer.disconnect() } }, [revealMoreShelves, visibleCount, previews.length]) const visiblePreviews = useMemo( () => previews.slice(0, visibleCount), [previews, visibleCount], ) const libraries = useMemo(() => previews.map((preview) => preview.library), [previews]) const libraryData = useMemo( () => Object.fromEntries( previews.map((preview) => [ preview.library.id, { cards: preview.cards, items: [], total: preview.total }, ]), ), [previews], ) const libraryCounts = useMemo( () => Object.fromEntries( previews.map((preview) => [preview.library.id, preview.total]), ), [previews], ) return ( <> {visiblePreviews.length > 0 && (
{visiblePreviews.map((preview) => (
))} {visibleCount < previews.length && ( )}
)} ) }