courrier preview
This commit is contained in:
parent
ad03dba3bf
commit
f9e0b323ce
@ -79,80 +79,74 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
theme: 'snow',
|
||||
});
|
||||
|
||||
// Set initial content (sanitized)
|
||||
// Set initial content properly
|
||||
if (initialContent) {
|
||||
try {
|
||||
// First, ensure we preserve the raw HTML structure
|
||||
const preservedContent = sanitizeHtml(initialContent);
|
||||
console.log('Setting initial content in editor', {
|
||||
length: initialContent.length,
|
||||
startsWithHtml: initialContent.trim().startsWith('<')
|
||||
});
|
||||
|
||||
// Check if there are tables in the content
|
||||
const hasTables = preservedContent.includes('<table');
|
||||
// Make sure content is properly sanitized before injecting it
|
||||
const cleanContent = initialContent
|
||||
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '') // Remove scripts
|
||||
.replace(/on\w+="[^"]*"/g, '') // Remove event handlers
|
||||
.replace(/(javascript|jscript|vbscript|mocha):/gi, 'removed:'); // Remove protocol handlers
|
||||
|
||||
// For content with tables, we need special handling
|
||||
if (hasTables && preserveFormatting && tableModule) {
|
||||
// First, set the content directly to the root
|
||||
quillRef.current.root.innerHTML = preservedContent;
|
||||
|
||||
// Initialize better table module after content is set
|
||||
setTimeout(() => {
|
||||
try {
|
||||
// Clean up any existing tables first
|
||||
const tables = quillRef.current.root.querySelectorAll('table');
|
||||
tables.forEach((table: HTMLTableElement) => {
|
||||
// Add required data attributes that the module expects
|
||||
if (!table.getAttribute('data-table')) {
|
||||
table.setAttribute('data-table', 'true');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize the module now that content is already in place
|
||||
const betterTableModule = {
|
||||
operationMenu: {
|
||||
items: {
|
||||
unmergeCells: {
|
||||
text: 'Unmerge cells'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Force a refresh
|
||||
quillRef.current.update();
|
||||
|
||||
// Ensure the cursor and scroll position is at the top of the editor
|
||||
quillRef.current.setSelection(0, 0);
|
||||
|
||||
// Also scroll the container to the top
|
||||
if (editorRef.current) {
|
||||
editorRef.current.scrollTop = 0;
|
||||
|
||||
// Also find and scroll parent containers that might have scroll
|
||||
const scrollContainer = editorRef.current.closest('.ql-container');
|
||||
if (scrollContainer) {
|
||||
scrollContainer.scrollTop = 0;
|
||||
}
|
||||
|
||||
// One more check for nested scroll containers (like overflow divs)
|
||||
const parentScrollContainer = editorRef.current.closest('.rich-email-editor-container');
|
||||
if (parentScrollContainer) {
|
||||
parentScrollContainer.scrollTop = 0;
|
||||
}
|
||||
}
|
||||
} catch (tableErr) {
|
||||
console.error('Error initializing table module:', tableErr);
|
||||
}
|
||||
}, 100);
|
||||
} else {
|
||||
// For content without tables, use the standard paste method
|
||||
quillRef.current.clipboard.dangerouslyPasteHTML(0, preservedContent);
|
||||
quillRef.current.setSelection(0, 0);
|
||||
// First, directly set the content
|
||||
if (editorRef.current) {
|
||||
editorRef.current.innerHTML = cleanContent;
|
||||
}
|
||||
|
||||
// Then let Quill parse and format it correctly
|
||||
setTimeout(() => {
|
||||
// Only proceed if editor ref is still available
|
||||
if (!editorRef.current) return;
|
||||
|
||||
// Get the content from the editor element
|
||||
const content = editorRef.current.innerHTML;
|
||||
|
||||
// Clear the editor
|
||||
quillRef.current.setText('');
|
||||
|
||||
// Insert clean content
|
||||
quillRef.current.clipboard.dangerouslyPasteHTML(0, content);
|
||||
|
||||
// Set cursor at the beginning (before the quoted content)
|
||||
quillRef.current.setSelection(0, 0);
|
||||
|
||||
// Ensure the cursor and scroll position is at the top of the editor
|
||||
if (editorRef.current) {
|
||||
editorRef.current.scrollTop = 0;
|
||||
|
||||
// Find and scroll parent containers that might have scroll
|
||||
const scrollable = [
|
||||
editorRef.current.closest('.ql-container'),
|
||||
editorRef.current.closest('.rich-email-editor-container'),
|
||||
editorRef.current.closest('.overflow-y-auto'),
|
||||
document.querySelector('.overflow-y-auto')
|
||||
];
|
||||
|
||||
scrollable.forEach(el => {
|
||||
if (el instanceof HTMLElement) {
|
||||
el.scrollTop = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 100);
|
||||
} catch (err) {
|
||||
console.error('Error setting initial content:', err);
|
||||
// Fallback method if the above fails
|
||||
// Fallback: just set text
|
||||
quillRef.current.setText('');
|
||||
quillRef.current.clipboard.dangerouslyPasteHTML(sanitizeHtml(initialContent));
|
||||
quillRef.current.setSelection(0, 0);
|
||||
|
||||
// Try simplest approach
|
||||
try {
|
||||
quillRef.current.clipboard.dangerouslyPasteHTML(initialContent);
|
||||
} catch (e) {
|
||||
console.error('Fallback failed too:', e);
|
||||
// Last resort: strip all HTML
|
||||
quillRef.current.setText(initialContent.replace(/<[^>]*>/g, ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -242,10 +242,23 @@ export function formatForwardedEmail(email: EmailMessage): {
|
||||
const toString = formatEmailAddresses(email.to || []);
|
||||
const dateString = formatEmailDate(email.date);
|
||||
|
||||
// Get original content as HTML
|
||||
const originalContent = email.content.isHtml && email.content.html
|
||||
? email.content.html
|
||||
: formatPlainTextToHtml(email.content.text);
|
||||
// Get original content - use the raw content if possible to preserve formatting
|
||||
let originalContent = '';
|
||||
|
||||
if (email.content) {
|
||||
if (email.content.isHtml && email.content.html) {
|
||||
originalContent = email.content.html;
|
||||
} else if (email.content.text) {
|
||||
// Format plain text with basic HTML formatting
|
||||
originalContent = formatPlainTextToHtml(email.content.text);
|
||||
}
|
||||
} else if (email.html) {
|
||||
originalContent = email.html;
|
||||
} else if (email.text) {
|
||||
originalContent = formatPlainTextToHtml(email.text);
|
||||
} else {
|
||||
originalContent = '<p>No content</p>';
|
||||
}
|
||||
|
||||
// Check if the content already has a forwarded message header
|
||||
const hasExistingHeader = originalContent.includes('---------- Forwarded message ---------');
|
||||
@ -263,7 +276,7 @@ export function formatForwardedEmail(email: EmailMessage): {
|
||||
} else {
|
||||
// Create formatted content for forwarded email
|
||||
htmlContent = `
|
||||
<div style="min-height: 20px;">
|
||||
<div style="min-height: 20px;"></div>
|
||||
<div style="border-top: 1px solid #ccc; margin-top: 10px; padding-top: 10px;">
|
||||
<div style="font-family: Arial, sans-serif; color: #333;">
|
||||
<div style="margin-bottom: 15px;">
|
||||
@ -278,16 +291,22 @@ export function formatForwardedEmail(email: EmailMessage): {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Ensure we have clean HTML
|
||||
const cleanedHtml = DOMPurify.sanitize(htmlContent, {
|
||||
ADD_TAGS: ['style'],
|
||||
ADD_ATTR: ['target', 'rel', 'href', 'src', 'style', 'class', 'id'],
|
||||
ALLOW_DATA_ATTR: true
|
||||
});
|
||||
|
||||
// Create normalized content with HTML and extracted text
|
||||
const content: EmailContent = {
|
||||
html: sanitizeHtml(htmlContent),
|
||||
html: cleanedHtml,
|
||||
text: '', // Will be extracted when composing
|
||||
isHtml: true,
|
||||
direction: email.content.direction || 'ltr'
|
||||
direction: email.content?.direction || 'ltr'
|
||||
};
|
||||
|
||||
// Extract text from HTML if in browser environment
|
||||
@ -322,13 +341,13 @@ export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all
|
||||
let cc = undefined;
|
||||
if (type === 'reply-all' && (email.to || email.cc)) {
|
||||
const allRecipients = [
|
||||
...(email.to || []),
|
||||
...(email.cc || [])
|
||||
...(typeof email.to === 'string' ? [{name: '', address: email.to}] : (email.to || [])),
|
||||
...(typeof email.cc === 'string' ? [{name: '', address: email.cc}] : (email.cc || []))
|
||||
];
|
||||
|
||||
// Remove duplicates, then convert to string
|
||||
const uniqueRecipients = [...new Map(allRecipients.map(addr =>
|
||||
[addr.address, addr]
|
||||
[typeof addr === 'string' ? addr : addr.address, addr]
|
||||
)).values()];
|
||||
|
||||
cc = formatEmailAddresses(uniqueRecipients);
|
||||
@ -338,14 +357,35 @@ export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all
|
||||
const subjectBase = email.subject || '(No subject)';
|
||||
const subject = subjectBase.match(/^Re:/i) ? subjectBase : `Re: ${subjectBase}`;
|
||||
|
||||
// Get original content as HTML
|
||||
const originalContent = email.content.isHtml && email.content.html
|
||||
? email.content.html
|
||||
: formatPlainTextToHtml(email.content.text);
|
||||
// Get original content - use the raw content if possible to preserve formatting
|
||||
let originalContent = '';
|
||||
|
||||
if (email.content) {
|
||||
if (email.content.isHtml && email.content.html) {
|
||||
originalContent = email.content.html;
|
||||
} else if (email.content.text) {
|
||||
// Format plain text with basic HTML formatting
|
||||
originalContent = formatPlainTextToHtml(email.content.text);
|
||||
}
|
||||
} else if (email.html) {
|
||||
originalContent = email.html;
|
||||
} else if (email.text) {
|
||||
originalContent = formatPlainTextToHtml(email.text);
|
||||
} else {
|
||||
originalContent = '<p>No content</p>';
|
||||
}
|
||||
|
||||
// Format sender info
|
||||
const sender = email.from && email.from.length > 0 ? email.from[0] : undefined;
|
||||
const senderName = sender ? (sender.name || sender.address) : 'Unknown Sender';
|
||||
let senderName = 'Unknown Sender';
|
||||
if (email.from) {
|
||||
if (Array.isArray(email.from) && email.from.length > 0) {
|
||||
const sender = email.from[0];
|
||||
senderName = typeof sender === 'string' ? sender : (sender.name || sender.address);
|
||||
} else if (typeof email.from === 'string') {
|
||||
senderName = email.from;
|
||||
}
|
||||
}
|
||||
|
||||
const formattedDate = formatEmailDate(email.date);
|
||||
|
||||
// Create the reply content with attribution line
|
||||
@ -361,12 +401,19 @@ export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Ensure we have clean HTML
|
||||
const cleanedHtml = DOMPurify.sanitize(htmlContent, {
|
||||
ADD_TAGS: ['style'],
|
||||
ADD_ATTR: ['target', 'rel', 'href', 'src', 'style', 'class', 'id'],
|
||||
ALLOW_DATA_ATTR: true
|
||||
});
|
||||
|
||||
// Create normalized content with HTML and extracted text
|
||||
const content: EmailContent = {
|
||||
html: sanitizeHtml(htmlContent),
|
||||
html: cleanedHtml,
|
||||
text: '', // Will be extracted when composing
|
||||
isHtml: true,
|
||||
direction: email.content.direction || 'ltr'
|
||||
direction: email.content?.direction || 'ltr'
|
||||
};
|
||||
|
||||
// Extract text from HTML if in browser environment
|
||||
|
||||
Loading…
Reference in New Issue
Block a user