From 5da40aa3aa1d9cac6ca8a392477df67f9849d7d1 Mon Sep 17 00:00:00 2001 From: alma Date: Fri, 25 Apr 2025 21:25:27 +0200 Subject: [PATCH] panel 2 courier api restore --- components/ComposeEmail.tsx | 325 +++++++++++++++++++----------------- 1 file changed, 174 insertions(+), 151 deletions(-) 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 = ` -
-

- ${quotedContent} -
- `; + // Set the content in the compose area with proper structure + formattedContent = ` +
+

+ ${quotedContent} +
+ `; - 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';