mail page fix design
This commit is contained in:
parent
0549e3f020
commit
69d4a69713
@ -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() {
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user