panel 2 courier api restore
This commit is contained in:
parent
72207ee218
commit
94fb7453b5
@ -122,13 +122,20 @@ export default function ComposeEmail({
|
||||
console.log('[DEBUG] Attempting to fetch email content directly');
|
||||
try {
|
||||
// Fetch the email content if not available
|
||||
const response = await fetch(`/api/courrier/${emailToProcess.id}?folder=${encodeURIComponent(emailToProcess.folder || 'INBOX')}`);
|
||||
const response = await fetch(`/api/courrier/${emailToProcess.id}?folder=${encodeURIComponent(emailToProcess.folder || 'INBOX')}&fetchFull=true`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch email content: ${response.status}`);
|
||||
}
|
||||
|
||||
const fullContent = await response.json();
|
||||
console.log('[DEBUG] API response for full email content:', {
|
||||
hasContent: !!fullContent?.content,
|
||||
hasBody: !!fullContent?.body,
|
||||
hasHtml: !!fullContent?.html,
|
||||
hasText: !!fullContent?.text,
|
||||
contentLength: fullContent?.content?.length || 0
|
||||
});
|
||||
|
||||
// Update the email content with the fetched full content
|
||||
if (fullContent && fullContent.content) {
|
||||
@ -144,7 +151,23 @@ export default function ComposeEmail({
|
||||
console.log('[DEBUG] Successfully fetched TEXT for reply/forward');
|
||||
emailToProcess.content = fullContent.text;
|
||||
} else {
|
||||
throw new Error('No content in fetched email');
|
||||
console.error('[DEBUG] No usable content found in API response');
|
||||
|
||||
// Try using fullContent directly if it's a string
|
||||
if (typeof fullContent === 'string' && fullContent.length > 0) {
|
||||
console.log('[DEBUG] Using fullContent string directly');
|
||||
emailToProcess.content = fullContent;
|
||||
} else {
|
||||
throw new Error('No content in fetched email');
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we actually have content
|
||||
if (!emailToProcess.content || emailToProcess.content.trim().length === 0) {
|
||||
console.error('[DEBUG] Content still empty after fetch, using fallback');
|
||||
// Use any available preview or raw data
|
||||
emailToProcess.content = emailToProcess.preview ||
|
||||
(fullContent.raw ? fullContent.raw : 'Email content unavailable');
|
||||
}
|
||||
} catch (fetchError) {
|
||||
console.error('[DEBUG] Error fetching email content:', fetchError);
|
||||
@ -173,7 +196,12 @@ export default function ComposeEmail({
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[DEBUG] Sending content to parse-email API, length:', emailToProcess!.content.length);
|
||||
// Add more debug logging to track content fetching
|
||||
console.log('[DEBUG] Sending content to parse-email API, content type:',
|
||||
typeof emailToProcess!.content,
|
||||
'length:', emailToProcess!.content.length,
|
||||
'starts with:', emailToProcess!.content.substring(0, 50)
|
||||
);
|
||||
|
||||
let emailContent;
|
||||
let parseSuccess = false;
|
||||
@ -202,7 +230,21 @@ export default function ComposeEmail({
|
||||
throw new Error(data.error || 'Failed to parse email');
|
||||
}
|
||||
|
||||
// Prioritize HTML content if available, fallback to text
|
||||
emailContent = data.html || data.text || '';
|
||||
|
||||
// Add a fallback check if both HTML and text are empty
|
||||
if (!emailContent.trim()) {
|
||||
// Try to extract content from the raw source
|
||||
console.log('[DEBUG] Empty content from parser, using raw content as fallback');
|
||||
emailContent = emailToProcess.content;
|
||||
|
||||
// If content looks like HTML, use it directly, otherwise wrap in pre tags
|
||||
if (!emailContent.startsWith('<') || !emailContent.endsWith('>')) {
|
||||
emailContent = `<pre style="white-space: pre-wrap; word-break: break-word;">${emailContent}</pre>`;
|
||||
}
|
||||
}
|
||||
|
||||
parseSuccess = true;
|
||||
} catch (error) {
|
||||
console.error('[DEBUG] API parse error:', error);
|
||||
@ -211,117 +253,128 @@ export default function ComposeEmail({
|
||||
|
||||
// If content looks like HTML, use it directly, otherwise wrap in pre tags
|
||||
if (!emailContent.startsWith('<') || !emailContent.endsWith('>')) {
|
||||
emailContent = `<pre>${emailContent}</pre>`;
|
||||
emailContent = `<pre style="white-space: pre-wrap; word-break: break-word;">${emailContent}</pre>`;
|
||||
}
|
||||
}
|
||||
|
||||
if (!emailContent) {
|
||||
console.warn('[DEBUG] No content available after parsing');
|
||||
emailContent = '<div>No content available</div>';
|
||||
if (!emailContent || !emailContent.trim()) {
|
||||
console.warn('[DEBUG] No content available after parsing, trying direct content');
|
||||
// Final fallback: Try to use direct content or preview
|
||||
emailContent = emailToProcess.content || emailToProcess.preview ||
|
||||
(emailToProcess.body ?
|
||||
`<pre style="white-space: pre-wrap; word-break: break-word;">${emailToProcess.body}</pre>` :
|
||||
'<div>No content available in the original message</div>');
|
||||
}
|
||||
|
||||
// Format the reply/forward content
|
||||
const contentLength = emailToProcess && emailToProcess.content ? emailToProcess.content.length : 0;
|
||||
console.log('[DEBUG] Sending content to parse-email API, length:', contentLength);
|
||||
|
||||
let formattedContent;
|
||||
try {
|
||||
if (emailToProcess && emailToProcess.content) {
|
||||
// Process email 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);
|
||||
};
|
||||
|
||||
const quotedContent = forwardFrom ? `
|
||||
<div class="forwarded-message" style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
||||
---------- Forwarded message ---------<br/>
|
||||
From: ${formatEmailAddresses(emailToProcess?.from) || 'Unknown Sender'}<br/>
|
||||
Date: ${new Date(emailToProcess?.date || Date.now()).toLocaleString()}<br/>
|
||||
Subject: ${emailToProcess?.subject || 'No Subject'}<br/>
|
||||
To: ${formatEmailAddresses(emailToProcess?.to) || ''}<br/>
|
||||
${emailToProcess?.cc ? `Cc: ${formatEmailAddresses(emailToProcess.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;">
|
||||
${emailContent}
|
||||
</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;">
|
||||
${emailContent}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Set the content in the compose area with proper structure
|
||||
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 });
|
||||
|
||||
// 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');
|
||||
}
|
||||
const formatQuotedContent = (content: string) => {
|
||||
// First try to clean up any problematic formatting
|
||||
let cleanedContent = content;
|
||||
|
||||
// If content is very short or empty, add a clear message
|
||||
if (!cleanedContent || cleanedContent.trim().length < 10) {
|
||||
return `<div style="color: #888; font-style: italic;">Original message was empty or could not be loaded</div>`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[DEBUG] Error formatting email content:', error);
|
||||
emailContent = '<div style="color: #ef4444;">Error parsing original message content.</div>';
|
||||
|
||||
// If we're working with plain text, make it readable
|
||||
if (!cleanedContent.includes('<') || !cleanedContent.includes('>')) {
|
||||
return `<pre style="white-space: pre-wrap; word-break: break-word; font-family: ui-sans-serif, system-ui, sans-serif; margin: 0;">${cleanedContent}</pre>`;
|
||||
}
|
||||
|
||||
// For HTML content, ensure it's properly contained
|
||||
return cleanedContent;
|
||||
};
|
||||
|
||||
// Process email 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);
|
||||
};
|
||||
|
||||
const quotedContent = forwardFrom ? `
|
||||
<div class="forwarded-message" style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
||||
---------- Forwarded message ---------<br/>
|
||||
From: ${formatEmailAddresses(emailToProcess?.from) || 'Unknown Sender'}<br/>
|
||||
Date: ${new Date(emailToProcess?.date || Date.now()).toLocaleString()}<br/>
|
||||
Subject: ${emailToProcess?.subject || 'No Subject'}<br/>
|
||||
To: ${formatEmailAddresses(emailToProcess?.to) || ''}<br/>
|
||||
${emailToProcess?.cc ? `Cc: ${formatEmailAddresses(emailToProcess.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;">
|
||||
${formatQuotedContent(emailContent)}
|
||||
</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;">
|
||||
${formatQuotedContent(emailContent)}
|
||||
</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 });
|
||||
|
||||
// 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) {
|
||||
console.error('[DEBUG] Error initializing compose content:', error);
|
||||
@ -462,6 +515,27 @@ export default function ComposeEmail({
|
||||
}
|
||||
};
|
||||
|
||||
// Add focus handling for better UX
|
||||
const handleComposeAreaClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
// If the click is directly on the compose area and not on any child element
|
||||
if (e.target === e.currentTarget) {
|
||||
// Find the cursor position element
|
||||
const cursorPosition = e.currentTarget.querySelector('.cursor-position');
|
||||
if (cursorPosition) {
|
||||
// Focus the cursor position element
|
||||
(cursorPosition as HTMLElement).focus();
|
||||
|
||||
// Set cursor at the beginning
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.setStart(cursorPosition, 0);
|
||||
range.collapse(true);
|
||||
selection?.removeAllRanges();
|
||||
selection?.addRange(range);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!showCompose) return null;
|
||||
|
||||
return (
|
||||
@ -562,6 +636,7 @@ export default function ComposeEmail({
|
||||
ref={composeBodyRef}
|
||||
contentEditable="true"
|
||||
onInput={handleInput}
|
||||
onClick={handleComposeAreaClick}
|
||||
className="flex-1 w-full bg-white border border-gray-300 rounded-md p-4 text-black overflow-y-auto focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||
style={{
|
||||
direction: 'ltr',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user