From f19aaf6207a46a961d3df51ddd23e63e16808f7a Mon Sep 17 00:00:00 2001 From: Linus Rath Date: Thu, 9 Apr 2026 19:50:42 +0200 Subject: [PATCH] fix: embed dropped images as data URLs and prevent duplicate attachment #163 --- components/email/email-composer.tsx | 23 ++++++++++++----------- components/email/rich-text-editor.tsx | 1 + 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/components/email/email-composer.tsx b/components/email/email-composer.tsx index c98bfd77..06a49912 100644 --- a/components/email/email-composer.tsx +++ b/components/email/email-composer.tsx @@ -532,17 +532,18 @@ export function EmailComposer({ } }, [client, t]); - const handleImageUpload = useCallback(async (file: File): Promise => { - if (!client) return null; - try { - const { blobId } = await client.uploadBlob(file); - return await client.fetchBlobAsObjectUrl(blobId, file.name, file.type); - } catch (error) { - debug.error(`Failed to upload inline image ${file.name}:`, error); - toast.error(t('upload_failed', { filename: file.name })); - return null; - } - }, [client, t]); + const handleImageUpload = useCallback((file: File): Promise => { + return new Promise((resolve) => { + const reader = new FileReader(); + reader.onload = (e) => resolve((e.target?.result as string) ?? null); + reader.onerror = () => { + debug.error(`Failed to read inline image ${file.name}`); + toast.error(t('upload_failed', { filename: file.name })); + resolve(null); + }; + reader.readAsDataURL(file); + }); + }, [t]); const handleFileSelect = async (event: React.ChangeEvent) => { if (!event.target.files) return; diff --git a/components/email/rich-text-editor.tsx b/components/email/rich-text-editor.tsx index 144286bf..a6c966f3 100644 --- a/components/email/rich-text-editor.tsx +++ b/components/email/rich-text-editor.tsx @@ -118,6 +118,7 @@ export function RichTextEditor({ ); if (imageFiles.length === 0) return false; event.preventDefault(); + event.stopPropagation(); for (const file of imageFiles) { upload(file).then((url) => { if (url) {