mail page fix design

This commit is contained in:
alma 2025-04-21 19:46:01 +02:00
parent 0549e3f020
commit 69d4a69713
2 changed files with 72 additions and 94 deletions

View File

@ -328,93 +328,61 @@ const initialSidebarItems = [
} }
]; ];
function getReplyBody(email: Email, type: 'reply' | 'reply-all' | 'forward'): string { function getReplyBody(email: any, type: 'reply' | 'reply-all' | 'forward' = 'reply'): string {
if (!email.body) return ''; let content = '';
try { if (email.body) {
// Split email into headers and body // Handle multipart emails
const [headersPart, ...bodyParts] = email.body.split('\r\n\r\n'); if (email.body.includes('Content-Type: multipart/alternative')) {
if (!headersPart || bodyParts.length === 0) { const parts = email.body.split('--');
throw new Error('Invalid email format: missing headers or body'); for (const part of parts) {
} if (part.includes('Content-Type: text/html')) {
content = part.split('\n\n')[1] || '';
const body = bodyParts.join('\r\n\r\n'); break;
}
// 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 { } else {
content = headerInfo.encoding === 'quoted-printable' content = email.body;
? decodeQuotedPrintable(body, headerInfo.charset)
: body;
} }
// Convert plain text to HTML if needed // Clean and structure the content
if (!headerInfo.contentType.includes('text/html')) { content = content
content = content .replace(/<br\s*\/?>/gi, '\n')
.split('\n') .replace(/<p>/gi, '\n')
.map(line => { .replace(/<\/p>/gi, '\n')
if (!line.trim()) return '<br>'; .replace(/<div>/gi, '\n')
if (line.startsWith('>')) { .replace(/<\/div>/gi, '\n')
return `<p class="text-gray-600" dir="ltr" style="unicode-bidi: bidi-override; direction: ltr;">${line}</p>`; .trim();
}
return `<p dir="ltr" style="unicode-bidi: bidi-override; direction: ltr;">${line}</p>`; // Convert plain text to HTML while preserving formatting
}) content = content
.join(''); .split('\n')
} .map(line => `<p>${line}</p>`)
.join('');
// Clean HTML content
content = cleanHtml(content); // Add proper quoting structure
const quotedContent = `
const date = new Date(email.date).toLocaleString(); <blockquote style="border-left: 2px solid #ccc; padding-left: 10px; margin: 10px 0 0 0">
${content}
if (type === 'forward') { </blockquote>
return ` `;
<div class="prose max-w-none" dir="ltr" style="unicode-bidi: bidi-override; direction: ltr;">
<div class="border-l-4 border-gray-300 pl-4 my-4"> // Add metadata based on type
<p class="text-sm text-gray-600 mb-2" dir="ltr" style="unicode-bidi: bidi-override; direction: ltr;"><strong>From:</strong> ${email.from}</p> const metadata = `
<p class="text-sm text-gray-600 mb-2" dir="ltr" style="unicode-bidi: bidi-override; direction: ltr;"><strong>Date:</strong> ${date}</p> <div style="color: #666; font-size: 0.9em; margin-bottom: 10px;">
<p class="text-sm text-gray-600 mb-2" dir="ltr" style="unicode-bidi: bidi-override; direction: ltr;"><strong>Subject:</strong> ${email.subject}</p> ${type === 'forward' ? 'Forwarded message' : 'Original message'}<br/>
<p class="text-sm text-gray-600 mb-2" dir="ltr" style="unicode-bidi: bidi-override; direction: ltr;"><strong>To:</strong> ${Array.isArray(email.to) ? email.to.join(', ') : email.to}</p> From: ${email.from}<br/>
<div class="mt-4 prose-sm" dir="ltr" style="unicode-bidi: bidi-override; direction: ltr;">${content}</div> Date: ${new Date(email.date).toLocaleString()}<br/>
</div> Subject: ${email.subject}
</div> </div>
`; `;
} else {
return ` return type === 'forward'
<div class="prose max-w-none" dir="ltr" style="unicode-bidi: bidi-override; direction: ltr;"> ? `<div>${metadata}${quotedContent}</div>`
<div class="border-l-4 border-gray-300 pl-4 my-4"> : `<div><br/><br/>${metadata}${quotedContent}</div>`;
<p class="text-sm text-gray-600 mb-2" dir="ltr" style="unicode-bidi: bidi-override; direction: ltr;">On ${date}, ${email.from} wrote:</p>
<div class="mt-4 prose-sm" dir="ltr" style="unicode-bidi: bidi-override; direction: ltr;">${content}</div>
</div>
</div>
`;
}
} catch (error) {
console.error('Error processing email body:', error);
return '';
} }
return '';
} }
export default function CourrierPage() { export default function CourrierPage() {

View File

@ -1,10 +1,17 @@
'use client'; 'use client';
import { useRef, useEffect } from 'react'; import { useRef, useEffect, useState } from 'react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Paperclip, X } from 'lucide-react'; 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 { interface ComposeEmailProps {
showCompose: boolean; showCompose: boolean;
@ -49,17 +56,22 @@ export default function ComposeEmail({
setAttachments, setAttachments,
handleSend handleSend
}: ComposeEmailProps) { }: ComposeEmailProps) {
const composeBodyRef = useRef<HTMLDivElement>(null); const editorRef = useRef<HTMLDivElement>(null);
const [direction, setDirection] = useState<'ltr' | 'rtl'>('ltr');
useEffect(() => { useEffect(() => {
if (composeBodyRef.current) { if (editorRef.current) {
composeBodyRef.current.innerHTML = composeBody; editorRef.current.innerHTML = composeBody;
const plainText = editorRef.current.textContent || '';
setDirection(detectDirection(plainText));
} }
}, [composeBody]); }, [composeBody]);
const handleInput = (e: React.FormEvent<HTMLDivElement>) => { const handleInput = () => {
if (composeBodyRef.current) { if (editorRef.current) {
setComposeBody(composeBodyRef.current.innerHTML); const plainText = editorRef.current.textContent || '';
setDirection(detectDirection(plainText));
setComposeBody(editorRef.current.innerHTML);
} }
}; };
@ -213,7 +225,7 @@ export default function ComposeEmail({
{/* Message Body */} {/* Message Body */}
<div className="flex-1 min-h-[200px] overflow-auto"> <div className="flex-1 min-h-[200px] overflow-auto">
<div <div
ref={composeBodyRef} ref={editorRef}
contentEditable contentEditable
className="prose max-w-none min-h-[200px] p-4 border border-gray-300 rounded-lg bg-white" className="prose max-w-none min-h-[200px] p-4 border border-gray-300 rounded-lg bg-white"
style={{ style={{
@ -224,11 +236,9 @@ export default function ComposeEmail({
fontFamily: 'inherit', fontFamily: 'inherit',
fontSize: 'inherit', fontSize: 'inherit',
lineHeight: 'inherit', lineHeight: 'inherit',
textAlign: 'left', textAlign: 'left'
direction: 'ltr',
unicodeBidi: 'bidi-override'
}} }}
dir="ltr" dir={direction}
onInput={handleInput} onInput={handleInput}
/> />
</div> </div>