mail page fix design

This commit is contained in:
alma 2025-04-21 22:08:48 +02:00
parent 7fc90a4940
commit 30684cb3b9
3 changed files with 67 additions and 86 deletions

View File

@ -1434,9 +1434,10 @@ export default function CourrierPage() {
// Get the formatted original email content // Get the formatted original email content
const originalContent = getReplyBody(selectedEmail, type); const originalContent = getReplyBody(selectedEmail, type);
// Format with clear separator // Create a clean structure with clear separation
const formattedContent = ` const formattedContent = `
<div id="original-email" dir="ltr" style="margin-top: 20px; border-top: 1px solid #e0e0e0; padding-top: 10px; color: #555;"> <div id="new-reply-area" dir="ltr" style="margin-bottom: 20px; min-height: 40px;"></div>
<div id="original-email" dir="ltr" style="border-top: 1px solid #e0e0e0; padding-top: 10px; color: #555;">
${originalContent} ${originalContent}
</div> </div>
`; `;

View File

@ -57,60 +57,65 @@ export default function ComposeEmail({
originalEmail originalEmail
}: ComposeEmailProps) { }: ComposeEmailProps) {
const composeBodyRef = useRef<HTMLDivElement>(null); const composeBodyRef = useRef<HTMLDivElement>(null);
const newReplyRef = useRef<HTMLDivElement>(null); const [localContent, setLocalContent] = useState('');
const [showOriginalContent, setShowOriginalContent] = useState(true);
useEffect(() => { useEffect(() => {
if (composeBodyRef.current) { if (composeBodyRef.current) {
const decodedContent = decodeComposeContent(composeBody); // Only initialize once when empty or first loading
if (!composeBody || composeBody.trim() === '') {
// Simple structure with one area for new text and one for original
composeBodyRef.current.innerHTML = `
<div id="new-reply-area" dir="ltr" style="margin-bottom: 20px; min-height: 40px;"></div>
<div id="original-email" dir="ltr" style="border-top: 1px solid #e0e0e0; padding-top: 10px; color: #555;">
${composeBody ? decodeComposeContent(composeBody) : ''}
</div>
`;
// Create the clear structure with proper content // Place cursor at the beginning of new reply area
composeBodyRef.current.innerHTML = ` const newReplyArea = composeBodyRef.current.querySelector('#new-reply-area');
<div id="new-reply-area" dir="ltr" style="margin-bottom: 20px; min-height: 40px; direction: ltr; text-align: left; unicode-bidi: normal;"></div> if (newReplyArea) {
<div class="original-email" dir="ltr" style="border-top: 1px solid #e0e0e0; padding-top: 10px; color: #555; direction: ltr; text-align: left; unicode-bidi: normal;"> const range = document.createRange();
${decodedContent} const sel = window.getSelection();
</div> range.setStart(newReplyArea, 0);
`; range.collapse(true);
sel?.removeAllRanges();
// Place cursor in the new reply area sel?.addRange(range);
const newReplyArea = composeBodyRef.current.querySelector('#new-reply-area'); }
if (newReplyArea) {
const range = document.createRange();
const sel = window.getSelection();
range.setStart(newReplyArea, 0);
range.collapse(true);
sel?.removeAllRanges();
sel?.addRange(range);
}
}
}, [composeBody]);
// Enhanced input handler to prevent text reversal
const handleInput = (e: React.FormEvent<HTMLDivElement>) => {
if (composeBodyRef.current) {
// Get the current HTML content
const fullContent = e.currentTarget.innerHTML;
// Split at the divider to separate new reply from original content
const parts = fullContent.split('<div class="original-email"');
// Only update the first part (user's input) and preserve original email
if (parts.length > 1) {
// Extract user-typed content (ensuring it's not reversed)
const userContent = parts[0];
// Keep original content as is
const originalContent = '<div class="original-email"' + parts[1];
// Update the compose body with encoded content
const encodedContent = encodeComposeContent(userContent + originalContent);
setComposeBody(encodedContent);
} else { } else {
// If no original content yet, just encode the user's input // For existing content, just update the original email part
const encodedContent = encodeComposeContent(fullContent); const originalEmailDiv = composeBodyRef.current.querySelector('#original-email');
setComposeBody(encodedContent); if (originalEmailDiv) {
originalEmailDiv.innerHTML = decodeComposeContent(composeBody);
}
} }
} }
}, []); // Only run once on initial mount, not on every composeBody change
// Modified input handler to prevent nested structures
const handleInput = (e: React.FormEvent<HTMLDivElement>) => {
// Get raw content from the editable div
const currentContent = e.currentTarget.innerHTML;
// Store the original content reference
const originalEmailDiv = e.currentTarget.querySelector('#original-email');
const originalContent = originalEmailDiv?.innerHTML || '';
// Extract user-typed content (everything before original-email div)
const userContent = currentContent.split('<div id="original-email"')[0];
// Update local state
setLocalContent(userContent);
// Combine new reply and original formatted correctly
const combinedContent = `
${userContent}
<div id="original-email" dir="ltr" style="border-top: 1px solid #e0e0e0; padding-top: 10px; color: #555;">
${originalContent}
</div>
`;
// Update the compose body
setComposeBody(combinedContent);
}; };
const handleFileAttachment = async (e: React.ChangeEvent<HTMLInputElement>) => { const handleFileAttachment = async (e: React.ChangeEvent<HTMLInputElement>) => {
@ -261,20 +266,12 @@ export default function ComposeEmail({
</div> </div>
{/* Original Email Content Preview */} {/* Original Email Content Preview */}
{originalEmail && showOriginalContent && ( {originalEmail && (
<div className="border rounded-md p-4 bg-gray-50"> <div className="border rounded-md p-4 bg-gray-50">
<div className="flex items-center justify-between mb-2"> <div className="flex items-center justify-between mb-2">
<h4 className="text-sm font-medium text-gray-700"> <h4 className="text-sm font-medium text-gray-700">
{originalEmail.type === 'forward' ? 'Forwarded Message' : 'Original Message'} {originalEmail.type === 'forward' ? 'Forwarded Message' : 'Original Message'}
</h4> </h4>
<Button
variant="ghost"
size="sm"
onClick={() => setShowOriginalContent(false)}
className="text-gray-500 hover:text-gray-700"
>
<X className="h-4 w-4" />
</Button>
</div> </div>
<div <div
className="prose max-w-none text-sm text-gray-600" className="prose max-w-none text-sm text-gray-600"
@ -293,9 +290,7 @@ export default function ComposeEmail({
className="w-full h-full mt-1 bg-white border border-gray-300 rounded-md p-2 text-gray-900 overflow-y-auto" className="w-full h-full mt-1 bg-white border border-gray-300 rounded-md p-2 text-gray-900 overflow-y-auto"
style={{ style={{
minHeight: '200px', minHeight: '200px',
direction: 'ltr', direction: 'ltr'
textAlign: 'left',
unicodeBidi: 'normal'
}} }}
dir="ltr" dir="ltr"
spellCheck="true" spellCheck="true"

View File

@ -1,15 +1,12 @@
/** /**
* Simple MIME decoder for compose message box * Simple MIME decoder for compose message box
* Handles basic email content and text direction * Handles basic email content without creating nested structures
*/ */
export function decodeComposeContent(content: string): string { export function decodeComposeContent(content: string): string {
if (!content) return ''; if (!content) return '';
// Debug logging // Basic HTML cleaning without creating nested structures
console.log("Before decode:", content);
// Basic HTML cleaning without any string manipulation that could reverse text
let cleaned = content let cleaned = content
// Remove script and style tags // Remove script and style tags
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '') .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
@ -29,8 +26,6 @@ export function decodeComposeContent(content: string): string {
.replace(/<br\s*\/?>/gi, '\n') .replace(/<br\s*\/?>/gi, '\n')
.replace(/<p[^>]*>/gi, '\n') .replace(/<p[^>]*>/gi, '\n')
.replace(/<\/p>/gi, '\n') .replace(/<\/p>/gi, '\n')
.replace(/<div[^>]*>/gi, '\n')
.replace(/<\/div>/gi, '\n')
// Handle lists // Handle lists
.replace(/<ul[^>]*>/gi, '\n') .replace(/<ul[^>]*>/gi, '\n')
.replace(/<\/ul>/gi, '\n') .replace(/<\/ul>/gi, '\n')
@ -60,21 +55,14 @@ export function decodeComposeContent(content: string): string {
.replace(/\s+/g, ' ') .replace(/\s+/g, ' ')
.trim(); .trim();
// Debug logging // Do NOT wrap in additional divs
console.log("After decode:", cleaned);
// Ensure all content has proper direction
cleaned = `<div dir="ltr" style="direction: ltr; text-align: left; unicode-bidi: normal;">${cleaned}</div>`;
return cleaned; return cleaned;
} }
export function encodeComposeContent(content: string): string { export function encodeComposeContent(content: string): string {
if (!content) return ''; if (!content) return '';
// Debug logging // Basic HTML encoding without adding structure
console.log("Before encode:", content);
// Basic HTML encoding without reversing
const encoded = content const encoded = content
.replace(/&/g, '&amp;') .replace(/&/g, '&amp;')
.replace(/</g, '&lt;') .replace(/</g, '&lt;')
@ -83,8 +71,5 @@ export function encodeComposeContent(content: string): string {
.replace(/'/g, '&#39;') .replace(/'/g, '&#39;')
.replace(/\n/g, '<br>'); .replace(/\n/g, '<br>');
// Debug logging
console.log("After encode:", encoded);
return encoded; return encoded;
} }