mail page fix
This commit is contained in:
parent
d55d2915cf
commit
0e7f48079d
@ -63,6 +63,7 @@ interface Email {
|
||||
cc?: string;
|
||||
bcc?: string;
|
||||
flags?: string[];
|
||||
raw: string;
|
||||
}
|
||||
|
||||
interface Attachment {
|
||||
@ -327,64 +328,81 @@ const initialSidebarItems = [
|
||||
];
|
||||
|
||||
function getReplyBody(email: Email, type: 'reply' | 'reply-all' | 'forward'): string {
|
||||
const { headers, body } = splitEmailHeadersAndBody(email.body);
|
||||
if (!email.raw) return '';
|
||||
|
||||
const { headers, body } = splitEmailHeadersAndBody(email.raw);
|
||||
const { contentType, encoding, charset } = parseEmailHeaders(headers);
|
||||
const isHtml = contentType.includes('text/html');
|
||||
const isMultipart = contentType.includes('multipart');
|
||||
const isQuotedPrintable = encoding === 'quoted-printable';
|
||||
|
||||
let content = body;
|
||||
if (isQuotedPrintable) {
|
||||
content = decodeQuotedPrintable(content, charset);
|
||||
}
|
||||
|
||||
if (isMultipart) {
|
||||
const parts = content.split('--boundary');
|
||||
content = parts.find((part: string) => part.includes('text/html')) || parts.find((part: string) => part.includes('text/plain')) || '';
|
||||
const partHeaders = content.split('\n\n')[0];
|
||||
const partContent = content.split('\n\n').slice(1).join('\n\n');
|
||||
const { contentType: partContentType, encoding: partEncoding, charset: partCharset } = parseEmailHeaders(partHeaders);
|
||||
content = partContent;
|
||||
if (partEncoding === 'quoted-printable') {
|
||||
content = decodeQuotedPrintable(content, partCharset);
|
||||
|
||||
let content = '';
|
||||
|
||||
if (contentType.includes('multipart/')) {
|
||||
const boundary = contentType.match(/boundary="([^"]+)"/)?.[1];
|
||||
if (boundary) {
|
||||
const parts = body.split('--' + boundary).filter(part => part.trim());
|
||||
|
||||
// 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 = selectedPart.split('\r\n\r\n')[0];
|
||||
const partBody = selectedPart.split('\r\n\r\n').slice(1).join('\r\n\r\n');
|
||||
const { encoding: partEncoding } = parseEmailHeaders(partHeaders);
|
||||
|
||||
content = partEncoding === 'quoted-printable'
|
||||
? decodeQuotedPrintable(partBody, charset)
|
||||
: partBody;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isHtml) {
|
||||
// Preserve HTML structure while cleaning potentially dangerous elements
|
||||
content = cleanHtml(content);
|
||||
} else {
|
||||
// Convert plain text to HTML while preserving formatting
|
||||
content = content
|
||||
.replace(/\n/g, '<br>')
|
||||
.replace(/\t/g, ' ')
|
||||
.replace(/ /g, ' ');
|
||||
content = encoding === 'quoted-printable'
|
||||
? decodeQuotedPrintable(body, charset)
|
||||
: body;
|
||||
}
|
||||
|
||||
|
||||
// Convert plain text to HTML if needed
|
||||
if (!contentType.includes('text/html')) {
|
||||
content = content
|
||||
.split('\n')
|
||||
.map(line => `<p>${line}</p>`)
|
||||
.join('');
|
||||
}
|
||||
|
||||
// Sanitize HTML content
|
||||
content = DOMPurify.sanitize(content, {
|
||||
ALLOWED_TAGS: [
|
||||
'p', 'br', 'div', 'span', 'b', 'i', 'u', 'strong', 'em',
|
||||
'blockquote', 'ul', 'ol', 'li', 'a', 'h1', 'h2', 'h3', 'h4',
|
||||
'table', 'thead', 'tbody', 'tr', 'td', 'th'
|
||||
],
|
||||
ALLOWED_ATTR: ['href', 'style', 'class'],
|
||||
});
|
||||
|
||||
const date = new Date(email.date).toLocaleString();
|
||||
|
||||
if (type === 'forward') {
|
||||
return `
|
||||
<div class="forwarded-message">
|
||||
<p>---------- Forwarded message ---------</p>
|
||||
<p>From: ${email.from}</p>
|
||||
<p>Date: ${new Date(email.date).toLocaleString()}</p>
|
||||
<p>Subject: ${email.subject}</p>
|
||||
<p>To: ${email.to}</p>
|
||||
<br>
|
||||
<div class="prose">
|
||||
${content}
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
---------- Forwarded message ----------<br/>
|
||||
From: ${email.from}<br/>
|
||||
Date: ${date}<br/>
|
||||
Subject: ${email.subject}<br/>
|
||||
To: ${Array.isArray(email.to) ? email.to.join(', ') : email.to}<br/>
|
||||
<br/>
|
||||
${content}
|
||||
`;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="reply-message">
|
||||
<p>On ${new Date(email.date).toLocaleString()}, ${email.from} wrote:</p>
|
||||
<blockquote class="prose">
|
||||
} else {
|
||||
return `
|
||||
<br/>
|
||||
<br/>
|
||||
On ${date}, ${email.from} wrote:<br/>
|
||||
<blockquote style="border-left: 2px solid #ccc; padding-left: 1em; margin-left: 0.5em;">
|
||||
${content}
|
||||
</blockquote>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export default function CourrierPage() {
|
||||
@ -555,7 +573,8 @@ export default function CourrierPage() {
|
||||
folder: email.folder || currentView,
|
||||
cc: email.cc,
|
||||
bcc: email.bcc,
|
||||
flags: email.flags || []
|
||||
flags: email.flags || [],
|
||||
raw: email.body || ''
|
||||
}));
|
||||
|
||||
// Only update unread count if we're in the Inbox folder
|
||||
@ -1444,7 +1463,8 @@ export default function CourrierPage() {
|
||||
folder: email.folder || newMailbox,
|
||||
cc: email.cc,
|
||||
bcc: email.bcc,
|
||||
flags: email.flags || []
|
||||
flags: email.flags || [],
|
||||
raw: email.body || ''
|
||||
}));
|
||||
|
||||
setEmails(processedEmails);
|
||||
@ -1680,26 +1700,13 @@ export default function CourrierPage() {
|
||||
<div
|
||||
contentEditable
|
||||
className="prose max-w-none min-h-[200px] p-4 focus:outline-none border rounded-md"
|
||||
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(composeBody) }}
|
||||
onInput={(e) => {
|
||||
// Preserve formatting by using a temporary div to clean the HTML
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = e.currentTarget.innerHTML;
|
||||
|
||||
// Remove any potentially dangerous elements/attributes while preserving formatting
|
||||
const cleanHtml = DOMPurify.sanitize(tempDiv.innerHTML, {
|
||||
ALLOWED_TAGS: ['p', 'br', 'div', 'span', 'b', 'i', 'u', 'strong', 'em', 'blockquote', 'ul', 'ol', 'li', 'a'],
|
||||
ALLOWED_ATTR: ['href', 'style', 'class'],
|
||||
});
|
||||
|
||||
setComposeBody(cleanHtml);
|
||||
suppressContentEditableWarning
|
||||
onInput={(e: React.FormEvent<HTMLDivElement>) => {
|
||||
setComposeBody((e.target as HTMLDivElement).innerHTML);
|
||||
}}
|
||||
style={{
|
||||
minHeight: '200px',
|
||||
overflowY: 'auto',
|
||||
lineHeight: '1.5',
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{composeBody}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user