courrier preview
This commit is contained in:
parent
bbe4bc06c2
commit
0d88409508
@ -17,6 +17,7 @@ import {
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import RichEmailEditor from '@/components/email/RichEmailEditor';
|
||||
import { detectTextDirection } from '@/lib/utils/text-direction';
|
||||
import EmailContentDisplay from './EmailContentDisplay';
|
||||
|
||||
// Import from the centralized utils
|
||||
import {
|
||||
@ -346,6 +347,10 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleContentChange = (html: string) => {
|
||||
setEmailContent(html);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full max-h-[80vh] bg-white border rounded-md shadow-md">
|
||||
{/* Header */}
|
||||
@ -480,11 +485,11 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
{/* Message Body */}
|
||||
<RichEmailEditor
|
||||
initialContent={emailContent}
|
||||
onChange={(html) => {
|
||||
setEmailContent(html);
|
||||
}}
|
||||
onChange={handleContentChange}
|
||||
placeholder="Write your message here..."
|
||||
minHeight="320px"
|
||||
preserveFormatting={type === 'forward' || type === 'reply' || type === 'reply-all'}
|
||||
minHeight="200px"
|
||||
maxHeight="calc(100vh - 400px)"
|
||||
/>
|
||||
|
||||
{/* Attachments */}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
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';
|
||||
@ -28,6 +28,15 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
const quillRef = useRef<any>(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('<blockquote')
|
||||
);
|
||||
}, [initialContent, preserveFormatting]);
|
||||
|
||||
// Initialize Quill editor when component mounts
|
||||
useEffect(() => {
|
||||
// Import Quill dynamically (client-side only)
|
||||
@ -36,21 +45,32 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
|
||||
const Quill = (await import('quill')).default;
|
||||
|
||||
// Import quill-better-table
|
||||
// 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('<blockquote')
|
||||
);
|
||||
|
||||
// Import quill-better-table only if not dealing with pre-formatted content
|
||||
let tableModule = null;
|
||||
try {
|
||||
const QuillBetterTable = await import('quill-better-table');
|
||||
|
||||
// Register the table module if available
|
||||
if (QuillBetterTable && QuillBetterTable.default) {
|
||||
Quill.register({
|
||||
'modules/better-table': QuillBetterTable.default
|
||||
}, true);
|
||||
tableModule = QuillBetterTable.default;
|
||||
console.log('Better Table module registered successfully');
|
||||
if (!isPreFormattedContent) {
|
||||
try {
|
||||
const QuillBetterTable = await import('quill-better-table');
|
||||
|
||||
// Register the table module if available
|
||||
if (QuillBetterTable && QuillBetterTable.default) {
|
||||
Quill.register({
|
||||
'modules/better-table': QuillBetterTable.default
|
||||
}, true);
|
||||
tableModule = QuillBetterTable.default;
|
||||
console.log('Better Table module registered successfully');
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Table module not available:', err);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Table module not available:', err);
|
||||
} else {
|
||||
console.log('Pre-formatted content detected, disabling table module for compatibility');
|
||||
}
|
||||
|
||||
// Define custom formats/modules with table support
|
||||
@ -78,8 +98,8 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
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,
|
||||
// Don't initialize better-table if this is pre-formatted content
|
||||
'better-table': isPreFormattedContent ? false : tableModule,
|
||||
},
|
||||
placeholder: placeholder,
|
||||
theme: 'snow',
|
||||
@ -93,7 +113,8 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
startsWithHtml: initialContent.trim().startsWith('<'),
|
||||
containsForwardedMessage: initialContent.includes('---------- Forwarded message ----------'),
|
||||
containsReplyIndicator: initialContent.includes('wrote:'),
|
||||
hasBlockquote: initialContent.includes('<blockquote')
|
||||
hasBlockquote: initialContent.includes('<blockquote'),
|
||||
preserveFormatting: preserveFormatting
|
||||
});
|
||||
|
||||
// Detect text direction
|
||||
@ -145,17 +166,38 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
quillRef.current.setText('Error loading content');
|
||||
}
|
||||
} else {
|
||||
// Use direct innerHTML setting for the initial content
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
|
||||
// Set the direction for the content
|
||||
if (quillRef.current && quillRef.current.format) {
|
||||
// Special handling for reply/forward content
|
||||
if (isPreFormattedContent && preserveFormatting) {
|
||||
console.log('Setting pre-formatted reply/forward content with special handling');
|
||||
|
||||
// First clear the editor and add a line break for the user to type in
|
||||
quillRef.current.setText('\n\n');
|
||||
|
||||
// Set cursor at the top
|
||||
quillRef.current.setSelection(0, 0);
|
||||
|
||||
// Now append the quoted content as a read-only section
|
||||
const quoteIndex = quillRef.current.getText().length;
|
||||
quillRef.current.clipboard.dangerouslyPasteHTML(quoteIndex, sanitizedContent);
|
||||
|
||||
// Set direction for the editor
|
||||
quillRef.current.format('direction', direction);
|
||||
if (direction === 'rtl') {
|
||||
quillRef.current.format('align', 'right');
|
||||
}
|
||||
} else {
|
||||
console.warn('Cannot format content: editor not fully initialized');
|
||||
// Use direct innerHTML setting for the initial content
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
|
||||
// Set the direction for the content
|
||||
if (quillRef.current && quillRef.current.format) {
|
||||
quillRef.current.format('direction', direction);
|
||||
if (direction === 'rtl') {
|
||||
quillRef.current.format('align', 'right');
|
||||
}
|
||||
} else {
|
||||
console.warn('Cannot format content: editor not fully initialized');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,6 +309,17 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
console.log('Content appears to be pre-formatted as reply/forward, using as-is');
|
||||
// Just do basic sanitization without additional processing
|
||||
sanitizedContent = sanitizeHtml(initialContent);
|
||||
|
||||
// Disable formatting operations that might conflict with pre-formatted content
|
||||
try {
|
||||
// Disable better-table module to prevent errors with forwarded tables
|
||||
if (quillRef.current.getModule('better-table')) {
|
||||
// Try to disable better-table module operations
|
||||
quillRef.current.getModule('better-table').tableSelection = null;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Error disabling table module:', err);
|
||||
}
|
||||
} else {
|
||||
// Full processing for regular content
|
||||
sanitizedContent = processHtmlContent(initialContent);
|
||||
@ -297,31 +350,52 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
quillRef.current.setText(textContent || 'No content available');
|
||||
}
|
||||
} else {
|
||||
// SIMPLIFIED: Set content directly to the root element rather than using clipboard
|
||||
if (quillRef.current && quillRef.current.root) {
|
||||
// First set the content
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
// Special handling for reply/forward content
|
||||
if (isPreFormattedContent && preserveFormatting) {
|
||||
console.log('Setting pre-formatted reply/forward content with special handling');
|
||||
|
||||
// Then safely apply formatting only if quillRef is valid
|
||||
try {
|
||||
if (quillRef.current && quillRef.current.format && quillRef.current.root.innerHTML.trim().length > 0) {
|
||||
// Set the direction for the content
|
||||
quillRef.current.format('direction', direction);
|
||||
if (direction === 'rtl') {
|
||||
quillRef.current.format('align', 'right');
|
||||
// First clear the editor and add a line break for the user to type in
|
||||
quillRef.current.setText('\n\n');
|
||||
|
||||
// Set cursor at the top
|
||||
quillRef.current.setSelection(0, 0);
|
||||
|
||||
// Now append the quoted content as a read-only section
|
||||
const quoteIndex = quillRef.current.getText().length;
|
||||
quillRef.current.clipboard.dangerouslyPasteHTML(quoteIndex, sanitizedContent);
|
||||
|
||||
// Set direction for the editor
|
||||
quillRef.current.format('direction', direction);
|
||||
if (direction === 'rtl') {
|
||||
quillRef.current.format('align', 'right');
|
||||
}
|
||||
} else {
|
||||
// SIMPLIFIED: Set content directly to the root element rather than using clipboard
|
||||
if (quillRef.current && quillRef.current.root) {
|
||||
// First set the content
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
|
||||
// Then safely apply formatting only if quillRef is valid
|
||||
try {
|
||||
if (quillRef.current && quillRef.current.format && quillRef.current.root.innerHTML.trim().length > 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');
|
||||
}
|
||||
|
||||
// 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 (formatError) {
|
||||
console.error('Error applying formatting:', formatError);
|
||||
// Continue without formatting if there's an error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user