diff --git a/components/email/RichEmailEditor.tsx b/components/email/RichEmailEditor.tsx index 9995a3f2..f83906f6 100644 --- a/components/email/RichEmailEditor.tsx +++ b/components/email/RichEmailEditor.tsx @@ -74,6 +74,9 @@ const RichEmailEditor: React.FC = ({ // Add any custom toolbar handlers here } }, + clipboard: { + matchVisual: false // Disable clipboard matching for better HTML handling + }, // Don't initialize better-table yet - we'll do it after content is loaded 'better-table': false, }, @@ -93,68 +96,54 @@ const RichEmailEditor: React.FC = ({ direction }); - // Make sure content is properly sanitized before injecting it - const cleanContent = sanitizeHtml(processedContent || initialContent); + // Simplify complex email content to something Quill can handle better + const sanitizedContent = sanitizeHtml(processedContent || initialContent); - // First, directly set the content - if (editorRef.current) { - editorRef.current.innerHTML = cleanContent; + // Use direct innerHTML setting for the initial content + quillRef.current.root.innerHTML = sanitizedContent; + + // Set the direction for the content + quillRef.current.format('direction', direction); + if (direction === 'rtl') { + quillRef.current.format('align', 'right'); } - // Then let Quill parse and format it correctly - setTimeout(() => { - // Only proceed if editor ref is still available - if (!editorRef.current) return; + // Set cursor at the beginning + 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; - // Get the content from the editor element - const content = editorRef.current.innerHTML; + // 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') + ]; - // Clear the editor - quillRef.current.setText(''); - - // Insert clean content - quillRef.current.clipboard.dangerouslyPasteHTML(0, content); - - // Set the direction for the content - quillRef.current.format('direction', direction); - if (direction === 'rtl') { - quillRef.current.format('align', 'right'); - } - - // 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); + scrollable.forEach(el => { + if (el instanceof HTMLElement) { + el.scrollTop = 0; + } + }); + } } catch (err) { console.error('Error setting initial content:', err); // Fallback: just set text quillRef.current.setText(''); - // Try simplest approach + // Extract text as a last resort try { - quillRef.current.clipboard.dangerouslyPasteHTML(initialContent); + // Create a temporary div to extract text from HTML + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = initialContent; + const textContent = tempDiv.textContent || tempDiv.innerText || ''; + quillRef.current.setText(textContent); } catch (e) { console.error('Fallback failed too:', e); - // Last resort: strip all HTML - quillRef.current.setText(initialContent.replace(/<[^>]*>/g, '')); + quillRef.current.setText(''); } } } @@ -187,24 +176,22 @@ const RichEmailEditor: React.FC = ({ }; }, []); - // Update content from props if changed externally + // Update content from props if changed externally - using a simpler approach useEffect(() => { - if (quillRef.current && isReady) { + if (quillRef.current && isReady && initialContent) { const currentContent = quillRef.current.root.innerHTML; + // Only update if content changed to avoid editor position reset if (initialContent !== currentContent) { try { - // Preserve cursor position if possible - const selection = quillRef.current.getSelection(); - // Process content to ensure correct direction const { direction, html: processedContent } = processContentWithDirection(initialContent); - // First clear the content - quillRef.current.root.innerHTML = ''; + // Sanitize the HTML + const sanitizedContent = sanitizeHtml(processedContent || initialContent); - // Then insert the new content at position 0 - quillRef.current.clipboard.dangerouslyPasteHTML(0, sanitizeHtml(processedContent || initialContent)); + // SIMPLIFIED: Set content directly to the root element rather than using clipboard + quillRef.current.root.innerHTML = sanitizedContent; // Set the direction for the content quillRef.current.format('direction', direction); @@ -215,14 +202,20 @@ const RichEmailEditor: React.FC = ({ // Force update quillRef.current.update(); - // Restore selection if possible - if (selection) { - setTimeout(() => quillRef.current.setSelection(selection), 10); - } + // Set selection to beginning + quillRef.current.setSelection(0, 0); } catch (err) { console.error('Error updating content:', err); - // Fallback update method - quillRef.current.clipboard.dangerouslyPasteHTML(sanitizeHtml(initialContent)); + // Safer fallback that avoids clipboard API + try { + // Extract basic text if everything else fails + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = initialContent; + const textContent = tempDiv.textContent || tempDiv.innerText || ''; + quillRef.current.setText(textContent); + } catch (e) { + console.error('All fallbacks failed:', e); + } } } } @@ -399,36 +392,13 @@ const RichEmailEditor: React.FC = ({ font-size: 13px !important; } - /* Status styles for email displays */ - :global(.ql-editor td[class*="status"]), - :global(.ql-editor td[class*="Status"]) { - background-color: #f8f9fa !important; - font-weight: 500 !important; - } - - /* Amount styles */ - :global(.ql-editor td[class*="amount"]), - :global(.ql-editor td[class*="Amount"]), - :global(.ql-editor td[class*="price"]), - :global(.ql-editor td[class*="Price"]) { - text-align: right !important; - font-family: monospace !important; - } - - /* Header row styles */ - :global(.ql-editor tr:first-child td), - :global(.ql-editor th) { - background-color: #f8f9fa !important; - font-weight: 600 !important; - } - - /* Improve table cells with specific content */ - :global(.ql-editor td:has(div[class*="number"])), - :global(.ql-editor td:has(div[class*="Number"])), - :global(.ql-editor td:has(div[class*="invoice"])), - :global(.ql-editor td:has(div[class*="Invoice"])) { - font-family: monospace !important; - letter-spacing: 0.5px !important; + /* Email quote styling */ + :global(.email-original-content) { + margin-top: 20px !important; + padding-top: 10px !important; + border-top: 1px solid #ddd !important; + color: #555 !important; + font-size: 13px !important; } /* Fix quoted paragraphs */ diff --git a/lib/utils/email-utils.ts b/lib/utils/email-utils.ts index a631b9f4..78ec94e7 100644 --- a/lib/utils/email-utils.ts +++ b/lib/utils/email-utils.ts @@ -279,14 +279,15 @@ export function formatReplyEmail(originalEmail: EmailMessage | LegacyEmailMessag // Extract content using centralized utility const { text: originalTextContent, html: originalHtmlContent } = extractEmailContent(originalEmail); - // Create content with appropriate quote formatting + // Create a simpler HTML structure that's easier for Quill to handle const replyBody = `
-
-
-

On ${dateStr}, ${fromStr} wrote:

- ${originalHtmlContent || originalTextContent.replace(/\n/g, '
')} -
+ `; // Process the content with proper direction @@ -334,20 +335,19 @@ export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMe // Extract content using centralized utility const { text: originalTextContent, html: originalHtmlContent } = extractEmailContent(originalEmail); - // Create forwarded content with header information + // Create a simpler forwarded content structure for better Quill compatibility const forwardBody = `
-
-