From c2395e51d1898f26b2238a17f6340a6bbd7bb329 Mon Sep 17 00:00:00 2001 From: alma Date: Wed, 16 Apr 2025 12:28:54 +0200 Subject: [PATCH] mail page ui correction maj compose 9 --- app/mail/page.tsx | 186 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 153 insertions(+), 33 deletions(-) diff --git a/app/mail/page.tsx b/app/mail/page.tsx index 730de5a..235298b 100644 --- a/app/mail/page.tsx +++ b/app/mail/page.tsx @@ -47,6 +47,13 @@ interface Email { category: string; } +interface Attachment { + name: string; + type: string; + content: string; + encoding: string; +} + // Improved MIME Decoder Implementation for Infomaniak function extractBoundary(headers: string): string | null { const boundaryMatch = headers.match(/boundary="?([^"\r\n;]+)"?/i) || @@ -451,7 +458,7 @@ export default function MailPage() { const [itemToDelete, setItemToDelete] = useState(null); const [showCc, setShowCc] = useState(false); const [contentLoading, setContentLoading] = useState(false); - const [attachments, setAttachments] = useState([]); + const [attachments, setAttachments] = useState([]); // Move getSelectedEmail inside the component const getSelectedEmail = () => { @@ -621,7 +628,7 @@ export default function MailPage() { } }; - // Handle reply + // Handle reply with MIME encoding const handleReply = async (type: 'reply' | 'replyAll' | 'forward') => { const selectedEmailData = getSelectedEmail(); if (!selectedEmailData) return; @@ -629,25 +636,158 @@ export default function MailPage() { setShowCompose(true); const subject = `${type === 'forward' ? 'Fwd: ' : 'Re: '}${selectedEmailData.subject}`; let to = ''; + let cc = ''; let content = ''; + // Parse the original email content using MIME decoder + const parsedEmail = parseFullEmail(selectedEmailData.body); + const decodedBody = parsedEmail.text || parsedEmail.html || selectedEmailData.body; + + // Format the date properly + const emailDate = new Date(selectedEmailData.date).toLocaleString('en-US', { + weekday: 'short', + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + hour12: true + }); + switch (type) { case 'reply': to = selectedEmailData.from; - content = `\n\nOn ${new Date(selectedEmailData.date).toLocaleString()}, ${selectedEmailData.fromName} wrote:\n> ${selectedEmailData.body.split('\n').join('\n> ')}`; + content = `\n\nOn ${emailDate}, ${selectedEmailData.fromName} wrote:\n> ${decodedBody.split('\n').join('\n> ')}`; break; + case 'replyAll': to = selectedEmailData.from; - content = `\n\nOn ${new Date(selectedEmailData.date).toLocaleString()}, ${selectedEmailData.fromName} wrote:\n> ${selectedEmailData.body.split('\n').join('\n> ')}`; + // Extract CC addresses from the original email, excluding our own address + const originalCc = selectedEmailData.cc || ''; + const ourEmail = accounts.find(acc => acc.id === selectedEmailData.accountId)?.email || ''; + const ccAddresses = originalCc + .split(',') + .map(addr => addr.trim()) + .filter(addr => addr !== ourEmail && addr !== selectedEmailData.from); + cc = ccAddresses.join(', '); + content = `\n\nOn ${emailDate}, ${selectedEmailData.fromName} wrote:\n> ${decodedBody.split('\n').join('\n> ')}`; break; + case 'forward': - content = `\n\n---------- Forwarded message ----------\nFrom: ${selectedEmailData.fromName} <${selectedEmailData.from}>\nDate: ${new Date(selectedEmailData.date).toLocaleString()}\nSubject: ${selectedEmailData.subject}\n\n${selectedEmailData.body}`; + // Include original attachments if any + const attachmentInfo = parsedEmail.attachments.length > 0 + ? '\n\n-------- Attachments --------\n' + + parsedEmail.attachments.map(att => att.filename).join('\n') + : ''; + + content = `\n\n---------- Forwarded message ----------\n` + + `From: ${selectedEmailData.fromName} <${selectedEmailData.from}>\n` + + `Date: ${emailDate}\n` + + `Subject: ${selectedEmailData.subject}\n` + + `To: ${selectedEmailData.to}\n` + + (selectedEmailData.cc ? `Cc: ${selectedEmailData.cc}\n` : '') + + `\n\n${decodedBody}${attachmentInfo}`; break; } + // Set the form state setComposeSubject(subject); setComposeTo(to); - setComposeBody(cleanEmailContent(content)); + setComposeCc(cc); + setComposeBody(content); + + // Show CC field if there are CC recipients + if (cc) { + setShowCc(true); + } + + // Handle attachments for forwarded emails + if (type === 'forward' && parsedEmail.attachments.length > 0) { + // You'll need to implement attachment handling here + // This could involve creating File objects from the attachment data + // or storing the attachment information to be processed when sending + handleForwardedAttachments(parsedEmail.attachments); + } + }; + + // Handle forwarded attachments + const handleForwardedAttachments = (attachments: Array<{ + filename: string; + contentType: string; + encoding: string; + content: string; + }>) => { + // Store attachment information in state + setAttachments(attachments.map(att => ({ + name: att.filename, + type: att.contentType, + content: att.content, + encoding: att.encoding + }))); + }; + + // Modified send function to handle MIME encoding + const handleSend = async () => { + try { + // Create multipart message + const boundary = `----=_Part_${Date.now()}`; + let mimeContent = [ + `Content-Type: multipart/mixed; boundary="${boundary}"`, + '', + `--${boundary}`, + 'Content-Type: text/plain; charset=utf-8', + 'Content-Transfer-Encoding: 7bit', + '', + composeBody, + '' + ]; + + // Add attachments if any + if (attachments.length > 0) { + for (const attachment of attachments) { + mimeContent = mimeContent.concat([ + `--${boundary}`, + `Content-Type: ${attachment.type}`, + `Content-Transfer-Encoding: base64`, + `Content-Disposition: attachment; filename="${attachment.name}"`, + '', + attachment.content, + '' + ]); + } + } + + // Close the multipart message + mimeContent.push(`--${boundary}--`); + + // Send the email with MIME content + await fetch('/api/mail/send', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + to: composeTo, + cc: composeCc, + bcc: composeBcc, + subject: composeSubject, + body: mimeContent.join('\r\n'), + attachments: attachments + }) + }); + + // Clear the form + setShowCompose(false); + setComposeTo(''); + setComposeCc(''); + setComposeBcc(''); + setComposeSubject(''); + setComposeBody(''); + setShowCc(false); + setShowBcc(false); + setAttachments([]); + + } catch (error) { + console.error('Error sending email:', error); + } }; const handleBulkDelete = () => { @@ -695,7 +835,12 @@ export default function MailPage() { const handleFileAttachment = (e: React.ChangeEvent) => { if (e.target.files) { - setAttachments(Array.from(e.target.files)); + setAttachments(Array.from(e.target.files).map(file => ({ + name: file.name, + type: file.type, + content: URL.createObjectURL(file), + encoding: 'base64' + }))); } }; @@ -1231,32 +1376,7 @@ export default function MailPage() {