mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-09-24 04:26:14 +00:00

Added Filewithid type Updated code where file was being used to use filewithid Updated places we identified files by name or composite keys to use UUID Updated places we should have been using quickkey Updated pageeditor issue where we parsed pagenumber from pageid instead of using pagenumber directly --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: James Brunton <jbrunton96@gmail.com>
176 lines
5.8 KiB
TypeScript
176 lines
5.8 KiB
TypeScript
import React, { useState, useCallback, useEffect } from 'react';
|
|
import { Modal } from '@mantine/core';
|
|
import { Dropzone } from '@mantine/dropzone';
|
|
import { FileMetadata } from '../types/file';
|
|
import { useFileManager } from '../hooks/useFileManager';
|
|
import { useFilesModalContext } from '../contexts/FilesModalContext';
|
|
import { createFileId } from '../types/fileContext';
|
|
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<FileManagerProps> = ({ selectedTool }) => {
|
|
const { isFilesModalOpen, closeFilesModal, onFilesSelect, onStoredFilesSelect } = useFilesModalContext();
|
|
const [recentFiles, setRecentFiles] = useState<FileMetadata[]>([]);
|
|
const [isDragging, setIsDragging] = useState(false);
|
|
const [isMobile, setIsMobile] = useState(false);
|
|
|
|
const { loadRecentFiles, handleRemoveFile, storeFile, convertToFile } = useFileManager();
|
|
|
|
// Wrapper for storeFile that generates UUID
|
|
const storeStirlingFile = useCallback(async (file: File) => {
|
|
const fileId = createFileId(); // Generate UUID for storage
|
|
return await storeFile(file, fileId);
|
|
}, [storeFile]);
|
|
|
|
// 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: FileMetadata[]) => {
|
|
try {
|
|
// Use stored files flow that preserves original IDs
|
|
const filesWithMetadata = await Promise.all(
|
|
files.map(async (metadata) => ({
|
|
file: await convertToFile(metadata),
|
|
originalId: metadata.id,
|
|
metadata
|
|
}))
|
|
);
|
|
onStoredFilesSelect(filesWithMetadata);
|
|
} catch (error) {
|
|
console.error('Failed to process selected files:', error);
|
|
}
|
|
}, [convertToFile, onStoredFilesSelect]);
|
|
|
|
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 () => {
|
|
// FileMetadata doesn't have blob URLs, so no cleanup needed
|
|
// Blob URLs are managed by FileContext and tool operations
|
|
console.log('FileManager unmounting - FileContext handles blob URL cleanup');
|
|
};
|
|
}, []);
|
|
|
|
// 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 (
|
|
<Modal
|
|
opened={isFilesModalOpen}
|
|
onClose={closeFilesModal}
|
|
size={isMobile ? "100%" : "auto"}
|
|
centered
|
|
radius="md"
|
|
className="overflow-hidden p-0"
|
|
withCloseButton={false}
|
|
styles={{
|
|
content: {
|
|
position: 'relative',
|
|
margin: isMobile ? '1rem' : '2rem'
|
|
},
|
|
body: { padding: 0 },
|
|
header: { display: 'none' }
|
|
}}
|
|
>
|
|
<div style={{
|
|
position: 'relative',
|
|
height: modalHeight,
|
|
width: modalWidth,
|
|
maxWidth: modalMaxWidth,
|
|
maxHeight: modalMaxHeight,
|
|
minWidth: modalMinWidth,
|
|
margin: '0 auto',
|
|
overflow: 'hidden'
|
|
}}>
|
|
<Dropzone
|
|
onDrop={handleNewFileUpload}
|
|
onDragEnter={() => setIsDragging(true)}
|
|
onDragLeave={() => setIsDragging(false)}
|
|
accept={{}}
|
|
multiple={true}
|
|
activateOnClick={false}
|
|
style={{
|
|
height: '100%',
|
|
width: '100%',
|
|
border: 'none',
|
|
borderRadius: 'var(--radius-md)',
|
|
backgroundColor: 'var(--bg-file-manager)'
|
|
}}
|
|
styles={{
|
|
inner: { pointerEvents: 'all' }
|
|
}}
|
|
>
|
|
<FileManagerProvider
|
|
recentFiles={recentFiles}
|
|
onFilesSelected={handleFilesSelected}
|
|
onNewFilesSelect={handleNewFileUpload}
|
|
onClose={closeFilesModal}
|
|
isFileSupported={isFileSupported}
|
|
isOpen={isFilesModalOpen}
|
|
onFileRemove={handleRemoveFileByIndex}
|
|
modalHeight={modalHeight}
|
|
refreshRecentFiles={refreshRecentFiles}
|
|
>
|
|
{isMobile ? <MobileLayout /> : <DesktopLayout />}
|
|
</FileManagerProvider>
|
|
</Dropzone>
|
|
|
|
<DragOverlay isVisible={isDragging} />
|
|
</div>
|
|
</Modal>
|
|
);
|
|
};
|
|
|
|
export default FileManager;
|