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
|
// 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>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -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() === '') {
|
||||||
// Create the clear structure with proper content
|
// Simple structure with one area for new text and one for original
|
||||||
composeBodyRef.current.innerHTML = `
|
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 id="new-reply-area" dir="ltr" style="margin-bottom: 20px; min-height: 40px;"></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;">
|
<div id="original-email" dir="ltr" style="border-top: 1px solid #e0e0e0; padding-top: 10px; color: #555;">
|
||||||
${decodedContent}
|
${composeBody ? decodeComposeContent(composeBody) : ''}
|
||||||
</div>
|
</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];
|
|
||||||
|
|
||||||
// Keep original content as is
|
// Place cursor at the beginning of new reply area
|
||||||
const originalContent = '<div class="original-email"' + parts[1];
|
const newReplyArea = composeBodyRef.current.querySelector('#new-reply-area');
|
||||||
|
if (newReplyArea) {
|
||||||
// Update the compose body with encoded content
|
const range = document.createRange();
|
||||||
const encodedContent = encodeComposeContent(userContent + originalContent);
|
const sel = window.getSelection();
|
||||||
setComposeBody(encodedContent);
|
range.setStart(newReplyArea, 0);
|
||||||
|
range.collapse(true);
|
||||||
|
sel?.removeAllRanges();
|
||||||
|
sel?.addRange(range);
|
||||||
|
}
|
||||||
} 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"
|
||||||
|
|||||||
@ -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')
|
||||||
@ -59,22 +54,15 @@ export function decodeComposeContent(content: string): string {
|
|||||||
// Clean up whitespace
|
// Clean up whitespace
|
||||||
.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, '&')
|
.replace(/&/g, '&')
|
||||||
.replace(/</g, '<')
|
.replace(/</g, '<')
|
||||||
@ -82,9 +70,6 @@ export function encodeComposeContent(content: string): string {
|
|||||||
.replace(/"/g, '"')
|
.replace(/"/g, '"')
|
||||||
.replace(/'/g, ''')
|
.replace(/'/g, ''')
|
||||||
.replace(/\n/g, '<br>');
|
.replace(/\n/g, '<br>');
|
||||||
|
|
||||||
// Debug logging
|
|
||||||
console.log("After encode:", encoded);
|
|
||||||
|
|
||||||
return encoded;
|
return encoded;
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user