import React, { useState, useEffect, ChangeEvent, useRef, useCallback, } from "react"; import { Input } from "@/components/ui/input"; import { Upload } from "lucide-react"; import { FileMeta, CustomFile } from "@/types/webrtc"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from "@/components/ui/dialog"; // Add this declaration at the top of the file to extend existing types and avoid IDE errors declare module "@/components/ui/input" { interface InputProps { webkitdirectory?: string | boolean; directory?: string | boolean; } } import { getDictionary } from "@/lib/dictionary"; import { useLocale } from "@/hooks/useLocale"; import type { Messages } from "@/types/messages"; import { en } from "@/constants/messages/en"; // Import English dictionary as default const traverseFileTree = async ( item: FileSystemEntry, path = "" ): Promise => { return new Promise((resolve) => { // console.log('path',path)//path in ['','test/','test/sub/'] if (item.isFile) { (item as FileSystemFileEntry).file((file: File) => { // console.log('file.name',file.name)//file.name in ['Gmail-773240713232313363.txt','link.txt','cvat-serverless部署踩坑及部署模型测试 (1).docx','images.jpg'] // console.log('fullName',path + file.name,'folderName',path.split('/')[0]) const customFile: CustomFile = Object.assign(file, { fullName: path + file.name, folderName: path.split("/")[0], }); resolve([customFile]); }); } else if (item.isDirectory) { const dirReader = (item as FileSystemDirectoryEntry).createReader(); let entries: FileSystemEntry[] = []; const readEntries = () => { dirReader.readEntries(async (results) => { if (results.length) { entries = entries.concat(Array.from(results)); readEntries(); } else { const newPath = path + item.name + "/"; const subResults = await Promise.all( entries.map((entry) => traverseFileTree(entry, newPath)) ); // console.log('subResults',subResults) const files: CustomFile[] = subResults.flat(); // console.log('files',files) resolve(files); // Removed conditional judgment, directly return processed files } }); }; readEntries(); } }); }; function formatFileChosen( template: string, fileNum: number, folderNum: number ) { return template .replace("{fileNum}", fileNum.toString()) .replace("{folderNum}", folderNum.toString()); } interface FileUploadHandlerProps { onFilePicked: (files: CustomFile[]) => void; } const FileUploadHandler: React.FC = ({ onFilePicked, }) => { const locale = useLocale(); const [messages, setMessages] = useState(en); // Use English dictionary as initial value const dropZoneRef = useRef(null); // Drag and drop files to attachments -- support const folderInputRef = useRef(null); const fileInputRef = useRef(null); // File selector -- message prompt const [fileText, setFileText] = useState( en.text.fileUploadHandler.NoFileChosen_tips ); const [isModalOpen, setIsModalOpen] = useState(false); useEffect(() => { if (locale !== "en") { // Only load other language packs if not English getDictionary(locale) .then((dict) => { setMessages(dict); setFileText(dict.text.fileUploadHandler.NoFileChosen_tips); }) .catch((error) => console.error("Failed to load messages:", error)); } }, [locale]); const handleFileChange = useCallback( (newFiles: CustomFile[]) => { // console.log(newFiles); onFilePicked(newFiles); const fileNum = newFiles.length; const folderNum = newFiles.filter((file) => file.folderName).length; const choose_dis = formatFileChosen( messages!.text.fileUploadHandler.fileChosen_tips_template, fileNum, folderNum ); setFileText(choose_dis); setTimeout( () => setFileText(messages!.text.fileUploadHandler.NoFileChosen_tips), 2000 ); // Reset the file input if (fileInputRef.current) { fileInputRef.current.value = ""; } }, [messages, onFilePicked] ); // Drag and drop folder upload response processing const handleDrop = useCallback( (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); const items = e.dataTransfer.items; if (items) { const itemsArray = Array.from(items); Promise.all( itemsArray.map((item) => { const entry = item.webkitGetAsEntry(); if (entry) { return traverseFileTree(entry); } return Promise.resolve([]); }) ).then((results) => { const allFiles = results.flat(); handleFileChange(allFiles); }); } }, [handleFileChange] ); /* Define a callback function handleDragOver to handle the drag-over event. In handleDragOver, prevent default behavior and event propagation to ensure custom handling. There is no dependency array, which means the handleDragOver function will only be created once when the component first renders, and will not be re-created in subsequent renders. */ const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); }, []); // Click to upload file processing const handleFileInputChange = useCallback( (e: ChangeEvent) => { if (e.target.files) { const files = Array.from(e.target.files); let files2 = []; for (let file of files) { const customFile: CustomFile = Object.assign(file, { fullName: file.name, folderName: "", }); files2.push(customFile); } handleFileChange(files2); setIsModalOpen(false); // Close the dialog } }, [handleFileChange] ); // Click to upload folder response processing const handleFolderInputChange = useCallback( (e: ChangeEvent) => { if (e.target.files) { const files_ = Array.from(e.target.files); let files: CustomFile[] = []; files_.forEach((file) => { // console.log('file.webkitRelativePath',file.webkitRelativePath)//[test/Gmail-773240713232313363.txt,test/link.txt,test/sub/cvat-serverless部署踩坑及部署模型测试 (1).docx,test/sub/images.jpg] const pathParts = file.webkitRelativePath.split("/"); const customFile: CustomFile = Object.assign(file, { fullName: file.webkitRelativePath, folderName: pathParts[0], }); files.push(customFile); }); handleFileChange(files); setIsModalOpen(false); // Close the dialog } }, [handleFileChange] ); // Handle drag and drop area click const handleZoneClick = () => { setIsModalOpen(true); }; // Handle file selection const handleSelectFile = () => { fileInputRef.current?.click(); }; // Handle folder selection const handleSelectFolder = () => { folderInputRef.current?.click(); }; if (messages === null) { return
Loading...
; } return ( <>

{messages.text.fileUploadHandler.Drag_tips}

{fileText}

{messages.text.fileUploadHandler.chosenDiagTitle} {messages.text.fileUploadHandler.chosenDiagDescription}
); }; export { FileUploadHandler };