'use client'; import React, { useEffect, useRef, useState, useMemo } from 'react'; import 'quill/dist/quill.snow.css'; import { sanitizeHtml } from '@/lib/utils/dom-purify-config'; import { detectTextDirection } from '@/lib/utils/text-direction'; import { processHtmlContent } from '@/lib/utils/email-content'; interface RichEmailEditorProps { initialContent: string; onChange: (content: string) => void; placeholder?: string; minHeight?: string; maxHeight?: string; preserveFormatting?: boolean; } const RichEmailEditor: React.FC = ({ initialContent, onChange, placeholder = 'Write your message here...', minHeight = '200px', maxHeight = 'calc(100vh - 400px)', preserveFormatting = false, }) => { const editorRef = useRef(null); const toolbarRef = useRef(null); const quillRef = useRef(null); const [isReady, setIsReady] = useState(false); // Determine if content is pre-formatted (for forward/reply) const isPreFormattedContent = useMemo(() => { return preserveFormatting && initialContent && ( (initialContent.includes('---------- Forwarded message ----------') || initialContent.includes('wrote:')) && initialContent.includes(' { // Import Quill dynamically (client-side only) const initializeQuill = async () => { if (!editorRef.current || !toolbarRef.current) return; const Quill = (await import('quill')).default; // Check if content already appears to be pre-formatted as a reply or forward const isPreFormattedContent = initialContent && ( (initialContent.includes('---------- Forwarded message ----------') || initialContent.includes('wrote:')) && initialContent.includes(' { if (el instanceof HTMLElement) { el.scrollTop = 0; } }); } } catch (err) { console.error('Error setting initial content:', err); // Enhanced fallback mechanism for complex content try { // First try to extract text from HTML const tempDiv = document.createElement('div'); tempDiv.innerHTML = initialContent; const textContent = tempDiv.textContent || tempDiv.innerText || ''; if (textContent.trim()) { console.log('Using extracted text fallback, length:', textContent.length); quillRef.current.setText(textContent); } else { // If text extraction fails or returns empty, provide a message console.log('Using empty content fallback'); quillRef.current.setText('Unable to load original content'); } } catch (e) { console.error('All fallbacks failed:', e); quillRef.current.setText('Error loading content'); } } } // Add change listener quillRef.current.on('text-change', () => { const html = quillRef.current.root.innerHTML; onChange(html); }); // Improve editor layout const editorContainer = editorElement.closest('.ql-container'); if (editorContainer) { editorContainer.classList.add('email-editor-container'); } setIsReady(true); }; initializeQuill().catch(err => { console.error('Failed to initialize Quill editor:', err); }); // Clean up on unmount return () => { if (quillRef.current) { // Clean up any event listeners or resources quillRef.current.off('text-change'); } }; }, []); // Update content from props if changed externally - using a simpler approach useEffect(() => { 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 { console.log('Updating content in editor:', { contentLength: initialContent.length, startsWithHtml: initialContent.trim().startsWith('<'), containsForwardedMessage: initialContent.includes('---------- Forwarded message ----------'), containsQuoteHeader: initialContent.includes('wrote:'), hasBlockquote: initialContent.includes(' 0) { // Set the direction for the content quillRef.current.format('direction', direction); if (direction === 'rtl') { quillRef.current.format('align', 'right'); } // Force update quillRef.current.update(); // Set selection to beginning quillRef.current.setSelection(0, 0); } else { console.warn('Skipping format - either editor not ready or content empty'); } } catch (formatError) { console.error('Error applying formatting:', formatError); // Continue without formatting if there's an error } } } } } catch (err) { console.error('Error updating content:', err); // 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 || ''; if (quillRef.current) { quillRef.current.setText(textContent || 'Error loading content'); } } catch (e) { console.error('All fallbacks failed:', e); // Last resort if (quillRef.current) { quillRef.current.setText('Error loading content'); } } } } } }, [initialContent, isReady]); return (
{/* Custom toolbar container */}
{/* Editor container with improved scrolling */}
{/* Loading indicator */} {!isReady && (
)}
{/* Custom styles for email context */}
); }; export default RichEmailEditor;