courrier preview
This commit is contained in:
parent
e9f104f40b
commit
0d4fc59904
@ -136,6 +136,20 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
}
|
||||
}
|
||||
|
||||
// Safety timeout to prevent endless loading
|
||||
const safetyTimeoutId = setTimeout(() => {
|
||||
const contentState = emailContent;
|
||||
if (!contentState || contentState === "") {
|
||||
console.warn('Email content initialization timed out after 5 seconds, using fallback template');
|
||||
// Create a basic fallback template
|
||||
const { fromStr, dateStr } = getFormattedInfoForEmail(initialEmail);
|
||||
const fallbackContent = type === 'forward'
|
||||
? `<div style="margin: 20px 0 10px 0; color: #666;">---------- Forwarded message ----------<br>Unable to load original message content</div>`
|
||||
: `<div style="margin: 20px 0 10px 0; color: #666;">On ${dateStr}, ${fromStr} wrote:<br>Unable to load original message content</div>`;
|
||||
setEmailContent(fallbackContent);
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
// Get recipients based on type
|
||||
if (type === 'reply' || type === 'reply-all') {
|
||||
// Get formatted data for reply
|
||||
@ -258,6 +272,9 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
setAttachments(formattedAttachments);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the safety timeout if we complete successfully
|
||||
return () => clearTimeout(safetyTimeoutId);
|
||||
} catch (error) {
|
||||
console.error('Error initializing compose form:', error);
|
||||
// Provide a fallback in case of error
|
||||
|
||||
@ -180,6 +180,22 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
|
||||
console.log('Initializing editor in', contentIsReplyOrForward ? 'reply/forward' : 'compose', 'mode');
|
||||
|
||||
// Clean up any existing Quill instance
|
||||
if (quillRef.current) {
|
||||
console.log('Cleaning up existing Quill instance before reinitializing');
|
||||
quillRef.current.off('text-change');
|
||||
try {
|
||||
// Safely remove the Quill instance to prevent memory leaks
|
||||
const editorContainer = editorRef.current.querySelector('.ql-editor');
|
||||
if (editorContainer) {
|
||||
editorContainer.innerHTML = '';
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error cleaning up editor:', e);
|
||||
}
|
||||
quillRef.current = null;
|
||||
}
|
||||
|
||||
const Quill = (await import('quill')).default;
|
||||
|
||||
// Import quill-better-table conditionally based on content type
|
||||
@ -216,6 +232,7 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
['clean'],
|
||||
];
|
||||
|
||||
try {
|
||||
// Create new Quill instance with the DOM element and custom toolbar
|
||||
const editorElement = editorRef.current;
|
||||
quillRef.current = new Quill(editorElement, {
|
||||
@ -247,8 +264,11 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
hasBlockquote: initialContent.includes('<blockquote')
|
||||
});
|
||||
|
||||
// Pre-process to handle blocked content like external images
|
||||
const preProcessedContent = handleBlockedContent(initialContent);
|
||||
|
||||
// Process HTML content using centralized utility with special settings for replies/forwards
|
||||
const processed = processHtmlContent(initialContent, {
|
||||
const processed = processHtmlContent(preProcessedContent, {
|
||||
sanitize: true,
|
||||
preserveReplyFormat: contentIsReplyOrForward
|
||||
});
|
||||
@ -274,7 +294,7 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
// Try to extract text content if HTML processing failed
|
||||
try {
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = initialContent;
|
||||
tempDiv.innerHTML = preProcessedContent;
|
||||
const textContent = tempDiv.textContent || tempDiv.innerText || 'Empty content';
|
||||
|
||||
// Set text directly to ensure something displays
|
||||
@ -371,6 +391,13 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
}
|
||||
|
||||
setIsReady(true);
|
||||
} catch (initError) {
|
||||
console.error('Critical error initializing editor:', initError);
|
||||
// Provide fallback in UI
|
||||
if (editorRef.current) {
|
||||
editorRef.current.innerHTML = '<div style="padding: 15px; color: #555;">Error loading editor. Please try again or use plain text mode.</div>';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
initializeQuill().catch(err => {
|
||||
@ -382,10 +409,50 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
if (quillRef.current) {
|
||||
// Clean up any event listeners or resources
|
||||
quillRef.current.off('text-change');
|
||||
quillRef.current = null;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Add utility function to handle blocked content
|
||||
/**
|
||||
* Pre-process content to handle blocked images and other resources
|
||||
*/
|
||||
function handleBlockedContent(htmlContent: string): string {
|
||||
if (!htmlContent) return htmlContent;
|
||||
|
||||
try {
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = htmlContent;
|
||||
|
||||
// Replace CID and other problematic image sources
|
||||
const images = tempDiv.querySelectorAll('img');
|
||||
images.forEach(img => {
|
||||
const src = img.getAttribute('src') || '';
|
||||
|
||||
// Handle CID attachments that would be blocked
|
||||
if (src.startsWith('cid:')) {
|
||||
console.log('Replacing CID image source:', src);
|
||||
img.setAttribute('src', 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="80" viewBox="0 0 100 80"><rect width="100" height="80" fill="%23eee"/><text x="50%" y="50%" font-family="sans-serif" font-size="12" text-anchor="middle" dominant-baseline="middle" fill="%23999">[Image: Attachment]</text></svg>');
|
||||
img.setAttribute('data-original-src', src);
|
||||
img.style.maxWidth = '300px';
|
||||
img.style.border = '1px dashed #ddd';
|
||||
}
|
||||
|
||||
// Handle tracking pixels and potentially blocked remote content
|
||||
if (src.includes('open?') || src.includes('tracking') || src.includes('pixel')) {
|
||||
console.log('Removing tracking pixel:', src);
|
||||
img.remove();
|
||||
}
|
||||
});
|
||||
|
||||
return tempDiv.innerHTML;
|
||||
} catch (error) {
|
||||
console.error('Error handling blocked content:', error);
|
||||
return htmlContent;
|
||||
}
|
||||
}
|
||||
|
||||
// Update content from props if changed externally - using a simpler approach
|
||||
useEffect(() => {
|
||||
if (quillRef.current && isReady && initialContent) {
|
||||
@ -420,14 +487,36 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
// Force a complete re-initialization of the editor by unmounting
|
||||
if (quillRef.current) {
|
||||
quillRef.current.off('text-change');
|
||||
|
||||
// Clear content to prevent flashing
|
||||
try {
|
||||
quillRef.current.root.innerHTML = '<div>Loading...</div>';
|
||||
} catch (e) {
|
||||
console.warn('Error clearing editor content:', e);
|
||||
}
|
||||
|
||||
quillRef.current = null;
|
||||
}
|
||||
setIsReady(false);
|
||||
|
||||
// Force a small delay before reinitializing to ensure cleanup completes
|
||||
setTimeout(() => {
|
||||
// Explicitly empty out the editor DOM node to ensure clean start
|
||||
if (editorRef.current) {
|
||||
while (editorRef.current.firstChild) {
|
||||
editorRef.current.removeChild(editorRef.current.firstChild);
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Pre-process to handle blocked content
|
||||
const preProcessedContent = handleBlockedContent(initialContent);
|
||||
|
||||
// Process HTML content using centralized utility
|
||||
const processed = processHtmlContent(initialContent, {
|
||||
const processed = processHtmlContent(preProcessedContent, {
|
||||
sanitize: true,
|
||||
preserveReplyFormat: contentIsReplyOrForward
|
||||
});
|
||||
@ -452,7 +541,7 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
console.warn('Sanitized content is empty, using original content');
|
||||
// If sanitized content is empty, try to extract text from original
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = initialContent;
|
||||
tempDiv.innerHTML = preProcessedContent;
|
||||
const textContent = tempDiv.textContent || tempDiv.innerText || '';
|
||||
|
||||
// Create simple HTML with text content
|
||||
|
||||
Loading…
Reference in New Issue
Block a user