diff --git a/frontend/src/components/FileExplorer.tsx b/frontend/src/components/FileExplorer.tsx index cff0025..8077b07 100644 --- a/frontend/src/components/FileExplorer.tsx +++ b/frontend/src/components/FileExplorer.tsx @@ -8,10 +8,10 @@ import { MoreVertical, Star, Download, Pencil, Copy, Move, FolderPlus, ArrowUp, X, Check, RefreshCw, Info, Link as LinkIcon, Image as ImageIcon, Film as FilmIcon, Sun, Moon, Music as MusicIcon, Code as CodeIcon, Archive as ArchiveIcon, CornerDownRight, - CheckCircle, AlertCircle + CheckCircle, AlertCircle, Menu } from 'lucide-react'; import api from '@/lib/api'; - +import { useIsMobile } from '@/hooks/useIsMobile'; const Folder = ({ className, style }: { className?: string, style?: React.CSSProperties }) => ( @@ -205,6 +205,11 @@ function ThumbnailImage({ checksum, fallbackIcon, downloadToken }: { checksum: s } export default function FileExplorer({ onLogout }: FileExplorerProps) { + const isMobile = useIsMobile(); + const [sidebarOpen, setSidebarOpen] = useState(false); + const [mobileSearchOpen, setMobileSearchOpen] = useState(false); + + const [currentPath, setCurrentPath] = useState(() => { if (typeof window !== 'undefined') { return new URLSearchParams(window.location.search).get('path') || '.'; @@ -644,7 +649,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { }); } - function handleContextMenu(e: React.MouseEvent, file: FileItem) { + function handleContextMenu(e: React.MouseEvent | { preventDefault: () => void, clientX: number, clientY: number }, file: FileItem) { e.preventDefault(); const estimatedHeight = activeSection === 'trash' ? 100 : 320; setContextMenu({ @@ -654,6 +659,43 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { }); } + const touchTimerRef = useRef(null); + + const getTouchHandlers = (file: FileItem) => ({ + onTouchStart: (e: React.TouchEvent) => { + if (!isMobile) return; + const touch = e.touches[0]; + const startX = touch.clientX; + const startY = touch.clientY; + + touchTimerRef.current = setTimeout(() => { + handleContextMenu({ preventDefault: () => {}, clientX: startX, clientY: startY }, file); + // Provide haptic feedback if available + if (typeof window !== 'undefined' && window.navigator && window.navigator.vibrate) { + window.navigator.vibrate(50); + } + }, 500); + }, + onTouchMove: () => { + if (touchTimerRef.current) { + clearTimeout(touchTimerRef.current); + touchTimerRef.current = null; + } + }, + onTouchEnd: () => { + if (touchTimerRef.current) { + clearTimeout(touchTimerRef.current); + touchTimerRef.current = null; + } + }, + onTouchCancel: () => { + if (touchTimerRef.current) { + clearTimeout(touchTimerRef.current); + touchTimerRef.current = null; + } + } + }); + async function handleSearch(query: string) { setSearchQuery(query); if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current); @@ -1236,9 +1278,21 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { }, [displayFiles, activeSection, trashAutoPurgeDays]); return ( -
+
+ {isMobile && sidebarOpen && ( +
setSidebarOpen(false)} + /> + )}