Browse Source

feat:editor: added support for image copypasting

Adam Jafarov 3 tuần trước cách đây
mục cha
commit
214abcc978
3 tập tin đã thay đổi với 64 bổ sung6 xóa
  1. 5 5
      backend/server.js
  2. 1 1
      public/posts
  3. 58 0
      src/components/PostEditor.jsx

+ 5 - 5
backend/server.js

@@ -59,11 +59,11 @@ const storage = multer.diskStorage({
         }
     },
     filename: function (req, file, cb) {
-        // Keep original filename but ensure uniqueness if needed? 
-        // For now, simple filename is better for markdown readability.
-        // We'll replace spaces with dashes.
-        const cleanName = file.originalname.replace(/[^a-z0-9.]/gi, "-").toLowerCase();
-        cb(null, cleanName); // Overwrites if exists, which might be desired behavior for updating images
+        const ext = path.extname(file.originalname).toLowerCase();
+        const namePart = path.basename(file.originalname, ext).replace(/[^a-z0-9]/gi, "-").toLowerCase();
+        const timestamp = Date.now();
+        const newFilename = `${namePart}_${timestamp}${ext}`;
+        cb(null, newFilename);
     },
 });
 

+ 1 - 1
public/posts

@@ -1 +1 @@
-Subproject commit 3434e83782a318ac05d0dfeb100f7ef07b78cb0d
+Subproject commit 270ea61a03bb97eb7e1b3adac45f25d8abeca313

+ 58 - 0
src/components/PostEditor.jsx

@@ -112,6 +112,63 @@ function PostEditor() {
         }));
     };
 
+    const handlePaste = async (event) => {
+        const items = event.clipboardData.items;
+        for (const item of items) {
+            if (item.type.indexOf("image") === 0) {
+                event.preventDefault();
+                const file = item.getAsFile();
+                if (!file) continue;
+
+                const tempSlug = getSlugForUpload() || "uploads";
+                const placeholder = `![Uploading ${file.name}...]()...`;
+
+                // Insert placeholder
+                // We use api.replaceSelection if we had access to 'api' from MDEditor, 
+                // but here we are in a raw paste handler on the textarea.
+                // We'll append for now or try to use a more sophisticated insertion if possible,
+                // but sticking to insertTextAtCursor is safer for state consistency.
+                // BETTER: MDEditor's `paste` command? No, native onPaste is best.
+                // Native paste on textarea doesn't give us cursor position easily in React state flow 
+                // without ref manipulation.
+                // Let's use the standard "append" or try to insert at end.
+                // Actually, let's use the replaceSelection approach if we can get a ref to the editor instance,
+                // but we don't have it easily. 
+                // We will append to content with a newline for simplicity and reliability.
+
+                setFormData(prev => ({ ...prev, content: prev.content + "\n" + placeholder }));
+
+                try {
+                    const formData = new FormData();
+                    formData.append("file", file);
+
+                    const response = await fetch(`${API_BASE}/upload?slug=${tempSlug}`, {
+                        method: "POST",
+                        body: formData,
+                        credentials: "include",
+                    });
+
+                    if (!response.ok) throw new Error("Upload failed");
+
+                    const data = await response.json();
+
+                    // Replace placeholder with actual image markdown
+                    setFormData(prev => ({
+                        ...prev,
+                        content: prev.content.replace(placeholder, `![${data.originalName}](${API_BASE}${data.url})`)
+                    }));
+
+                } catch (error) {
+                    console.error("Paste upload error:", error);
+                    setFormData(prev => ({
+                        ...prev,
+                        content: prev.content.replace(placeholder, `[Upload Failed: ${error.message}]`)
+                    }));
+                }
+            }
+        }
+    };
+
     // Gallery selection handler
     const handleImageSelect = (url, name) => {
         // We append to end since we lose cursor position when modal opens/closes
@@ -392,6 +449,7 @@ function PostEditor() {
                                     visibleDragBar={false}
                                     textareaProps={{
                                         placeholder: "Tell your story...",
+                                        onPaste: handlePaste
                                     }}
                                     className="shadow-sm" // Add slight shadow for separation
                                 />