From 69d4a697132af1ef7b224856edfa37cb3939c222 Mon Sep 17 00:00:00 2001 From: alma Date: Mon, 21 Apr 2025 19:46:01 +0200 Subject: [PATCH] mail page fix design --- app/courrier/page.tsx | 132 ++++++++++++++---------------------- components/ComposeEmail.tsx | 34 ++++++---- 2 files changed, 72 insertions(+), 94 deletions(-) diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index 86ea3b51..cb098b5d 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -328,93 +328,61 @@ const initialSidebarItems = [ } ]; -function getReplyBody(email: Email, type: 'reply' | 'reply-all' | 'forward'): string { - if (!email.body) return ''; +function getReplyBody(email: any, type: 'reply' | 'reply-all' | 'forward' = 'reply'): string { + let content = ''; - 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'); - } - - 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; + 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 = headerInfo.encoding === 'quoted-printable' - ? decodeQuotedPrintable(body, headerInfo.charset) - : body; + content = email.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 ''; + + // 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}
`; } + + return ''; } export default function CourrierPage() { diff --git a/components/ComposeEmail.tsx b/components/ComposeEmail.tsx index 17d4153e..0be0c10c 100644 --- a/components/ComposeEmail.tsx +++ b/components/ComposeEmail.tsx @@ -1,10 +1,17 @@ 'use client'; -import { useRef, useEffect } from 'react'; +import { useRef, useEffect, useState } 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; @@ -49,17 +56,22 @@ export default function ComposeEmail({ setAttachments, handleSend }: ComposeEmailProps) { - const composeBodyRef = useRef(null); + const editorRef = useRef(null); + const [direction, setDirection] = useState<'ltr' | 'rtl'>('ltr'); useEffect(() => { - if (composeBodyRef.current) { - composeBodyRef.current.innerHTML = composeBody; + if (editorRef.current) { + editorRef.current.innerHTML = composeBody; + const plainText = editorRef.current.textContent || ''; + setDirection(detectDirection(plainText)); } }, [composeBody]); - const handleInput = (e: React.FormEvent) => { - if (composeBodyRef.current) { - setComposeBody(composeBodyRef.current.innerHTML); + const handleInput = () => { + if (editorRef.current) { + const plainText = editorRef.current.textContent || ''; + setDirection(detectDirection(plainText)); + setComposeBody(editorRef.current.innerHTML); } }; @@ -213,7 +225,7 @@ export default function ComposeEmail({ {/* Message Body */}