courrier preview
This commit is contained in:
parent
c4958d7e4c
commit
7f87e4f00a
@ -74,6 +74,9 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
// Add any custom toolbar handlers here
|
// 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
|
// Don't initialize better-table yet - we'll do it after content is loaded
|
||||||
'better-table': false,
|
'better-table': false,
|
||||||
},
|
},
|
||||||
@ -93,68 +96,54 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
direction
|
direction
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make sure content is properly sanitized before injecting it
|
// Simplify complex email content to something Quill can handle better
|
||||||
const cleanContent = sanitizeHtml(processedContent || initialContent);
|
const sanitizedContent = sanitizeHtml(processedContent || initialContent);
|
||||||
|
|
||||||
// First, directly set the content
|
// Use direct innerHTML setting for the initial content
|
||||||
if (editorRef.current) {
|
quillRef.current.root.innerHTML = sanitizedContent;
|
||||||
editorRef.current.innerHTML = cleanContent;
|
|
||||||
|
// 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
|
// Set cursor at the beginning
|
||||||
setTimeout(() => {
|
quillRef.current.setSelection(0, 0);
|
||||||
// Only proceed if editor ref is still available
|
|
||||||
if (!editorRef.current) return;
|
// 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
|
// Find and scroll parent containers that might have scroll
|
||||||
const content = editorRef.current.innerHTML;
|
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
|
scrollable.forEach(el => {
|
||||||
quillRef.current.setText('');
|
if (el instanceof HTMLElement) {
|
||||||
|
el.scrollTop = 0;
|
||||||
// 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);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error setting initial content:', err);
|
console.error('Error setting initial content:', err);
|
||||||
// Fallback: just set text
|
// Fallback: just set text
|
||||||
quillRef.current.setText('');
|
quillRef.current.setText('');
|
||||||
|
|
||||||
// Try simplest approach
|
// Extract text as a last resort
|
||||||
try {
|
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) {
|
} catch (e) {
|
||||||
console.error('Fallback failed too:', e);
|
console.error('Fallback failed too:', e);
|
||||||
// Last resort: strip all HTML
|
quillRef.current.setText('');
|
||||||
quillRef.current.setText(initialContent.replace(/<[^>]*>/g, ''));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,24 +176,22 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Update content from props if changed externally
|
// Update content from props if changed externally - using a simpler approach
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (quillRef.current && isReady) {
|
if (quillRef.current && isReady && initialContent) {
|
||||||
const currentContent = quillRef.current.root.innerHTML;
|
const currentContent = quillRef.current.root.innerHTML;
|
||||||
|
|
||||||
// Only update if content changed to avoid editor position reset
|
// Only update if content changed to avoid editor position reset
|
||||||
if (initialContent !== currentContent) {
|
if (initialContent !== currentContent) {
|
||||||
try {
|
try {
|
||||||
// Preserve cursor position if possible
|
|
||||||
const selection = quillRef.current.getSelection();
|
|
||||||
|
|
||||||
// Process content to ensure correct direction
|
// Process content to ensure correct direction
|
||||||
const { direction, html: processedContent } = processContentWithDirection(initialContent);
|
const { direction, html: processedContent } = processContentWithDirection(initialContent);
|
||||||
|
|
||||||
// First clear the content
|
// Sanitize the HTML
|
||||||
quillRef.current.root.innerHTML = '';
|
const sanitizedContent = sanitizeHtml(processedContent || initialContent);
|
||||||
|
|
||||||
// Then insert the new content at position 0
|
// SIMPLIFIED: Set content directly to the root element rather than using clipboard
|
||||||
quillRef.current.clipboard.dangerouslyPasteHTML(0, sanitizeHtml(processedContent || initialContent));
|
quillRef.current.root.innerHTML = sanitizedContent;
|
||||||
|
|
||||||
// Set the direction for the content
|
// Set the direction for the content
|
||||||
quillRef.current.format('direction', direction);
|
quillRef.current.format('direction', direction);
|
||||||
@ -215,14 +202,20 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
// Force update
|
// Force update
|
||||||
quillRef.current.update();
|
quillRef.current.update();
|
||||||
|
|
||||||
// Restore selection if possible
|
// Set selection to beginning
|
||||||
if (selection) {
|
quillRef.current.setSelection(0, 0);
|
||||||
setTimeout(() => quillRef.current.setSelection(selection), 10);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error updating content:', err);
|
console.error('Error updating content:', err);
|
||||||
// Fallback update method
|
// Safer fallback that avoids clipboard API
|
||||||
quillRef.current.clipboard.dangerouslyPasteHTML(sanitizeHtml(initialContent));
|
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<RichEmailEditorProps> = ({
|
|||||||
font-size: 13px !important;
|
font-size: 13px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Status styles for email displays */
|
/* Email quote styling */
|
||||||
:global(.ql-editor td[class*="status"]),
|
:global(.email-original-content) {
|
||||||
:global(.ql-editor td[class*="Status"]) {
|
margin-top: 20px !important;
|
||||||
background-color: #f8f9fa !important;
|
padding-top: 10px !important;
|
||||||
font-weight: 500 !important;
|
border-top: 1px solid #ddd !important;
|
||||||
}
|
color: #555 !important;
|
||||||
|
font-size: 13px !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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix quoted paragraphs */
|
/* Fix quoted paragraphs */
|
||||||
|
|||||||
@ -279,14 +279,15 @@ export function formatReplyEmail(originalEmail: EmailMessage | LegacyEmailMessag
|
|||||||
// Extract content using centralized utility
|
// Extract content using centralized utility
|
||||||
const { text: originalTextContent, html: originalHtmlContent } = extractEmailContent(originalEmail);
|
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 = `
|
const replyBody = `
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<div class="email-original-content">
|
||||||
<blockquote style="border-left: 2px solid #ddd; padding-left: 10px; margin: 10px 0; color: #505050;">
|
<div>On ${dateStr}, ${fromStr} wrote:</div>
|
||||||
<p>On ${dateStr}, ${fromStr} wrote:</p>
|
<blockquote style="margin: 10px 0; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
|
||||||
${originalHtmlContent || originalTextContent.replace(/\n/g, '<br>')}
|
${originalTextContent.replace(/\n/g, '<br>')}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Process the content with proper direction
|
// Process the content with proper direction
|
||||||
@ -334,20 +335,19 @@ export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMe
|
|||||||
// Extract content using centralized utility
|
// Extract content using centralized utility
|
||||||
const { text: originalTextContent, html: originalHtmlContent } = extractEmailContent(originalEmail);
|
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 = `
|
const forwardBody = `
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<div class="email-original-content">
|
||||||
<div class="email-forwarded-content">
|
<div>---------- Forwarded message ---------</div>
|
||||||
<p>---------- Forwarded message ---------</p>
|
<div><strong>From:</strong> ${fromStr}</div>
|
||||||
<p><strong>From:</strong> ${fromStr}</p>
|
<div><strong>Date:</strong> ${dateStr}</div>
|
||||||
<p><strong>Date:</strong> ${dateStr}</p>
|
<div><strong>Subject:</strong> ${subject || ''}</div>
|
||||||
<p><strong>Subject:</strong> ${subject || ''}</p>
|
<div><strong>To:</strong> ${toStr}</div>
|
||||||
<p><strong>To:</strong> ${toStr}</p>
|
${ccStr ? `<div><strong>Cc:</strong> ${ccStr}</div>` : ''}
|
||||||
${ccStr ? `<p><strong>Cc:</strong> ${ccStr}</p>` : ''}
|
<blockquote style="margin-top: 10px; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
|
||||||
<div style="margin-top: 15px; border-top: 1px solid #eee; padding-top: 15px;">
|
${originalTextContent.replace(/\n/g, '<br>')}
|
||||||
${originalHtmlContent || originalTextContent.replace(/\n/g, '<br>')}
|
</blockquote>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user