27375c1a4d
- Replace hardcoded Tailwind colors (bg-white, bg-gray-50/100, text-gray-, border-gray-, divide-gray-*, text-blue-600/800, bg-blue-50) with design tokens (bg-card, bg-muted, text-foreground, text-muted-foreground, border-
border, text-primary, hover:bg-accent, bg-primary/10).
- ClipboardApp: update RichTextEditor toolbar/editor, FileUploadHandler, ShareCard, FileListDisplay, SendTabPanel, RetrieveTabPanel, FileTransferButton.
- Blog UI: unify styles in list page, tag page, post page, ArticleListItem, and TableOfContents.
- MDX/prose: normalize pre/code/table/blockquote/lists and figure captions; switch rehype table divider to theme token.
- Misc: adjust HomeClient and HowItWorks copy colors to tokens.
- No functional changes; light mode parity; improved contrast and consistency in dark mode.
223 lines
6.6 KiB
TypeScript
223 lines
6.6 KiB
TypeScript
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
|
|
|
|
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<FileUploadHandlerProps> = ({
|
|
onFilePicked,
|
|
}) => {
|
|
const locale = useLocale();
|
|
const [messages, setMessages] = useState<Messages>(en); // Use English dictionary as initial value
|
|
|
|
const folderInputRef = useRef<HTMLInputElement>(null);
|
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
// File selector -- message prompt
|
|
const [fileText, setFileText] = useState<string>(
|
|
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 = "";
|
|
}
|
|
if (folderInputRef.current) {
|
|
folderInputRef.current.value = "";
|
|
}
|
|
},
|
|
[messages, onFilePicked]
|
|
);
|
|
|
|
// Click to upload file processing
|
|
const handleFileInputChange = useCallback(
|
|
(e: ChangeEvent<HTMLInputElement>) => {
|
|
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<HTMLInputElement>) => {
|
|
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 <div>Loading...</div>;
|
|
}
|
|
return (
|
|
<>
|
|
<div
|
|
className="border-2 border-dashed border-border rounded-lg p-6 text-center cursor-pointer"
|
|
onClick={handleZoneClick}
|
|
>
|
|
<p className="text-sm text-muted-foreground mb-4">
|
|
{messages.text.fileUploadHandler.chooseFileTips}
|
|
</p>
|
|
<Upload className="h-12 w-12 mx-auto mb-4 text-primary" />
|
|
<p className="text-sm text-muted-foreground">{fileText}</p>
|
|
|
|
<Input
|
|
id="file-upload"
|
|
type="file"
|
|
onChange={handleFileInputChange}
|
|
multiple
|
|
className="hidden"
|
|
ref={fileInputRef}
|
|
/>
|
|
<Input
|
|
id="folder-upload"
|
|
type="file"
|
|
onChange={handleFolderInputChange}
|
|
multiple
|
|
webkitdirectory=""
|
|
directory=""
|
|
className="hidden"
|
|
ref={folderInputRef}
|
|
/>
|
|
</div>
|
|
|
|
<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
|
|
<DialogContent className="sm:max-w-[425px]">
|
|
<DialogHeader>
|
|
<DialogTitle className="text-xl font-semibold">
|
|
{messages.text.fileUploadHandler.chosenDiagTitle}
|
|
</DialogTitle>
|
|
<DialogDescription className="mt-2 text-muted-foreground">
|
|
{messages.text.fileUploadHandler.chosenDiagDescription}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<div className="flex justify-center gap-4 mt-6">
|
|
<button
|
|
onClick={handleSelectFile}
|
|
className="px-4 py-2 rounded transition-colors bg-primary text-primary-foreground hover:bg-primary/90"
|
|
>
|
|
{messages.text.fileUploadHandler.SelectFile_dis}
|
|
</button>
|
|
<button
|
|
onClick={handleSelectFolder}
|
|
className="px-4 py-2 rounded transition-colors bg-secondary text-secondary-foreground hover:bg-secondary/80"
|
|
>
|
|
{messages.text.fileUploadHandler.SelectFolder_dis}
|
|
</button>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export { FileUploadHandler };
|