'use client'; import { useState, useEffect, useCallback, useRef } from 'react'; import { FixedSizeGrid } from 'react-window'; import { Card, CardContent } from '@/components/ui/card'; import { StarRating } from '@/components/star-rating'; import { Film, Image as ImageIcon, HardDrive, Search, Folder, Play, ChevronLeft, Home, FileText } from 'lucide-react'; import { Button } from '@/components/ui/button'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; interface FileSystemItem { name: string; path: string; isDirectory: boolean; size: number; thumbnail?: string; type?: string; id?: number; avg_rating?: number; star_count?: number; } interface BreadcrumbItem { name: string; path: string; } interface VirtualizedFolderGridProps { currentPath: string; onVideoClick: (item: FileSystemItem) => void; onPhotoClick: (item: FileSystemItem, index: number) => void; onTextClick: (item: FileSystemItem) => void; onBackClick: () => void; onBreadcrumbClick: (path: string) => void; breadcrumbs: BreadcrumbItem[]; libraries: {id: number, path: string}[]; onItemsLoaded?: (items: FileSystemItem[]) => void; } const ITEM_HEIGHT = 280; // Increased for folder cards export default function VirtualizedFolderGrid({ currentPath, onVideoClick, onPhotoClick, onTextClick, onBackClick, onBreadcrumbClick, breadcrumbs, libraries, onItemsLoaded }: VirtualizedFolderGridProps) { const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [containerWidth, setContainerWidth] = useState(0); const router = useRouter(); const containerRef = useRef(null); const formatFileSize = (bytes: number) => { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; const formatFilePath = (path: string) => { if (!path) return ''; const lastSlashIndex = path.lastIndexOf('/'); const lastBackslashIndex = path.lastIndexOf('\\'); const lastSeparatorIndex = Math.max(lastSlashIndex, lastBackslashIndex); if (lastSeparatorIndex === -1) { return path; } const directory = path.substring(0, lastSeparatorIndex); const filename = path.substring(lastSeparatorIndex + 1); if (directory.length <= 40) { return `${directory}/${filename}`; } const maxDirLength = 35; const startLength = Math.floor(maxDirLength / 2); const endLength = maxDirLength - startLength - 3; const truncatedDir = directory.length > maxDirLength ? `${directory.substring(0, startLength)}...${directory.substring(directory.length - endLength)}` : directory; return `${truncatedDir}/${filename}`; }; const formatTitle = (title: string) => { if (!title) return ''; if (title.length <= 40) { return title; } return title.length > 60 ? title.substring(0, 60) + '...' : title; }; const getLibraryRoot = (currentPath: string): string | null => { if (!currentPath || libraries.length === 0) return null; for (const library of libraries) { if (currentPath.startsWith(library.path)) { return library.path; } } return null; }; const getParentPath = (currentPath: string): string => { if (!currentPath) return ''; const libraryRoot = getLibraryRoot(currentPath); if (!libraryRoot) return ''; if (currentPath === libraryRoot) return ''; const pathParts = currentPath.split('/'); const libraryParts = libraryRoot.split('/').filter(part => part.length > 0); const filteredPathParts = pathParts.filter(part => part.length > 0); if (filteredPathParts.length <= libraryParts.length) return ''; const parentParts = filteredPathParts.slice(0, -1); return '/' + parentParts.join('/'); }; const fetchItems = useCallback(async (path: string) => { setLoading(true); setError(''); try { const res = await fetch(`/api/files?path=${encodeURIComponent(path)}`); const data = await res.json(); if (!Array.isArray(data)) { console.error('Invalid response format:', data); setItems([]); setError('Invalid response from server'); } else { setItems(data); } } catch (error) { console.error('Error fetching items:', error); setItems([]); setError('Failed to load directory contents'); } finally { setLoading(false); } }, []); const getFileIcon = (item: FileSystemItem) => { if (item.isDirectory) return ; if (item.type === 'photo') return ; if (item.type === 'video') return ; if (item.type === 'text') return ; return ; }; const isMediaFile = (item: FileSystemItem) => { return item.type === 'video' || item.type === 'photo' || item.type === 'text'; }; // Calculate responsive column count and width const getColumnCount = useCallback(() => { if (containerWidth === 0) return 6; if (containerWidth < 640) return 2; if (containerWidth < 768) return 3; if (containerWidth < 1024) return 4; if (containerWidth < 1280) return 5; if (containerWidth < 1536) return 6; return 7; }, [containerWidth]); const getColumnWidth = useCallback(() => { const cols = getColumnCount(); const availableWidth = containerWidth - 32; const gapWidth = (cols - 1) * 16; return Math.floor((availableWidth - gapWidth) / cols); }, [containerWidth, getColumnCount]); // Update container width on resize useEffect(() => { const updateWidth = () => { if (containerRef.current) { setContainerWidth(containerRef.current.offsetWidth); } }; updateWidth(); window.addEventListener('resize', updateWidth); return () => window.removeEventListener('resize', updateWidth); }, []); useEffect(() => { if (currentPath) { fetchItems(currentPath); } }, [currentPath, fetchItems]); useEffect(() => { if (onItemsLoaded) { onItemsLoaded(items); } }, [items, onItemsLoaded]); const Cell = ({ columnIndex, rowIndex, style }: any) => { const columnCount = getColumnCount(); const index = rowIndex * columnCount + columnIndex; const item = items[index]; if (!item) return null; return (
{ if (item.type === 'video' && item.id) { e.preventDefault(); onVideoClick(item); } else if (item.type === 'photo' && item.id) { e.preventDefault(); const photoIndex = items.filter(i => i.type === 'photo' && i.id).findIndex(i => i.id === item.id); onPhotoClick(item, photoIndex); } else if (item.type === 'text' && item.id) { e.preventDefault(); onTextClick(item); } }}>
{item.isDirectory ? (
) : isMediaFile(item) ? (
{item.type === 'text' ? ( // For text files, show a text icon instead of thumbnail
) : ( // For photos and videos, show thumbnail {item.name} )} {item.type === 'video' && (
)} {item.type === 'photo' && (
)} {item.type === 'text' && (
)}
) : (
{getFileIcon(item)}
)}

{formatTitle(item.name)}

{formatFileSize(item.size)} {isMediaFile(item) && (item.avg_rating || 0) > 0 && ( )}

{formatFilePath(item.path)}

); }; const columnCount = getColumnCount(); const columnWidth = getColumnWidth(); const rowCount = Math.ceil(items.length / columnCount); // Calculate available height for the grid const getAvailableHeight = useCallback(() => { if (typeof window === 'undefined') return 600; // Calculate the actual header height and other UI elements const headerHeight = 200; // Title, breadcrumbs, back button const bottomPadding = 20; const availableHeight = window.innerHeight - headerHeight - bottomPadding; return Math.max(Math.min(availableHeight, window.innerHeight - 100), 400); }, []); if (!currentPath) { return (

Select a Library

Choose a media library from the sidebar to browse your files

); } if (loading && items.length === 0) { return (

Loading directory...

); } return (
{/* Header */}
{/* Back Button */}
{/* Breadcrumb Navigation */} {/* Current Directory Title */}

{currentPath ? currentPath.split('/').pop() : 'Libraries'}

{/* Folder Grid Container */}
{error ? (

Error Loading Directory

{error}

) : items.length > 0 && containerWidth > 0 ? (
{Cell} {/* Fancy scroll indicator */}
) : items.length === 0 ? (

Empty Directory

No files or folders found in this location.

) : (
)}
); }