panel 2 courier api restore
This commit is contained in:
parent
73c149069c
commit
5da40aa3aa
@ -100,20 +100,6 @@ export default function ComposeEmail({
|
|||||||
} : 'null'
|
} : 'null'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!emailToProcess?.content) {
|
|
||||||
console.error('[DEBUG] No email content found to process');
|
|
||||||
composeBodyRef.current.innerHTML = `
|
|
||||||
<div class="compose-area" contenteditable="true">
|
|
||||||
<br/>
|
|
||||||
<div style="color: #ef4444;">Error: No original message content available.</div>
|
|
||||||
<div style="color: #64748b; font-size: 0.875rem; margin-top: 0.5rem;">
|
|
||||||
Please select the email again or try refreshing the page.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set initial loading state
|
// Set initial loading state
|
||||||
composeBodyRef.current.innerHTML = `
|
composeBodyRef.current.innerHTML = `
|
||||||
<div class="compose-area" contenteditable="true">
|
<div class="compose-area" contenteditable="true">
|
||||||
@ -121,151 +107,187 @@ export default function ComposeEmail({
|
|||||||
<div class="text-gray-500">Loading original message...</div>
|
<div class="text-gray-500">Loading original message...</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
console.log('[DEBUG] Sending content to parse-email API, length:', emailToProcess.content.length);
|
// Check if we have content
|
||||||
|
if (!emailToProcess?.content) {
|
||||||
// Parse the original email using the API
|
console.error('[DEBUG] No email content found to process');
|
||||||
const response = await fetch('/api/parse-email', {
|
|
||||||
method: 'POST',
|
// Try to use body property if content is not available (for backward compatibility)
|
||||||
headers: {
|
if (emailToProcess && 'body' in emailToProcess && emailToProcess.body) {
|
||||||
'Content-Type': 'application/json',
|
console.log('[DEBUG] Using body property as fallback for content');
|
||||||
},
|
emailToProcess.content = emailToProcess.body;
|
||||||
body: JSON.stringify({ email: emailToProcess.content }),
|
} else if (emailToProcess) {
|
||||||
});
|
console.log('[DEBUG] Attempting to fetch email content directly');
|
||||||
|
try {
|
||||||
console.log('[DEBUG] Parse-email API response status:', response.status);
|
// Fetch the email content if not available
|
||||||
|
const response = await fetch(`/api/courrier/${emailToProcess.id}?folder=${encodeURIComponent(emailToProcess.folder || 'INBOX')}`);
|
||||||
const data = await response.json();
|
|
||||||
console.log('[DEBUG] Parse-email API response:', {
|
if (!response.ok) {
|
||||||
hasHtml: !!data.html,
|
throw new Error(`Failed to fetch email content: ${response.status}`);
|
||||||
hasText: !!data.text,
|
}
|
||||||
subject: data.subject,
|
|
||||||
error: data.error
|
const fullContent = await response.json();
|
||||||
});
|
|
||||||
|
// Update the email content with the fetched full content
|
||||||
if (!response.ok) {
|
if (fullContent && fullContent.content) {
|
||||||
throw new Error(data.error || 'Failed to parse email');
|
console.log('[DEBUG] Successfully fetched content for reply/forward');
|
||||||
|
emailToProcess.content = fullContent.content;
|
||||||
|
} else {
|
||||||
|
throw new Error('No content in fetched email');
|
||||||
|
}
|
||||||
|
} catch (fetchError) {
|
||||||
|
console.error('[DEBUG] Error fetching email content:', fetchError);
|
||||||
|
composeBodyRef.current.innerHTML = `
|
||||||
|
<div class="compose-area" contenteditable="true">
|
||||||
|
<br/>
|
||||||
|
<div style="color: #ef4444;">Error: No original message content available.</div>
|
||||||
|
<div style="color: #64748b; font-size: 0.875rem; margin-top: 0.5rem;">
|
||||||
|
Please select the email again or try refreshing the page.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
setIsLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No emailToProcess available
|
||||||
|
composeBodyRef.current.innerHTML = `
|
||||||
|
<div class="compose-area" contenteditable="true">
|
||||||
|
<br/>
|
||||||
|
<div style="color: #ef4444;">Error: No email selected for reply/forward.</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
setIsLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const emailContent = data.html || data.text || '';
|
console.log('[DEBUG] Sending content to parse-email API, length:', emailToProcess!.content.length);
|
||||||
|
|
||||||
|
let emailContent;
|
||||||
|
let parseSuccess = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Parse the original email using the API
|
||||||
|
const response = await fetch('/api/parse-email', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ email: emailToProcess.content }),
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[DEBUG] Parse-email API response status:', response.status);
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('[DEBUG] Parse-email API response:', {
|
||||||
|
hasHtml: !!data.html,
|
||||||
|
hasText: !!data.text,
|
||||||
|
subject: data.subject,
|
||||||
|
error: data.error
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error || 'Failed to parse email');
|
||||||
|
}
|
||||||
|
|
||||||
|
emailContent = data.html || data.text || '';
|
||||||
|
parseSuccess = true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[DEBUG] API parse error:', error);
|
||||||
|
// Try to use the content directly if API fails
|
||||||
|
emailContent = emailToProcess.content;
|
||||||
|
|
||||||
|
// If content looks like HTML, use it directly, otherwise wrap in pre tags
|
||||||
|
if (!emailContent.startsWith('<') || !emailContent.endsWith('>')) {
|
||||||
|
emailContent = `<pre>${emailContent}</pre>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!emailContent) {
|
if (!emailContent) {
|
||||||
console.warn('[DEBUG] No HTML or text content returned from parser');
|
console.warn('[DEBUG] No content available after parsing');
|
||||||
|
emailContent = '<div>No content available</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format the reply/forward content
|
// Format the reply/forward content
|
||||||
const quotedContent = forwardFrom ? `
|
const contentLength = emailToProcess && emailToProcess.content ? emailToProcess.content.length : 0;
|
||||||
<div class="forwarded-message" style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
console.log('[DEBUG] Sending content to parse-email API, length:', contentLength);
|
||||||
---------- Forwarded message ---------<br/>
|
|
||||||
From: ${emailToProcess.from}<br/>
|
let formattedContent;
|
||||||
Date: ${new Date(emailToProcess.date).toLocaleString()}<br/>
|
try {
|
||||||
Subject: ${emailToProcess.subject}<br/>
|
if (emailToProcess && emailToProcess.content) {
|
||||||
To: ${emailToProcess.to}<br/>
|
// Process email content
|
||||||
${emailToProcess.cc ? `Cc: ${emailToProcess.cc}<br/>` : ''}
|
const quotedContent = forwardFrom ? `
|
||||||
</div>
|
<div class="forwarded-message" style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
||||||
<div class="message-content" style="margin-top: 10px; color: #374151; max-height: 300px; overflow-y: auto; border: 1px solid #e5e7eb; padding: 10px; border-radius: 4px;">
|
---------- Forwarded message ---------<br/>
|
||||||
${emailContent}
|
From: ${emailToProcess?.from || 'Unknown Sender'}<br/>
|
||||||
</div>
|
Date: ${new Date(emailToProcess?.date || Date.now()).toLocaleString()}<br/>
|
||||||
` : `
|
Subject: ${emailToProcess?.subject || 'No Subject'}<br/>
|
||||||
<div class="quoted-message" style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
To: ${emailToProcess?.to || ''}<br/>
|
||||||
On ${new Date(emailToProcess.date).toLocaleString()}, ${emailToProcess.from} wrote:
|
${emailToProcess?.cc ? `Cc: ${emailToProcess.cc}<br/>` : ''}
|
||||||
</div>
|
</div>
|
||||||
<div class="message-content" style="margin: 10px 0 0 10px; padding-left: 1em; border-left: 2px solid #e5e7eb; color: #374151; max-height: 300px; overflow-y: auto;">
|
<div class="message-content" style="margin-top: 10px; color: #374151; max-height: 300px; overflow-y: auto; border: 1px solid #e5e7eb; padding: 10px; border-radius: 4px;">
|
||||||
${emailContent}
|
${emailContent}
|
||||||
</div>
|
</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()}, ${emailToProcess?.from || 'Unknown Sender'} wrote:
|
||||||
|
</div>
|
||||||
|
<div class="message-content" style="margin: 10px 0 0 10px; padding-left: 1em; border-left: 2px solid #e5e7eb; color: #374151; max-height: 300px; overflow-y: auto;">
|
||||||
|
${emailContent}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
// Set the content in the compose area with proper structure
|
// Set the content in the compose area with proper structure
|
||||||
const formattedContent = `
|
formattedContent = `
|
||||||
<div class="compose-area" contenteditable="true" style="min-height: 100px; padding: 10px;">
|
<div class="compose-area" contenteditable="true" style="min-height: 100px; padding: 10px;">
|
||||||
<div class="cursor-position" style="min-height: 20px;"><br/></div>
|
<div class="cursor-position" style="min-height: 20px;"><br/></div>
|
||||||
${quotedContent}
|
${quotedContent}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (composeBodyRef.current) {
|
if (composeBodyRef.current) {
|
||||||
composeBodyRef.current.innerHTML = formattedContent;
|
composeBodyRef.current.innerHTML = formattedContent;
|
||||||
|
|
||||||
// Place cursor at the beginning before the quoted content
|
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add event listeners to handle scrolling within message-content zone
|
|
||||||
const messageContentDivs = composeBodyRef.current.querySelectorAll('.message-content');
|
|
||||||
messageContentDivs.forEach(div => {
|
|
||||||
// Change cursor to text when hovering over the message content
|
|
||||||
div.addEventListener('mouseenter', () => {
|
|
||||||
(div as HTMLElement).style.outline = '1px solid #d1d5db';
|
|
||||||
(div as HTMLElement).style.cursor = 'text';
|
|
||||||
});
|
|
||||||
|
|
||||||
div.addEventListener('mouseleave', () => {
|
|
||||||
(div as HTMLElement).style.outline = 'none';
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make sure clicking inside positions cursor correctly
|
|
||||||
div.addEventListener('click', (e: Event) => {
|
|
||||||
const mouseEvent = e as MouseEvent;
|
|
||||||
mouseEvent.stopPropagation();
|
|
||||||
|
|
||||||
// Get precise click position for cursor placement
|
|
||||||
const selection = window.getSelection();
|
const selection = window.getSelection();
|
||||||
if (!selection) return;
|
const range = document.createRange();
|
||||||
|
const firstDiv = composeBodyRef.current.querySelector('.cursor-position');
|
||||||
try {
|
if (firstDiv) {
|
||||||
const range = document.caretRangeFromPoint(mouseEvent.clientX, mouseEvent.clientY);
|
range.setStart(firstDiv, 0);
|
||||||
if (range) {
|
range.collapse(true);
|
||||||
selection.removeAllRanges();
|
selection?.removeAllRanges();
|
||||||
selection.addRange(range);
|
selection?.addRange(range);
|
||||||
} else {
|
(firstDiv as HTMLElement).focus();
|
||||||
// Fallback if caretRangeFromPoint is not available
|
|
||||||
const newRange = document.createRange();
|
|
||||||
if (e.target instanceof Node) {
|
|
||||||
// Try to place at exact position
|
|
||||||
if (e.target.nodeType === Node.TEXT_NODE) {
|
|
||||||
const offset = Math.floor((e.target as Text).length / 2);
|
|
||||||
newRange.setStart(e.target, offset);
|
|
||||||
} else {
|
|
||||||
newRange.selectNodeContents(e.target);
|
|
||||||
}
|
|
||||||
newRange.collapse(true);
|
|
||||||
selection.removeAllRanges();
|
|
||||||
selection.addRange(newRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error positioning cursor:", err);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Handle wheel events to scroll within the zone
|
|
||||||
div.addEventListener('wheel', (e: Event) => {
|
|
||||||
const wheelEvent = e as WheelEvent;
|
|
||||||
const target = wheelEvent.currentTarget as HTMLElement;
|
|
||||||
const scrollAmount = wheelEvent.deltaY;
|
|
||||||
|
|
||||||
// Adjust scroll position
|
|
||||||
target.scrollTop += scrollAmount;
|
|
||||||
|
|
||||||
// Prevent page scroll when scrolling inside the message content
|
|
||||||
if ((target.scrollTop > 0 && scrollAmount > 0) ||
|
|
||||||
(target.scrollTop < target.scrollHeight - target.clientHeight && scrollAmount < 0)) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update compose state
|
// After setting the HTML content, add event listeners for scrolling
|
||||||
setComposeBody(formattedContent);
|
const messageContents = composeBodyRef.current.querySelectorAll('.message-content');
|
||||||
setLocalContent(formattedContent);
|
messageContents.forEach(container => {
|
||||||
console.log('[DEBUG] Successfully set compose content with scrollable message area');
|
container.addEventListener('wheel', (e: Event) => {
|
||||||
|
const wheelEvent = e as WheelEvent;
|
||||||
|
const target = e.currentTarget as HTMLElement;
|
||||||
|
const isAtBottom = target.scrollHeight - target.scrollTop <= target.clientHeight + 1;
|
||||||
|
const isAtTop = target.scrollTop <= 0;
|
||||||
|
|
||||||
|
// Let the container handle scrolling only if not at boundaries
|
||||||
|
if ((wheelEvent.deltaY > 0 && !isAtBottom) || (wheelEvent.deltaY < 0 && !isAtTop)) {
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update compose state
|
||||||
|
setComposeBody(formattedContent);
|
||||||
|
setLocalContent(formattedContent);
|
||||||
|
console.log('[DEBUG] Successfully set compose content with scrollable message area');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[DEBUG] Error formatting email content:', error);
|
||||||
|
emailContent = '<div style="color: #ef4444;">Error parsing original message content.</div>';
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[DEBUG] Error initializing compose content:', error);
|
console.error('[DEBUG] Error initializing compose content:', error);
|
||||||
@ -283,17 +305,18 @@ export default function ComposeEmail({
|
|||||||
setComposeBody(errorContent);
|
setComposeBody(errorContent);
|
||||||
setLocalContent(errorContent);
|
setLocalContent(errorContent);
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
initializeContent();
|
initializeContent();
|
||||||
}
|
}
|
||||||
}, [replyTo, forwardFrom]);
|
}, [replyTo, forwardFrom, setComposeBody]);
|
||||||
|
|
||||||
const handleInput = (e: React.FormEvent<HTMLDivElement>) => {
|
const handleInput = (e: React.FormEvent<HTMLDivElement>) => {
|
||||||
if (!composeBodyRef.current) return;
|
if (!e.currentTarget) return;
|
||||||
|
const content = e.currentTarget.innerHTML;
|
||||||
const content = composeBodyRef.current.innerHTML;
|
|
||||||
if (!content.trim()) {
|
if (!content.trim()) {
|
||||||
setLocalContent('');
|
setLocalContent('');
|
||||||
setComposeBody('');
|
setComposeBody('');
|
||||||
@ -307,7 +330,7 @@ export default function ComposeEmail({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure scrolling and cursor behavior works after edits
|
// Ensure scrolling and cursor behavior works after edits
|
||||||
const messageContentDivs = composeBodyRef.current.querySelectorAll('.message-content');
|
const messageContentDivs = e.currentTarget.querySelectorAll('.message-content');
|
||||||
messageContentDivs.forEach(div => {
|
messageContentDivs.forEach(div => {
|
||||||
// Make sure the div remains scrollable after input events
|
// Make sure the div remains scrollable after input events
|
||||||
(div as HTMLElement).style.maxHeight = '300px';
|
(div as HTMLElement).style.maxHeight = '300px';
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user