mail page fix design
This commit is contained in:
parent
7fc90a4940
commit
30684cb3b9
@ -1433,10 +1433,11 @@ export default function CourrierPage() {
|
||||
|
||||
// Get the formatted original email content
|
||||
const originalContent = getReplyBody(selectedEmail, type);
|
||||
|
||||
// Format with clear separator
|
||||
|
||||
// Create a clean structure with clear separation
|
||||
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}
|
||||
</div>
|
||||
`;
|
||||
|
||||
@ -57,60 +57,65 @@ export default function ComposeEmail({
|
||||
originalEmail
|
||||
}: ComposeEmailProps) {
|
||||
const composeBodyRef = useRef<HTMLDivElement>(null);
|
||||
const newReplyRef = useRef<HTMLDivElement>(null);
|
||||
const [showOriginalContent, setShowOriginalContent] = useState(true);
|
||||
|
||||
const [localContent, setLocalContent] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (composeBodyRef.current) {
|
||||
const decodedContent = decodeComposeContent(composeBody);
|
||||
|
||||
// Create the clear structure with proper content
|
||||
composeBodyRef.current.innerHTML = `
|
||||
<div id="new-reply-area" dir="ltr" style="margin-bottom: 20px; min-height: 40px; direction: ltr; text-align: left; unicode-bidi: normal;"></div>
|
||||
<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;">
|
||||
${decodedContent}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Place cursor in the new reply area
|
||||
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];
|
||||
// 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>
|
||||
`;
|
||||
|
||||
// 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);
|
||||
// Place cursor at the beginning of new reply area
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
// If no original content yet, just encode the user's input
|
||||
const encodedContent = encodeComposeContent(fullContent);
|
||||
setComposeBody(encodedContent);
|
||||
// For existing content, just update the original email part
|
||||
const originalEmailDiv = composeBodyRef.current.querySelector('#original-email');
|
||||
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>) => {
|
||||
@ -261,20 +266,12 @@ export default function ComposeEmail({
|
||||
</div>
|
||||
|
||||
{/* Original Email Content Preview */}
|
||||
{originalEmail && showOriginalContent && (
|
||||
{originalEmail && (
|
||||
<div className="border rounded-md p-4 bg-gray-50">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h4 className="text-sm font-medium text-gray-700">
|
||||
{originalEmail.type === 'forward' ? 'Forwarded Message' : 'Original Message'}
|
||||
</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
|
||||
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"
|
||||
style={{
|
||||
minHeight: '200px',
|
||||
direction: 'ltr',
|
||||
textAlign: 'left',
|
||||
unicodeBidi: 'normal'
|
||||
direction: 'ltr'
|
||||
}}
|
||||
dir="ltr"
|
||||
spellCheck="true"
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
/**
|
||||
* 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 {
|
||||
if (!content) return '';
|
||||
|
||||
// Debug logging
|
||||
console.log("Before decode:", content);
|
||||
|
||||
// Basic HTML cleaning without any string manipulation that could reverse text
|
||||
|
||||
// Basic HTML cleaning without creating nested structures
|
||||
let cleaned = content
|
||||
// Remove script and style tags
|
||||
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
|
||||
@ -29,8 +26,6 @@ export function decodeComposeContent(content: string): string {
|
||||
.replace(/<br\s*\/?>/gi, '\n')
|
||||
.replace(/<p[^>]*>/gi, '\n')
|
||||
.replace(/<\/p>/gi, '\n')
|
||||
.replace(/<div[^>]*>/gi, '\n')
|
||||
.replace(/<\/div>/gi, '\n')
|
||||
// Handle lists
|
||||
.replace(/<ul[^>]*>/gi, '\n')
|
||||
.replace(/<\/ul>/gi, '\n')
|
||||
@ -59,22 +54,15 @@ export function decodeComposeContent(content: string): string {
|
||||
// Clean up whitespace
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
|
||||
// Debug logging
|
||||
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>`;
|
||||
|
||||
// Do NOT wrap in additional divs
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
export function encodeComposeContent(content: string): string {
|
||||
if (!content) return '';
|
||||
|
||||
// Debug logging
|
||||
console.log("Before encode:", content);
|
||||
|
||||
// Basic HTML encoding without reversing
|
||||
|
||||
// Basic HTML encoding without adding structure
|
||||
const encoded = content
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
@ -82,9 +70,6 @@ export function encodeComposeContent(content: string): string {
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/\n/g, '<br>');
|
||||
|
||||
// Debug logging
|
||||
console.log("After encode:", encoded);
|
||||
|
||||
|
||||
return encoded;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user