'use client' import { useEffect, useState, useCallback, useRef } from 'react' import type { DirectoryListing, FileEntry } from '@/types' import VideoPlayerModal from './VideoPlayerModal' import ImageLightbox from './ImageLightbox' interface Props { libraryId: string initialPath: string } type ModalState = | { type: 'video'; url: string; name: string } | { type: 'image'; url: string; name: string } | null export default function MixedView({ libraryId, initialPath }: Props) { const [currentPath, setCurrentPath] = useState(initialPath) const [listing, setListing] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [modal, setModal] = useState(null) const loadPath = useCallback( (path: string) => { setLoading(true) setError(null) fetch( `/api/browse?libraryId=${encodeURIComponent(libraryId)}&path=${encodeURIComponent(path)}` ) .then((r) => r.json()) .then((data: DirectoryListing) => { setListing(data) setCurrentPath(path) setLoading(false) }) .catch(() => { setError('Failed to load directory') setLoading(false) }) }, [libraryId] ) useEffect(() => { loadPath(initialPath) }, [loadPath, initialPath]) const handleEntry = (entry: FileEntry) => { if (entry.type === 'directory') { const newPath = currentPath ? `${currentPath}/${entry.name}` : entry.name loadPath(newPath) return } if (!entry.url) return if (entry.mediaType === 'video') { setModal({ type: 'video', url: entry.url, name: entry.name }) } else if (entry.mediaType === 'image') { setModal({ type: 'image', url: entry.url, name: entry.name }) } else { // Download other file types window.open(entry.url, '_blank') } } const navigateUp = () => { const parts = currentPath.split('/').filter(Boolean) parts.pop() loadPath(parts.join('/')) } // Build breadcrumb segments const breadcrumbs = currentPath ? currentPath.split('/').filter(Boolean) : [] return ( <> {/* Breadcrumb */} {loading && } {error && (
{error}
)} {!loading && !error && listing && ( <> {listing.entries.length === 0 ? (
This folder is empty.
) : (
{/* Up button */} {breadcrumbs.length > 0 && ( )} {listing.entries.map((entry) => ( ))}
)} )} {modal?.type === 'video' && ( setModal(null)} /> )} {modal?.type === 'image' && ( setModal(null)} /> )} ) } function EntryTile({ entry, onOpen }: { entry: FileEntry; onOpen: (e: FileEntry) => void }) { type ImgState = 'loading' | 'loaded' | 'error' const [imgState, setImgState] = useState( entry.thumbnailUrl ? 'loading' : 'error' ) // Reset image state when the entry changes (e.g. navigating to a new folder) const prevUrl = useRef(entry.thumbnailUrl) if (prevUrl.current !== entry.thumbnailUrl) { prevUrl.current = entry.thumbnailUrl if (entry.thumbnailUrl) setImgState('loading') else setImgState('error') } const isDir = entry.type === 'directory' const isVideo = entry.mediaType === 'video' const showThumbnail = !isDir && imgState !== 'error' && entry.thumbnailUrl // Icon shown when no thumbnail available or while loading const icon = isDir ? '📁' : isVideo ? '▶' : entry.mediaType === 'image' ? '🖼' : '📄' return ( ) } function LoadingSkeleton() { return (
{Array.from({ length: 12 }).map((_, i) => (
))}
) }