From 127765069f9f3b503f0ce906a055e727891b569b Mon Sep 17 00:00:00 2001 From: alma Date: Mon, 21 Apr 2025 19:46:51 +0200 Subject: [PATCH] mail page fix design --- app/courrier/page.tsx | 300 ++++++++++++++++++++---------------- components/ComposeEmail.tsx | 50 ++---- 2 files changed, 181 insertions(+), 169 deletions(-) diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index cb098b5d..290d5d90 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -328,61 +328,93 @@ const initialSidebarItems = [ } ]; -function getReplyBody(email: any, type: 'reply' | 'reply-all' | 'forward' = 'reply'): string { - let content = ''; +function getReplyBody(email: Email, type: 'reply' | 'reply-all' | 'forward'): string { + if (!email.body) return ''; - if (email.body) { - // Handle multipart emails - if (email.body.includes('Content-Type: multipart/alternative')) { - const parts = email.body.split('--'); - for (const part of parts) { - if (part.includes('Content-Type: text/html')) { - content = part.split('\n\n')[1] || ''; - break; - } - } - } else { - content = email.body; + try { + // Split email into headers and body + const [headersPart, ...bodyParts] = email.body.split('\r\n\r\n'); + if (!headersPart || bodyParts.length === 0) { + throw new Error('Invalid email format: missing headers or body'); } - // Clean and structure the content - content = content - .replace(//gi, '\n') - .replace(/

/gi, '\n') - .replace(/<\/p>/gi, '\n') - .replace(/

/gi, '\n') - .replace(/<\/div>/gi, '\n') - .trim(); - - // Convert plain text to HTML while preserving formatting - content = content - .split('\n') - .map(line => `

${line}

`) - .join(''); - - // Add proper quoting structure - const quotedContent = ` -
- ${content} -
- `; - - // Add metadata based on type - const metadata = ` -
- ${type === 'forward' ? 'Forwarded message' : 'Original message'}
- From: ${email.from}
- Date: ${new Date(email.date).toLocaleString()}
- Subject: ${email.subject} -
- `; - - return type === 'forward' - ? `
${metadata}${quotedContent}
` - : `


${metadata}${quotedContent}
`; + const body = bodyParts.join('\r\n\r\n'); + + // Parse headers using Infomaniak MIME decoder + const headerInfo = parseEmailHeaders(headersPart); + const boundary = extractBoundary(headersPart); + + let content = ''; + + // If it's a multipart email + if (boundary) { + const parts = body.split(`--${boundary}`); + + // Find HTML part first, fallback to text part + const htmlPart = parts.find(part => part.toLowerCase().includes('content-type: text/html')); + const textPart = parts.find(part => part.toLowerCase().includes('content-type: text/plain')); + + const selectedPart = htmlPart || textPart; + if (selectedPart) { + const [partHeaders, ...partBodyParts] = selectedPart.split('\r\n\r\n'); + const partBody = partBodyParts.join('\r\n\r\n'); + const partHeaderInfo = parseEmailHeaders(partHeaders); + + content = partHeaderInfo.encoding === 'quoted-printable' + ? decodeQuotedPrintable(partBody, partHeaderInfo.charset) + : partBody; + } + } else { + content = headerInfo.encoding === 'quoted-printable' + ? decodeQuotedPrintable(body, headerInfo.charset) + : body; + } + + // Convert plain text to HTML if needed + if (!headerInfo.contentType.includes('text/html')) { + content = content + .split('\n') + .map(line => { + if (!line.trim()) return '
'; + if (line.startsWith('>')) { + return `

${line}

`; + } + return `

${line}

`; + }) + .join(''); + } + + // Clean HTML content + content = cleanHtml(content); + + const date = new Date(email.date).toLocaleString(); + + if (type === 'forward') { + return ` +
+
+

From: ${email.from}

+

Date: ${date}

+

Subject: ${email.subject}

+

To: ${Array.isArray(email.to) ? email.to.join(', ') : email.to}

+
${content}
+
+
+ `; + } else { + return ` +
+
+

On ${date}, ${email.from} wrote:

+
${content}
+
+
+ `; + } + } catch (error) { + console.error('Error processing email body:', error); + return ''; } - - return ''; } export default function CourrierPage() { @@ -1492,92 +1524,92 @@ export default function CourrierPage() {
- {/* Sidebar */} -
- {/* Courrier Title */} -
-
- - COURRIER -
-
- - {/* Compose button and refresh button */} -
- - -
- - {/* Accounts Section */} -
- - - {accountsDropdownOpen && ( -
- {accounts.map(account => ( -
- -
- ))} -
- )} -
- - {/* Navigation */} - {renderSidebarNav()} -
- - {/* Main content area */} -
- {/* Email list panel */} - {renderEmailListWrapper()} + {/* Sidebar */} +
+ {/* Courrier Title */} +
+
+ + COURRIER
+ + {/* Compose button and refresh button */} +
+ + +
+ + {/* Accounts Section */} +
+ + + {accountsDropdownOpen && ( +
+ {accounts.map(account => ( +
+ +
+ ))} +
+ )} +
+ + {/* Navigation */} + {renderSidebarNav()}
+ + {/* Main content area */} +
+ {/* Email list panel */} + {renderEmailListWrapper()} +
+
+
{/* Compose Email Modal */} diff --git a/components/ComposeEmail.tsx b/components/ComposeEmail.tsx index 0be0c10c..02c457eb 100644 --- a/components/ComposeEmail.tsx +++ b/components/ComposeEmail.tsx @@ -1,18 +1,12 @@ 'use client'; -import { useRef, useEffect, useState } from 'react'; +import { useRef, useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Paperclip, X } from 'lucide-react'; import { Textarea } from '@/components/ui/textarea'; -// Direction detection utility -function detectDirection(text: string): 'rtl' | 'ltr' { - const rtlChars = /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/; - return rtlChars.test(text) ? 'rtl' : 'ltr'; -} - interface ComposeEmailProps { showCompose: boolean; setShowCompose: (show: boolean) => void; @@ -56,22 +50,17 @@ export default function ComposeEmail({ setAttachments, handleSend }: ComposeEmailProps) { - const editorRef = useRef(null); - const [direction, setDirection] = useState<'ltr' | 'rtl'>('ltr'); + const composeBodyRef = useRef(null); useEffect(() => { - if (editorRef.current) { - editorRef.current.innerHTML = composeBody; - const plainText = editorRef.current.textContent || ''; - setDirection(detectDirection(plainText)); + if (composeBodyRef.current) { + composeBodyRef.current.innerHTML = composeBody; } }, [composeBody]); - const handleInput = () => { - if (editorRef.current) { - const plainText = editorRef.current.textContent || ''; - setDirection(detectDirection(plainText)); - setComposeBody(editorRef.current.innerHTML); + const handleInput = (e: React.FormEvent) => { + if (composeBodyRef.current) { + setComposeBody(composeBodyRef.current.innerHTML); } }; @@ -223,23 +212,14 @@ export default function ComposeEmail({
{/* Message Body */} -
-
+ +