import React, { useState, useCallback, useEffect } from 'react'; import { Modal } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { FileWithUrl } from '../types/file'; import { useFileManager } from '../hooks/useFileManager'; import { useFilesModalContext } from '../contexts/FilesModalContext'; import { Tool } from '../types/tool'; import MobileLayout from './fileManager/MobileLayout'; import DesktopLayout from './fileManager/DesktopLayout'; import DragOverlay from './fileManager/DragOverlay'; import { FileManagerProvider } from '../contexts/FileManagerContext'; interface FileManagerProps { selectedTool?: Tool | null; } const FileManager: React.FC = ({ selectedTool }) => { const { isFilesModalOpen, closeFilesModal, onFilesSelect } = useFilesModalContext(); const [recentFiles, setRecentFiles] = useState([]); const [isDragging, setIsDragging] = useState(false); const [isMobile, setIsMobile] = useState(false); const { loadRecentFiles, handleRemoveFile, storeFile, convertToFile } = useFileManager(); // File management handlers const isFileSupported = useCallback((fileName: string) => { if (!selectedTool?.supportedFormats) return true; const extension = fileName.split('.').pop()?.toLowerCase(); return selectedTool.supportedFormats.includes(extension || ''); }, [selectedTool?.supportedFormats]); const refreshRecentFiles = useCallback(async () => { const files = await loadRecentFiles(); setRecentFiles(files); }, [loadRecentFiles]); const handleFilesSelected = useCallback(async (files: FileWithUrl[]) => { try { const fileObjects = await Promise.all( files.map(async (fileWithUrl) => { return await convertToFile(fileWithUrl); }) ); onFilesSelect(fileObjects); } catch (error) { console.error('Failed to process selected files:', error); } }, [convertToFile, onFilesSelect]); const handleNewFileUpload = useCallback(async (files: File[]) => { if (files.length > 0) { try { // Files will get IDs assigned through onFilesSelect -> FileContext addFiles onFilesSelect(files); await refreshRecentFiles(); } catch (error) { console.error('Failed to process dropped files:', error); } } }, [onFilesSelect, refreshRecentFiles]); const handleRemoveFileByIndex = useCallback(async (index: number) => { await handleRemoveFile(index, recentFiles, setRecentFiles); }, [handleRemoveFile, recentFiles]); useEffect(() => { const checkMobile = () => setIsMobile(window.innerWidth < 1030); checkMobile(); window.addEventListener('resize', checkMobile); return () => window.removeEventListener('resize', checkMobile); }, []); useEffect(() => { if (isFilesModalOpen) { refreshRecentFiles(); } else { // Reset state when modal is closed setIsDragging(false); } }, [isFilesModalOpen, refreshRecentFiles]); // Cleanup any blob URLs when component unmounts useEffect(() => { return () => { // Clean up blob URLs from recent files recentFiles.forEach(file => { if (file.url && file.url.startsWith('blob:')) { URL.revokeObjectURL(file.url); } }); }; }, [recentFiles]); // Modal size constants for consistent scaling const modalHeight = '80vh'; const modalWidth = isMobile ? '100%' : '80vw'; const modalMaxWidth = isMobile ? '100%' : '1200px'; const modalMaxHeight = '1200px'; const modalMinWidth = isMobile ? '320px' : '800px'; return (
setIsDragging(true)} onDragLeave={() => setIsDragging(false)} accept={["*/*"] as any} multiple={true} activateOnClick={false} style={{ height: '100%', width: '100%', border: 'none', borderRadius: '30px', backgroundColor: 'var(--bg-file-manager)' }} styles={{ inner: { pointerEvents: 'all' } }} > {isMobile ? : }
); }; export default FileManager;