diff --git a/components/ComposeEmail.tsx b/components/ComposeEmail.tsx
index 41f07b65..97f1b126 100644
--- a/components/ComposeEmail.tsx
+++ b/components/ComposeEmail.tsx
@@ -100,20 +100,6 @@ export default function ComposeEmail({
} : 'null'
);
- if (!emailToProcess?.content) {
- console.error('[DEBUG] No email content found to process');
- composeBodyRef.current.innerHTML = `
-
-
-
Error: No original message content available.
-
- Please select the email again or try refreshing the page.
-
-
- `;
- return;
- }
-
// Set initial loading state
composeBodyRef.current.innerHTML = `
@@ -121,151 +107,187 @@ export default function ComposeEmail({
Loading original message...
`;
+
+ setIsLoading(true);
- console.log('[DEBUG] Sending content to parse-email API, length:', emailToProcess.content.length);
-
- // 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');
+ // Check if we have content
+ if (!emailToProcess?.content) {
+ console.error('[DEBUG] No email content found to process');
+
+ // Try to use body property if content is not available (for backward compatibility)
+ if (emailToProcess && 'body' in emailToProcess && emailToProcess.body) {
+ console.log('[DEBUG] Using body property as fallback for content');
+ emailToProcess.content = emailToProcess.body;
+ } else if (emailToProcess) {
+ 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')}`);
+
+ if (!response.ok) {
+ throw new Error(`Failed to fetch email content: ${response.status}`);
+ }
+
+ const fullContent = await response.json();
+
+ // Update the email content with the fetched full content
+ if (fullContent && fullContent.content) {
+ 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 = `
+
+
+
Error: No original message content available.
+
+ Please select the email again or try refreshing the page.
+
+
+ `;
+ setIsLoading(false);
+ return;
+ }
+ } else {
+ // No emailToProcess available
+ composeBodyRef.current.innerHTML = `
+
+
+
Error: No email selected for reply/forward.
+
+ `;
+ 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 = `${emailContent}`;
+ }
+ }
+
if (!emailContent) {
- console.warn('[DEBUG] No HTML or text content returned from parser');
+ console.warn('[DEBUG] No content available after parsing');
+ emailContent = 'No content available
';
}
// Format the reply/forward content
- const quotedContent = forwardFrom ? `
-
- ---------- Forwarded message ---------
- From: ${emailToProcess.from}
- Date: ${new Date(emailToProcess.date).toLocaleString()}
- Subject: ${emailToProcess.subject}
- To: ${emailToProcess.to}
- ${emailToProcess.cc ? `Cc: ${emailToProcess.cc}
` : ''}
-
-
- ${emailContent}
-
- ` : `
-
- On ${new Date(emailToProcess.date).toLocaleString()}, ${emailToProcess.from} wrote:
-
-
- ${emailContent}
-
- `;
+ 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 quotedContent = forwardFrom ? `
+
+ ---------- Forwarded message ---------
+ From: ${emailToProcess?.from || 'Unknown Sender'}
+ Date: ${new Date(emailToProcess?.date || Date.now()).toLocaleString()}
+ Subject: ${emailToProcess?.subject || 'No Subject'}
+ To: ${emailToProcess?.to || ''}
+ ${emailToProcess?.cc ? `Cc: ${emailToProcess.cc}
` : ''}
+
+
+ ${emailContent}
+
+ ` : `
+
+ On ${new Date(emailToProcess?.date || Date.now()).toLocaleString()}, ${emailToProcess?.from || 'Unknown Sender'} wrote:
+
+
+ ${emailContent}
+
+ `;
- // Set the content in the compose area with proper structure
- const formattedContent = `
-
- `;
+ // Set the content in the compose area with proper structure
+ formattedContent = `
+
+ `;
- if (composeBodyRef.current) {
- composeBodyRef.current.innerHTML = formattedContent;
+ 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();
- }
-
- // 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
+ // Place cursor at the beginning before the quoted content
const selection = window.getSelection();
- if (!selection) return;
-
- try {
- const range = document.caretRangeFromPoint(mouseEvent.clientX, mouseEvent.clientY);
- if (range) {
- selection.removeAllRanges();
- selection.addRange(range);
- } else {
- // 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);
+ 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();
}
- });
-
- // 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
- setComposeBody(formattedContent);
- setLocalContent(formattedContent);
- console.log('[DEBUG] Successfully set compose content with scrollable message area');
+ // After setting the HTML content, add event listeners for scrolling
+ const messageContents = composeBodyRef.current.querySelectorAll('.message-content');
+ messageContents.forEach(container => {
+ 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 = 'Error parsing original message content.
';
}
} catch (error) {
console.error('[DEBUG] Error initializing compose content:', error);
@@ -283,17 +305,18 @@ export default function ComposeEmail({
setComposeBody(errorContent);
setLocalContent(errorContent);
}
+ } finally {
+ setIsLoading(false);
}
};
initializeContent();
}
- }, [replyTo, forwardFrom]);
+ }, [replyTo, forwardFrom, setComposeBody]);
const handleInput = (e: React.FormEvent) => {
- if (!composeBodyRef.current) return;
-
- const content = composeBodyRef.current.innerHTML;
+ if (!e.currentTarget) return;
+ const content = e.currentTarget.innerHTML;
if (!content.trim()) {
setLocalContent('');
setComposeBody('');
@@ -307,7 +330,7 @@ export default function ComposeEmail({
}
// 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 => {
// Make sure the div remains scrollable after input events
(div as HTMLElement).style.maxHeight = '300px';