import EventEmitter from 'events'; import { ChangeEventHandler, FormEventHandler, useEffect, useRef, useState, } from 'react'; import { Box, Input, InputLabel } from '@mui/material'; import { UPLOAD_FILE_TYPES } from '../../lib/consts/UPLOAD_FILE_TYPES'; import { ProgressBar } from '../Bars'; import ContainedButton from '../ContainedButton'; import FileInfo from './FileInfo'; import MessageBox from '../MessageBox'; import { BodyText } from '../Text'; import mainAxiosInstance from '../../lib/singletons/mainAxiosInstance'; type FileUploadFormProps = { onFileUploadComplete?: () => void; eventEmitter?: EventEmitter; }; type SelectedFile = Pick< FileDetailMetadata, 'fileName' | 'fileLocations' | 'fileType' > & { file: File; }; type InUploadFile = Pick & { progressValue: number; }; const FILE_UPLOAD_FORM_DEFAULT_PROPS: Partial = { onFileUploadComplete: undefined, eventEmitter: undefined, }; const FileUploadForm = ( { onFileUploadComplete, eventEmitter, }: FileUploadFormProps = FILE_UPLOAD_FORM_DEFAULT_PROPS as FileUploadFormProps, ): JSX.Element => { const selectFileRef = useRef(); const [selectedFiles, setSelectedFiles] = useState([]); const [inUploadFiles, setInUploadFiles] = useState([]); const convertMIMETypeToFileTypeKey = (fileMIMEType: string): FileType => { const fileTypesIterator = UPLOAD_FILE_TYPES.entries(); let fileType: FileType | undefined; do { const fileTypesResult = fileTypesIterator.next(); if (fileTypesResult.value) { const [fileTypeKey, [mimeTypeToUse]] = fileTypesResult.value; if (fileMIMEType === mimeTypeToUse) { fileType = fileTypeKey; } } else { fileType = 'other'; } } while (!fileType); return fileType; }; const autocompleteAfterSelectFile: ChangeEventHandler = ({ target: { files }, }) => { if (files) { setSelectedFiles( Array.from(files).map( (file): SelectedFile => ({ file, fileName: file.name, fileLocations: [], fileType: convertMIMETypeToFileTypeKey(file.type), }), ), ); } }; const generateFileInfoOnChangeHandler = (fileIndex: number): FileInfoChangeHandler => (inputValues) => { selectedFiles[fileIndex] = { ...selectedFiles[fileIndex], ...inputValues, }; }; const uploadFiles: FormEventHandler = (event) => { event.preventDefault(); while (selectedFiles.length > 0) { const selectedFile = selectedFiles.shift(); if (selectedFile) { const { file, fileName } = selectedFile; const fileFormData = new FormData(); fileFormData.append('file', new File([file], fileName, { ...file })); // Re-add when the back-end tools can support changing file type on file upload. // Note: get file type from destructuring selectedFile. // fileFormData.append('file-type', fileType); const inUploadFile: InUploadFile = { fileName, progressValue: 0 }; inUploadFiles.push(inUploadFile); mainAxiosInstance .post('/files', fileFormData, { headers: { 'Content-Type': 'multipart/form-data', }, onUploadProgress: ({ loaded, total }) => { inUploadFile.progressValue = Math.round((loaded / total) * 100); setInUploadFiles([...inUploadFiles]); }, }) .then(() => { onFileUploadComplete?.call(null); inUploadFiles.splice(inUploadFiles.indexOf(inUploadFile), 1); setInUploadFiles([...inUploadFiles]); }); } } // Clears "staging area" (selected files) and populates "in-progress area" (in-upload files). setSelectedFiles([]); setInUploadFiles([...inUploadFiles]); }; useEffect(() => { eventEmitter?.addListener('openFilePicker', () => { selectFileRef.current?.click(); }); eventEmitter?.addListener('clearSelectedFiles', () => { setSelectedFiles([]); }); }, [eventEmitter]); return (
{inUploadFiles.map(({ fileName, progressValue }) => ( :first-child': { minWidth: 100, overflow: 'hidden', overflowWrap: 'normal', textOverflow: 'ellipsis', whiteSpace: 'nowrap', width: { xs: '100%', md: 200 }, wordBreak: 'keep-all', }, '& > :last-child': { flexGrow: 1 }, }} > ))} :not(:first-child)': { marginTop: '1em' }, }} > {selectedFiles.length > 0 && ( )} {selectedFiles.map( ( { file: { name: originalFileName }, fileName, // Re-add when the back-end tools can support changing file type on file upload. // Note: file type must be supplied to FileInfo. // fileType, fileLocations, }, fileIndex, ) => ( ), )} {selectedFiles.length > 0 && ( Upload )}
); }; FileUploadForm.defaultProps = FILE_UPLOAD_FORM_DEFAULT_PROPS; export default FileUploadForm;