panel 2 courier api restore
This commit is contained in:
parent
28a6478ea0
commit
a585bcb51d
@ -10,6 +10,8 @@ import { decodeComposeContent, encodeComposeContent } from '@/lib/compose-mime-d
|
|||||||
import { Email } from '@/app/courrier/page';
|
import { Email } from '@/app/courrier/page';
|
||||||
import mime from 'mime';
|
import mime from 'mime';
|
||||||
import { simpleParser } from 'mailparser';
|
import { simpleParser } from 'mailparser';
|
||||||
|
import { decodeEmail, cleanHtml } from '@/lib/mail-parser-wrapper';
|
||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
interface ComposeEmailProps {
|
interface ComposeEmailProps {
|
||||||
showCompose: boolean;
|
showCompose: boolean;
|
||||||
@ -126,124 +128,107 @@ export default function ComposeEmail({
|
|||||||
// Format the reply/forward content
|
// Format the reply/forward content
|
||||||
const type = replyTo ? 'reply' : 'forward';
|
const type = replyTo ? 'reply' : 'forward';
|
||||||
|
|
||||||
// Use simple, reliable formatting for the quoted content
|
|
||||||
const formatEmailAddresses = (addresses: any) => {
|
|
||||||
if (!addresses) return 'Unknown';
|
|
||||||
if (typeof addresses === 'string') return addresses;
|
|
||||||
if (Array.isArray(addresses)) {
|
|
||||||
return addresses.map(addr => addr.name || addr.address).join(', ');
|
|
||||||
}
|
|
||||||
return String(addresses);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Extract plain text content for reliable display
|
|
||||||
let emailContent = '';
|
|
||||||
try {
|
try {
|
||||||
// Parse the original email to get clean content
|
// Parse the email to get headers and content - using the same function as panel 3
|
||||||
const response = await fetch('/api/parse-email', {
|
const decoded = await decodeEmail(emailToProcess.content);
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ email: emailToProcess.content }),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
// Format the content based on reply type
|
||||||
const data = await response.json();
|
const quotedContent = type === 'forward' ? `
|
||||||
emailContent = data.text || '';
|
<div class="forwarded-message" style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
||||||
// If no text content, try to extract from HTML
|
---------- Forwarded message ---------<br/>
|
||||||
if (!emailContent && data.html) {
|
From: ${decoded.from || 'Unknown Sender'}<br/>
|
||||||
// Create a temporary div to extract text from HTML
|
Date: ${decoded.date ? decoded.date.toLocaleString() : new Date().toLocaleString()}<br/>
|
||||||
const tempDiv = document.createElement('div');
|
Subject: ${decoded.subject || 'No Subject'}<br/>
|
||||||
tempDiv.innerHTML = data.html;
|
To: ${decoded.to || ''}<br/>
|
||||||
emailContent = tempDiv.textContent || tempDiv.innerText || '';
|
${decoded.cc ? `Cc: ${decoded.cc}<br/>` : ''}
|
||||||
|
</div>
|
||||||
|
<div class="message-content" style="margin-top: 10px; border: 1px solid #e5e7eb; padding: 10px; border-radius: 4px; max-height: 300px; overflow-y: auto; color: #374151;">
|
||||||
|
${decoded.html ? DOMPurify.sanitize(decoded.html) : (decoded.text ? `<pre style="white-space: pre-wrap; word-break: break-word; font-family: system-ui, sans-serif; margin: 0;">${decoded.text}</pre>` : 'No content available')}
|
||||||
|
</div>
|
||||||
|
` : `
|
||||||
|
<div class="quoted-message" style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
||||||
|
On ${decoded.date ? decoded.date.toLocaleString() : new Date().toLocaleString()}, ${decoded.from || 'Unknown Sender'} wrote:
|
||||||
|
</div>
|
||||||
|
<div class="message-content" style="margin: 10px 0 0 10px; padding: 10px; border-left: 2px solid #e5e7eb; border: 1px solid #e5e7eb; border-radius: 4px; max-height: 300px; overflow-y: auto; color: #374151;">
|
||||||
|
${decoded.html ? DOMPurify.sanitize(decoded.html) : (decoded.text ? `<pre style="white-space: pre-wrap; word-break: break-word; font-family: system-ui, sans-serif; margin: 0;">${decoded.text}</pre>` : 'No content available')}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Set the content in the compose area with proper structure
|
||||||
|
const formattedContent = `
|
||||||
|
<div class="compose-area" contenteditable="true" style="min-height: 100px; padding: 10px;">
|
||||||
|
<div class="cursor-position" style="min-height: 20px; cursor: text;"><br/></div>
|
||||||
|
${quotedContent}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (composeBodyRef.current) {
|
||||||
|
composeBodyRef.current.innerHTML = formattedContent;
|
||||||
|
|
||||||
|
// Place cursor at the beginning before the quoted content
|
||||||
|
const selection = window.getSelection();
|
||||||
|
const range = document.createRange();
|
||||||
|
const firstDiv = composeBodyRef.current.querySelector('.cursor-position');
|
||||||
|
if (firstDiv) {
|
||||||
|
range.setStart(firstDiv, 0);
|
||||||
|
range.collapse(true);
|
||||||
|
selection?.removeAllRanges();
|
||||||
|
selection?.addRange(range);
|
||||||
|
(firstDiv as HTMLElement).focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// After setting the HTML content, add event listeners for scrolling
|
||||||
|
const messageContents = composeBodyRef.current.querySelectorAll('.message-content');
|
||||||
|
messageContents.forEach(container => {
|
||||||
|
// Make sure the container is properly styled for scrolling
|
||||||
|
(container as HTMLElement).style.maxHeight = '300px';
|
||||||
|
(container as HTMLElement).style.overflowY = 'auto';
|
||||||
|
(container as HTMLElement).style.border = '1px solid #e5e7eb';
|
||||||
|
(container as HTMLElement).style.borderRadius = '4px';
|
||||||
|
(container as HTMLElement).style.padding = '10px';
|
||||||
|
|
||||||
|
// Ensure wheel events are properly handled
|
||||||
|
if (!(container as HTMLElement).hasAttribute('data-scroll-handler-attached')) {
|
||||||
|
container.addEventListener('wheel', (e: Event) => {
|
||||||
|
const wheelEvent = e as WheelEvent;
|
||||||
|
const target = e.currentTarget as HTMLElement;
|
||||||
|
|
||||||
|
// Check if we're at the boundary of the scrollable area
|
||||||
|
const isAtBottom = target.scrollHeight - target.scrollTop <= target.clientHeight + 1;
|
||||||
|
const isAtTop = target.scrollTop <= 0;
|
||||||
|
|
||||||
|
// Only prevent default if we're not at the boundaries in the direction of scrolling
|
||||||
|
if ((wheelEvent.deltaY > 0 && !isAtBottom) || (wheelEvent.deltaY < 0 && !isAtTop)) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault(); // Prevent the parent container from scrolling
|
||||||
|
}
|
||||||
|
}, { passive: false }); // Important for preventDefault to work
|
||||||
|
|
||||||
|
// Mark this element as having a scroll handler attached
|
||||||
|
(container as HTMLElement).setAttribute('data-scroll-handler-attached', 'true');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update compose state
|
||||||
|
setComposeBody(formattedContent);
|
||||||
|
setLocalContent(formattedContent);
|
||||||
|
console.log('[DEBUG] Successfully set compose content with scrollable message area');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[DEBUG] Error parsing email:', error);
|
console.error('[DEBUG] Error parsing email:', error);
|
||||||
// Fallback to simple content extraction
|
// Fallback to simple content extraction
|
||||||
emailContent = emailToProcess.content.replace(/<[^>]*>/g, '');
|
const fallbackContent = emailToProcess.content.replace(/<[^>]*>/g, '');
|
||||||
}
|
composeBodyRef.current.innerHTML = `
|
||||||
|
<div class="compose-area" contenteditable="true">
|
||||||
// Format the content based on reply type
|
<br/>
|
||||||
const quotedContent = type === 'forward' ? `
|
<div style="color: #ef4444;">Error loading original message.</div>
|
||||||
<div class="forwarded-message" style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
<div style="color: #64748b; font-size: 0.875rem; margin-top: 0.5rem;">
|
||||||
---------- Forwarded message ---------<br/>
|
Technical details: ${error instanceof Error ? error.message : 'Unknown error'}
|
||||||
From: ${formatEmailAddresses(emailToProcess.from) || 'Unknown Sender'}<br/>
|
</div>
|
||||||
Date: ${new Date(emailToProcess.date || Date.now()).toLocaleString()}<br/>
|
</div>
|
||||||
Subject: ${emailToProcess.subject || 'No Subject'}<br/>
|
`;
|
||||||
To: ${formatEmailAddresses(emailToProcess.to) || ''}<br/>
|
setComposeBody(fallbackContent);
|
||||||
${emailToProcess.cc ? `Cc: ${formatEmailAddresses(emailToProcess.cc)}<br/>` : ''}
|
setLocalContent(fallbackContent);
|
||||||
</div>
|
|
||||||
<div class="message-content" style="margin-top: 10px; border: 1px solid #e5e7eb; padding: 10px; border-radius: 4px; max-height: 300px; overflow-y: auto; color: #374151;">
|
|
||||||
<pre style="white-space: pre-wrap; word-break: break-word; font-family: system-ui, sans-serif; margin: 0;">${emailContent}</pre>
|
|
||||||
</div>
|
|
||||||
` : `
|
|
||||||
<div class="quoted-message" style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
|
||||||
On ${new Date(emailToProcess.date || Date.now()).toLocaleString()}, ${formatEmailAddresses(emailToProcess.from) || 'Unknown Sender'} wrote:
|
|
||||||
</div>
|
|
||||||
<div class="message-content" style="margin: 10px 0 0 10px; padding: 10px; border-left: 2px solid #e5e7eb; border: 1px solid #e5e7eb; border-radius: 4px; max-height: 300px; overflow-y: auto; color: #374151;">
|
|
||||||
<pre style="white-space: pre-wrap; word-break: break-word; font-family: system-ui, sans-serif; margin: 0;">${emailContent}</pre>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Set the content in the compose area with proper structure
|
|
||||||
const formattedContent = `
|
|
||||||
<div class="compose-area" contenteditable="true" style="min-height: 100px; padding: 10px;">
|
|
||||||
<div class="cursor-position" style="min-height: 20px; cursor: text;"><br/></div>
|
|
||||||
${quotedContent}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
if (composeBodyRef.current) {
|
|
||||||
composeBodyRef.current.innerHTML = formattedContent;
|
|
||||||
|
|
||||||
// Place cursor at the beginning before the quoted content
|
|
||||||
const selection = window.getSelection();
|
|
||||||
const range = document.createRange();
|
|
||||||
const firstDiv = composeBodyRef.current.querySelector('.cursor-position');
|
|
||||||
if (firstDiv) {
|
|
||||||
range.setStart(firstDiv, 0);
|
|
||||||
range.collapse(true);
|
|
||||||
selection?.removeAllRanges();
|
|
||||||
selection?.addRange(range);
|
|
||||||
(firstDiv as HTMLElement).focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
// After setting the HTML content, add event listeners for scrolling
|
|
||||||
const messageContents = composeBodyRef.current.querySelectorAll('.message-content');
|
|
||||||
messageContents.forEach(container => {
|
|
||||||
// Make sure the container is properly styled for scrolling
|
|
||||||
(container as HTMLElement).style.maxHeight = '300px';
|
|
||||||
(container as HTMLElement).style.overflowY = 'auto';
|
|
||||||
(container as HTMLElement).style.border = '1px solid #e5e7eb';
|
|
||||||
(container as HTMLElement).style.borderRadius = '4px';
|
|
||||||
(container as HTMLElement).style.padding = '10px';
|
|
||||||
|
|
||||||
// Ensure wheel events are properly handled
|
|
||||||
if (!(container as HTMLElement).hasAttribute('data-scroll-handler-attached')) {
|
|
||||||
container.addEventListener('wheel', (e: Event) => {
|
|
||||||
const wheelEvent = e as WheelEvent;
|
|
||||||
const target = e.currentTarget as HTMLElement;
|
|
||||||
|
|
||||||
// Check if we're at the boundary of the scrollable area
|
|
||||||
const isAtBottom = target.scrollHeight - target.scrollTop <= target.clientHeight + 1;
|
|
||||||
const isAtTop = target.scrollTop <= 0;
|
|
||||||
|
|
||||||
// Only prevent default if we're not at the boundaries in the direction of scrolling
|
|
||||||
if ((wheelEvent.deltaY > 0 && !isAtBottom) || (wheelEvent.deltaY < 0 && !isAtTop)) {
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault(); // Prevent the parent container from scrolling
|
|
||||||
}
|
|
||||||
}, { passive: false }); // Important for preventDefault to work
|
|
||||||
|
|
||||||
// Mark this element as having a scroll handler attached
|
|
||||||
(container as HTMLElement).setAttribute('data-scroll-handler-attached', 'true');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update compose state
|
|
||||||
setComposeBody(formattedContent);
|
|
||||||
setLocalContent(formattedContent);
|
|
||||||
console.log('[DEBUG] Successfully set compose content with scrollable message area');
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[DEBUG] Error initializing compose content:', error);
|
console.error('[DEBUG] Error initializing compose content:', error);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user