courrier clean 2$
This commit is contained in:
parent
0bb4fac9f9
commit
5686b9fb7d
@ -59,7 +59,7 @@ import {
|
||||
formatReplyEmail,
|
||||
formatEmailForReplyOrForward,
|
||||
EmailMessage as FormatterEmailMessage,
|
||||
cleanHtmlContent
|
||||
sanitizeHtml
|
||||
} from '@/lib/utils/email-formatter';
|
||||
|
||||
export interface Account {
|
||||
@ -168,7 +168,7 @@ function EmailContent({ email }: { email: Email }) {
|
||||
email.content = fullContent.content;
|
||||
|
||||
// Render the content using the centralized cleaner
|
||||
const sanitizedHtml = cleanHtmlContent(fullContent.content);
|
||||
const sanitizedHtml = sanitizeHtml(fullContent.content);
|
||||
setContent(
|
||||
<div
|
||||
className="email-content prose prose-sm max-w-none dark:prose-invert"
|
||||
@ -199,7 +199,7 @@ function EmailContent({ email }: { email: Email }) {
|
||||
// Check if content is already HTML
|
||||
if (formattedEmail.startsWith('<') && formattedEmail.endsWith('>')) {
|
||||
// Content is likely HTML, sanitize using the centralized cleaner
|
||||
const sanitizedHtml = cleanHtmlContent(formattedEmail);
|
||||
const sanitizedHtml = sanitizeHtml(formattedEmail);
|
||||
setContent(
|
||||
<div
|
||||
className="email-content prose prose-sm max-w-none dark:prose-invert"
|
||||
@ -209,7 +209,7 @@ function EmailContent({ email }: { email: Email }) {
|
||||
setDebugInfo('Rendered existing HTML content with centralized formatter');
|
||||
} else {
|
||||
// For plain text or complex formats, use the centralized formatter
|
||||
const cleanedContent = cleanHtmlContent(formattedEmail);
|
||||
const cleanedContent = sanitizeHtml(formattedEmail);
|
||||
|
||||
// If it looks like HTML, render it as HTML
|
||||
if (cleanedContent.includes('<') && cleanedContent.includes('>')) {
|
||||
@ -453,15 +453,15 @@ function EmailPreview({ email }: { email: Email }) {
|
||||
try {
|
||||
// If we have the content already, extract preview from it
|
||||
if (email.content) {
|
||||
// Use cleanHtmlContent to safely extract text from HTML
|
||||
const cleanContent = cleanHtmlContent(email.content);
|
||||
// Use sanitizeHtml to safely extract text from HTML
|
||||
const cleanContent = sanitizeHtml(email.content);
|
||||
const plainText = cleanContent.replace(/<[^>]*>/g, ' ').trim();
|
||||
if (mounted) {
|
||||
setPreview(plainText.substring(0, 150) + '...');
|
||||
}
|
||||
} else {
|
||||
// Use the centralized cleaner instead of decodeEmail
|
||||
const cleanContent = cleanHtmlContent(email.content || '');
|
||||
const cleanContent = sanitizeHtml(email.content || '');
|
||||
const plainText = cleanContent.replace(/<[^>]*>/g, ' ').trim();
|
||||
|
||||
if (mounted) {
|
||||
|
||||
@ -88,7 +88,15 @@
|
||||
.email-content .header { margin-bottom: 1em; }
|
||||
.email-content .footer { font-size: 0.875rem; color: #6b7280; margin-top: 1em; }
|
||||
|
||||
/* Force email content direction */
|
||||
/* Email editor styles */
|
||||
.email-editor {
|
||||
/* Allow text direction to be controlled by component */
|
||||
direction: inherit;
|
||||
unicode-bidi: isolate;
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
/* Email content wrapper should still force LTR for quoted content */
|
||||
.email-content-wrapper {
|
||||
direction: ltr !important;
|
||||
unicode-bidi: isolate !important;
|
||||
@ -101,10 +109,3 @@
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
/* Email editor styles */
|
||||
.email-editor {
|
||||
direction: ltr !important;
|
||||
unicode-bidi: isolate !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
|
||||
@ -16,7 +16,8 @@ import {
|
||||
formatForwardedEmail,
|
||||
formatReplyEmail,
|
||||
formatEmailForReplyOrForward,
|
||||
EmailMessage as FormatterEmailMessage
|
||||
EmailMessage as FormatterEmailMessage,
|
||||
sanitizeHtml
|
||||
} from '@/lib/utils/email-formatter';
|
||||
|
||||
// Define EmailMessage interface locally instead of importing from server-only file
|
||||
@ -87,6 +88,7 @@ interface LegacyComposeEmailProps {
|
||||
interface ComposeEmailProps {
|
||||
initialEmail?: EmailMessage | null;
|
||||
type?: 'new' | 'reply' | 'reply-all' | 'forward';
|
||||
initialRTL?: boolean;
|
||||
onClose: () => void;
|
||||
onSend: (emailData: {
|
||||
to: string;
|
||||
@ -110,14 +112,6 @@ function isLegacyProps(props: ComposeEmailAllProps): props is LegacyComposeEmail
|
||||
return 'showCompose' in props && 'setShowCompose' in props;
|
||||
}
|
||||
|
||||
// Configure DOMPurify to preserve certain attributes
|
||||
DOMPurify.addHook('afterSanitizeAttributes', function(node) {
|
||||
// Preserve direction attributes
|
||||
if (node.hasAttribute('dir')) {
|
||||
node.setAttribute('dir', node.getAttribute('dir') || 'ltr');
|
||||
}
|
||||
});
|
||||
|
||||
export default function ComposeEmail(props: ComposeEmailAllProps) {
|
||||
// Handle legacy props by adapting them to new component
|
||||
if (isLegacyProps(props)) {
|
||||
@ -125,7 +119,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
|
||||
}
|
||||
|
||||
// Continue with modern implementation for new props
|
||||
const { initialEmail, type = 'new', onClose, onSend } = props;
|
||||
const { initialEmail, type = 'new', initialRTL, onClose, onSend } = props;
|
||||
|
||||
// Email form state
|
||||
const [to, setTo] = useState<string>('');
|
||||
@ -136,7 +130,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
|
||||
const [showCc, setShowCc] = useState<boolean>(false);
|
||||
const [showBcc, setShowBcc] = useState<boolean>(false);
|
||||
const [sending, setSending] = useState<boolean>(false);
|
||||
const [isRTL, setIsRTL] = useState<boolean>(false);
|
||||
const [isRTL, setIsRTL] = useState<boolean>(initialRTL || false);
|
||||
const [attachments, setAttachments] = useState<Array<{
|
||||
name: string;
|
||||
content: string;
|
||||
@ -147,6 +141,13 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
|
||||
const editorRef = useRef<HTMLDivElement>(null);
|
||||
const attachmentInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// Initialize RTL state from prop if provided
|
||||
useEffect(() => {
|
||||
if (initialRTL !== undefined) {
|
||||
setIsRTL(initialRTL);
|
||||
}
|
||||
}, [initialRTL]);
|
||||
|
||||
// Initialize the form when replying to or forwarding an email
|
||||
useEffect(() => {
|
||||
if (initialEmail && type !== 'new') {
|
||||
@ -264,7 +265,19 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
|
||||
|
||||
// Toggle text direction for the entire editor
|
||||
const toggleTextDirection = () => {
|
||||
setIsRTL(!isRTL);
|
||||
// Toggle the RTL state
|
||||
const newRTL = !isRTL;
|
||||
setIsRTL(newRTL);
|
||||
|
||||
// Apply the direction to the editor content immediately
|
||||
if (editorRef.current) {
|
||||
// Preserve the content but update the direction attributes
|
||||
editorRef.current.dir = newRTL ? 'rtl' : 'ltr';
|
||||
editorRef.current.style.textAlign = newRTL ? 'right' : 'left';
|
||||
editorRef.current.style.direction = newRTL ? 'rtl' : 'ltr';
|
||||
|
||||
// No need to modify the content, just let the user edit with proper directionality
|
||||
}
|
||||
};
|
||||
|
||||
// Send email without modifying pre-formatted content
|
||||
@ -418,7 +431,9 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
|
||||
dangerouslySetInnerHTML={{ __html: emailContent }}
|
||||
dir={isRTL ? 'rtl' : 'ltr'}
|
||||
style={{
|
||||
textAlign: isRTL ? 'right' : 'left'
|
||||
textAlign: isRTL ? 'right' : 'left',
|
||||
direction: isRTL ? 'rtl' : 'ltr',
|
||||
unicodeBidi: 'isolate'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@ -514,6 +529,23 @@ function LegacyAdapter({
|
||||
replyTo,
|
||||
forwardFrom
|
||||
}: LegacyComposeEmailProps) {
|
||||
// Check if the user has RTL preference
|
||||
const [preferRTL, setPreferRTL] = useState<boolean>(false);
|
||||
|
||||
// Detect RTL content in body on initialization
|
||||
useEffect(() => {
|
||||
if (!composeBody) return;
|
||||
|
||||
// Better RTL detection based on common RTL languages (Arabic, Hebrew, Persian, Urdu, etc.)
|
||||
const rtlRegex = /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/;
|
||||
|
||||
// If there are RTL characters, set preferRTL to true
|
||||
if (rtlRegex.test(composeBody)) {
|
||||
setPreferRTL(true);
|
||||
console.log('RTL text detected - setting editor to RTL mode');
|
||||
}
|
||||
}, [composeBody]);
|
||||
|
||||
// Determine the type from the original email or subject
|
||||
const determineType = (): 'new' | 'reply' | 'reply-all' | 'forward' => {
|
||||
if (originalEmail) {
|
||||
@ -581,6 +613,7 @@ function LegacyAdapter({
|
||||
<ComposeEmail
|
||||
initialEmail={emailForCompose}
|
||||
type={type}
|
||||
initialRTL={preferRTL}
|
||||
onClose={() => {
|
||||
onCancel?.();
|
||||
setShowCompose(false);
|
||||
|
||||
@ -93,25 +93,15 @@ export function formatEmailDate(date: Date | string | undefined): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean HTML content to prevent RTL/LTR issues
|
||||
* This is the ONLY function that should be used for cleaning HTML content
|
||||
* Sanitize HTML content before processing or displaying
|
||||
* @param content HTML content to sanitize
|
||||
* @returns Sanitized HTML
|
||||
*/
|
||||
export function cleanHtmlContent(content: string): string {
|
||||
export function sanitizeHtml(content: string): string {
|
||||
if (!content) return '';
|
||||
|
||||
// First sanitize the HTML with our configured DOMPurify
|
||||
const sanitized = DOMPurify.sanitize(content);
|
||||
|
||||
// Process content to ensure consistent direction
|
||||
let processed = sanitized;
|
||||
|
||||
// Replace RTL attributes with LTR if needed
|
||||
// We're now more careful to only modify direction attributes if needed
|
||||
processed = processed.replace(/dir\s*=\s*["']rtl["']/gi, 'dir="ltr"');
|
||||
processed = processed.replace(/style\s*=\s*["']([^"']*)direction\s*:\s*rtl;?([^"']*)["']/gi,
|
||||
(match, before, after) => `style="${before}direction: ltr;${after}"`);
|
||||
|
||||
return processed;
|
||||
// Sanitize the HTML using our configured DOMPurify
|
||||
return DOMPurify.sanitize(content);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,8 +123,8 @@ export function formatForwardedEmail(email: EmailMessage): {
|
||||
const toString = formatEmailAddresses(email.to || []);
|
||||
const dateString = formatEmailDate(email.date);
|
||||
|
||||
// Get and clean original content
|
||||
const originalContent = cleanHtmlContent(email.content || email.html || email.text || '');
|
||||
// Get and sanitize original content
|
||||
const originalContent = sanitizeHtml(email.content || email.html || email.text || '');
|
||||
|
||||
// Check if the content already has a forwarded message header
|
||||
const hasExistingHeader = originalContent.includes('---------- Forwarded message ---------');
|
||||
@ -209,8 +199,8 @@ export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all
|
||||
// Create quote header
|
||||
const quoteHeader = `<div style="font-weight: 500; direction: ltr; text-align: left;" dir="ltr">On ${formattedDate}, ${fromText} wrote:</div>`;
|
||||
|
||||
// Get and clean original content
|
||||
const quotedContent = cleanHtmlContent(email.html || email.content || email.text || '');
|
||||
// Get and sanitize original content
|
||||
const quotedContent = sanitizeHtml(email.html || email.content || email.text || '');
|
||||
|
||||
// Format recipients
|
||||
let to = formatEmailAddresses(email.from || []);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user