import { useState, useEffect } from "react"; import type { DragEvent } from "react"; import { UploadImageBytes, UploadImage, SelectImageVideo } from "../../wailsjs/go/main/App"; import { Upload, Loader2, ImagePlus, X, Check, FileVideo, ImageIcon } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; interface UploadedFile { id: string; name: string; url: string; type: 'image' | 'video' | 'unknown'; status: 'uploading' | 'done' | 'error'; error?: string; } interface DragDropMediaProps { value: string; onChange: (value: string) => void; className?: string; } export function DragDropMedia({ value, onChange, className }: DragDropMediaProps) { const [isDragging, setIsDragging] = useState(false); const [files, setFiles] = useState(() => { if (!value) return []; return value.split('\n').filter(line => line.trim()).map((line, i) => { const match = line.match(/!\[(.*?)\]\((.*?)\)/); if (match) { return { id: `init-${i}-${Date.now()}`, name: match[1] === 'image' || match[1] === 'video' ? `file-${i}` : match[1], url: match[2] || line, type: (match[2] && match[2].match(/\.(mp4|mkv|webm|mov)$/i)) ? 'video' : 'image', status: 'done' }; } return { id: `init-${i}-${Date.now()}`, name: 'unknown', url: line, type: 'image', status: 'done' }; }); }); useEffect(() => { const newValue = files .filter(f => f.status === 'done' && f.url) .map(f => f.url) .join('\n'); if (newValue !== value) { onChange(newValue); } }, [files]); const handleDragOver = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(true); }; const handleDragLeave = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); }; const handleDrop = async (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { await handleFiles(Array.from(e.dataTransfer.files)); } }; const handleFiles = async (fileList: File[]) => { const timestamp = Date.now(); const newFiles: UploadedFile[] = fileList.map((f, i) => ({ id: `drop-${timestamp}-${i}`, name: f.name, url: '', type: f.type.startsWith('video') ? 'video' : 'image', status: 'uploading' })); setFiles(prev => [...prev, ...newFiles]); for (let i = 0; i < fileList.length; i++) { const file = fileList[i]; const fileId = newFiles[i].id; try { const base64 = await fileToBase64(file); const result = await UploadImageBytes(file.name, base64); setFiles(prev => prev.map(f => f.id === fileId ? { ...f, status: 'done', url: result } : f)); } catch (err: any) { console.error("Upload failed", err); setFiles(prev => prev.map(f => f.id === fileId ? { ...f, status: 'error', error: err.message || "Upload failed" } : f)); } } }; const handleSelectFile = async () => { try { const paths = await SelectImageVideo(); if (paths && paths.length > 0) { const timestamp = Date.now(); const newFiles: UploadedFile[] = paths.map((p, i) => ({ id: `select-${timestamp}-${i}`, name: p.split(/[\\/]/).pop() || 'unknown', url: '', type: p.match(/\.(mp4|mkv|webm|mov)$/i) ? 'video' : 'image', status: 'uploading' })); setFiles(prev => [...prev, ...newFiles]); for (let i = 0; i < paths.length; i++) { const path = paths[i]; const fileId = newFiles[i].id; try { const result = await UploadImage(path); setFiles(prev => prev.map(f => f.id === fileId ? { ...f, status: 'done', url: result } : f)); } catch (err: any) { setFiles(prev => prev.map(f => f.id === fileId ? { ...f, status: 'error', error: err.message } : f)); } } } } catch (err: any) { console.error("Select file failed", err); } }; const removeFile = (index: number) => { setFiles(prev => prev.filter((_, i) => i !== index)); }; return (
{ if (e.target === e.currentTarget) handleSelectFile(); }}> {files.length === 0 && (
Drop media here or click to browse Supports PNG, JPG, MP4, MOV
)}
{files.map((file, i) => (
{file.type === 'video' ? : }
{file.name}
{file.status === 'uploading' && Uploading...} {file.status === 'done' && Ready} {file.status === 'error' && {file.error || 'Failed'}}
))}
{isDragging && (
Drop files to add
)}
); } const fileToBase64 = (file: File): Promise => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result as string); reader.onerror = (error) => reject(error); }); };