courrier clean 2$

This commit is contained in:
alma 2025-04-26 18:46:21 +02:00
parent e3b946f7e9
commit 3e6b8cae1f

View File

@ -109,19 +109,13 @@ function isLegacyProps(props: ComposeEmailAllProps): props is LegacyComposeEmail
return 'showCompose' in props && 'setShowCompose' in props; return 'showCompose' in props && 'setShowCompose' in props;
} }
// Create a utility function to preprocess email content // Configure DOMPurify to preserve certain attributes
function preprocessEmailContent(content: string): string { DOMPurify.addHook('afterSanitizeAttributes', function(node) {
if (!content) return ''; // Preserve direction attributes
if (node.hasAttribute('dir')) {
// Sanitize HTML content node.setAttribute('dir', node.getAttribute('dir') || 'ltr');
const sanitized = DOMPurify.sanitize(content); }
});
// Fix common RTL/LTR issues by ensuring consistent direction
// Wrap content in a div with explicit direction if needed
const processed = sanitized.replace(/<div dir=(["'])rtl\1/g, '<div dir="ltr"');
return processed;
}
export default function ComposeEmail(props: ComposeEmailAllProps) { export default function ComposeEmail(props: ComposeEmailAllProps) {
// Handle legacy props by adapting them to new component // Handle legacy props by adapting them to new component
@ -137,10 +131,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
const [cc, setCc] = useState<string>(''); const [cc, setCc] = useState<string>('');
const [bcc, setBcc] = useState<string>(''); const [bcc, setBcc] = useState<string>('');
const [subject, setSubject] = useState<string>(''); const [subject, setSubject] = useState<string>('');
const [body, setBody] = useState<string>(''); const [emailContent, setEmailContent] = useState<string>('');
const [userMessage, setUserMessage] = useState<string>('');
const [originalContent, setOriginalContent] = useState<string>('');
const [editingOriginalContent, setEditingOriginalContent] = useState<boolean>(false);
const [showCc, setShowCc] = useState<boolean>(false); const [showCc, setShowCc] = useState<boolean>(false);
const [showBcc, setShowBcc] = useState<boolean>(false); const [showBcc, setShowBcc] = useState<boolean>(false);
const [sending, setSending] = useState<boolean>(false); const [sending, setSending] = useState<boolean>(false);
@ -153,7 +144,6 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
// Refs // Refs
const editorRef = useRef<HTMLDivElement>(null); const editorRef = useRef<HTMLDivElement>(null);
const originalContentRef = useRef<HTMLDivElement>(null);
const attachmentInputRef = useRef<HTMLInputElement>(null); const attachmentInputRef = useRef<HTMLInputElement>(null);
// Initialize the form when replying to or forwarding an email // Initialize the form when replying to or forwarding an email
@ -179,8 +169,8 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
// For forwarding, use the dedicated formatter // For forwarding, use the dedicated formatter
const { subject, content } = formatForwardedEmail(formatterEmail); const { subject, content } = formatForwardedEmail(formatterEmail);
setSubject(subject); setSubject(subject);
setBody(content); // Apply the formatted content directly without additional sanitization
setUserMessage(content); setEmailContent(content);
} else { } else {
// For reply/reply-all, use the reply formatter // For reply/reply-all, use the reply formatter
const { to, cc, subject, content } = formatReplyEmail(formatterEmail, type as 'reply' | 'reply-all'); const { to, cc, subject, content } = formatReplyEmail(formatterEmail, type as 'reply' | 'reply-all');
@ -190,8 +180,8 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
setShowCc(true); setShowCc(true);
} }
setSubject(subject); setSubject(subject);
setBody(content); // Apply the formatted content directly without additional sanitization
setUserMessage(content); setEmailContent(content);
} }
// Focus editor after initializing // Focus editor after initializing
@ -199,10 +189,10 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
if (editorRef.current) { if (editorRef.current) {
editorRef.current.focus(); editorRef.current.focus();
// Place cursor at the beginning try {
const selection = window.getSelection(); // Place cursor at the beginning
if (selection) { const selection = window.getSelection();
try { if (selection) {
const range = document.createRange(); const range = document.createRange();
if (editorRef.current.firstChild) { if (editorRef.current.firstChild) {
@ -212,9 +202,9 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
selection.removeAllRanges(); selection.removeAllRanges();
selection.addRange(range); selection.addRange(range);
} }
} catch (e) {
console.error('Error positioning cursor:', e);
} }
} catch (e) {
console.error('Error positioning cursor:', e);
} }
} }
}, 100); }, 100);
@ -224,43 +214,6 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
} }
}, [initialEmail, type]); }, [initialEmail, type]);
// Format date for the forwarded message header
const formatDate = (date: Date | null): string => {
if (!date) return '';
try {
return date.toLocaleString('en-US', {
weekday: 'short',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
} catch (e) {
return date.toString();
}
};
// Format sender address in a readable format
const formatSender = (from: Array<{name?: string, address: string}> | undefined): string => {
if (!from || from.length === 0) return 'Unknown';
return from.map(sender =>
sender.name && sender.name !== sender.address
? `${sender.name} <${sender.address}>`
: sender.address
).join(', ');
};
// Format recipient addresses in a readable format
const formatRecipients = (recipients: Array<{name?: string, address: string}> | undefined): string => {
if (!recipients || recipients.length === 0) return '';
return recipients.map(recipient =>
recipient.name && recipient.name !== recipient.address
? `${recipient.name} <${recipient.address}>`
: recipient.address
).join(', ');
};
// Handle attachment selection // Handle attachment selection
const handleAttachmentClick = () => { const handleAttachmentClick = () => {
attachmentInputRef.current?.click(); attachmentInputRef.current?.click();
@ -271,12 +224,6 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
const files = e.target.files; const files = e.target.files;
if (!files || files.length === 0) return; if (!files || files.length === 0) return;
// Convert selected files to attachments
const newAttachments = Array.from(files).map(file => ({
file,
uploading: true
}));
// Read files as data URLs // Read files as data URLs
for (const file of files) { for (const file of files) {
const reader = new FileReader(); const reader = new FileReader();
@ -308,50 +255,20 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
setAttachments(current => current.filter((_, i) => i !== index)); setAttachments(current => current.filter((_, i) => i !== index));
}; };
// Handle editing of original content // Handle editor input without re-sanitizing content
const handleOriginalContentInput = (e: React.FormEvent<HTMLDivElement>) => { const handleEditorInput = () => {
if (originalContentRef.current) { if (editorRef.current) {
const content = originalContentRef.current.innerHTML; // Capture innerHTML directly without reapplying DOMPurify
setOriginalContent(content); setEmailContent(editorRef.current.innerHTML);
} }
}; };
// Toggle original content editing // Toggle text direction for the entire editor
const toggleEditOriginalContent = () => { const toggleTextDirection = () => {
setEditingOriginalContent(!editingOriginalContent); setIsRTL(!isRTL);
// If we're starting to edit, make sure the content is ready and focused
if (!editingOriginalContent) {
setTimeout(() => {
if (originalContentRef.current) {
originalContentRef.current.focus();
// Place cursor at the beginning
const selection = window.getSelection();
const range = document.createRange();
if (originalContentRef.current.firstChild) {
range.setStart(originalContentRef.current.firstChild, 0);
} else {
range.setStart(originalContentRef.current, 0);
}
range.collapse(true);
selection?.removeAllRanges();
selection?.addRange(range);
}
}, 100);
}
}; };
// Handling click on original content even when not in edit mode // Send email without modifying pre-formatted content
const handleOriginalContentClick = () => {
if (!editingOriginalContent) {
toggleEditOriginalContent();
}
};
// Modified send handler to use the entire body content
const handleSend = async () => { const handleSend = async () => {
if (!to) { if (!to) {
alert('Please specify at least one recipient'); alert('Please specify at least one recipient');
@ -361,13 +278,12 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
try { try {
setSending(true); setSending(true);
// Use the complete edited content as is
await onSend({ await onSend({
to, to,
cc: cc || undefined, cc: cc || undefined,
bcc: bcc || undefined, bcc: bcc || undefined,
subject, subject,
body: userMessage, // Use the full edited message body: emailContent, // Use the raw edited content
attachments attachments
}); });
@ -380,23 +296,6 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
} }
}; };
// Handle editor input for the entire content
const handleEditorInput = (e: React.FormEvent<HTMLDivElement>) => {
if (editorRef.current) {
const content = editorRef.current.innerHTML;
setUserMessage(content);
setBody(content);
}
};
// Toggle text direction
const toggleTextDirection = () => {
setIsRTL(!isRTL);
if (editorRef.current) {
editorRef.current.dir = !isRTL ? 'rtl' : 'ltr';
}
};
return ( return (
<Card className="w-full max-w-4xl mx-auto"> <Card className="w-full max-w-4xl mx-auto">
<CardHeader> <CardHeader>
@ -509,14 +408,15 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
</Button> </Button>
</div> </div>
{/* Email editor with a single editable area for the entire message */} {/* Email editor with a single editable area */}
<div className="border rounded-md overflow-hidden"> <div className="border rounded-md overflow-hidden">
<div <div
ref={editorRef} ref={editorRef}
contentEditable={!sending} contentEditable={!sending}
className="w-full p-4 min-h-[300px] focus:outline-none" className="w-full p-4 min-h-[300px] focus:outline-none"
onInput={handleEditorInput} onInput={handleEditorInput}
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(body) }} // Use dangerouslySetInnerHTML without sanitizing again
dangerouslySetInnerHTML={{ __html: emailContent }}
dir={isRTL ? 'rtl' : 'ltr'} dir={isRTL ? 'rtl' : 'ltr'}
style={{ style={{
textAlign: isRTL ? 'right' : 'left' textAlign: isRTL ? 'right' : 'left'