From cdd5bd98bc30b3f3edd27b85291070c3d00bd24b Mon Sep 17 00:00:00 2001 From: alma Date: Thu, 1 May 2025 15:49:25 +0200 Subject: [PATCH] courrier preview --- components/email/EmailDetailView.tsx | 53 ++++++++++++++++- hooks/use-email-state.ts | 89 +++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 6 deletions(-) diff --git a/components/email/EmailDetailView.tsx b/components/email/EmailDetailView.tsx index afb1ef28..4877c79a 100644 --- a/components/email/EmailDetailView.tsx +++ b/components/email/EmailDetailView.tsx @@ -45,11 +45,35 @@ export default function EmailDetailView({ // Render email content based on the email body const renderEmailContent = () => { try { - console.log('EmailDetailView renderEmailContent', { + // Enhanced debugging to trace exactly what's in the content + console.log('EmailDetailView renderEmailContent - DETAILED DEBUG', { + emailId: email.id, + subject: email.subject, hasContent: !!email.content, contentType: typeof email.content, + contentKeys: email.content && typeof email.content === 'object' ? Object.keys(email.content) : [], + contentStringLength: typeof email.content === 'string' ? email.content.length : 'N/A', + contentHtmlLength: email.content && typeof email.content === 'object' && 'html' in email.content && typeof (email.content as any).html === 'string' + ? ((email.content as any).html as string).length + : 0, + contentTextLength: email.content && typeof email.content === 'object' && 'text' in email.content && typeof (email.content as any).text === 'string' + ? ((email.content as any).text as string).length + : 0, + contentSample: typeof email.content === 'string' + ? email.content.substring(0, 100) + : (email.content && typeof email.content === 'object' && 'html' in email.content && typeof (email.content as any).html === 'string' + ? ((email.content as any).html as string).substring(0, 100) + : (email.content && typeof email.content === 'object' && 'text' in email.content && typeof (email.content as any).text === 'string' + ? ((email.content as any).text as string).substring(0, 100) + : 'N/A')), hasHtml: !!email.html, - hasText: !!email.text + htmlLength: email.html?.length || 0, + htmlSample: email.html?.substring(0, 100) || 'N/A', + hasText: !!email.text, + textLength: email.text?.length || 0, + textSample: email.text?.substring(0, 100) || 'N/A', + contentIsNull: email.content === null, + contentIsUndefined: email.content === undefined, }); // Determine what content to use and how to handle it @@ -59,15 +83,29 @@ export default function EmailDetailView({ // If content is a string, use it directly if (typeof email.content === 'string') { contentToUse = email.content; + console.log('Using email.content as string', contentToUse.substring(0, 50)); } // If content is an object with html/text properties else if (typeof email.content === 'object') { - contentToUse = email.content.html || email.content.text || ''; + const contentObj = email.content as {html?: string; text?: string}; + if (contentObj.html) { + contentToUse = contentObj.html; + console.log('Using email.content.html', contentToUse.substring(0, 50)); + } else if (contentObj.text) { + // Convert plain text to HTML + contentToUse = contentObj.text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/\n/g, '
'); + console.log('Using email.content.text (converted)', contentToUse.substring(0, 50)); + } } } // Fall back to html or text properties if content is not available else if (email.html) { contentToUse = email.html; + console.log('Using fallback email.html', contentToUse.substring(0, 50)); } else if (email.text) { // Convert plain text to HTML with line breaks @@ -76,6 +114,15 @@ export default function EmailDetailView({ .replace(//g, '>') .replace(/\n/g, '
'); + console.log('Using fallback email.text (converted)', contentToUse.substring(0, 50)); + } + + // Log if no content was found + if (!contentToUse) { + console.error('No renderable content found in email!', { + id: email.id, + subject: email.subject + }); } // Return content or fallback message diff --git a/hooks/use-email-state.ts b/hooks/use-email-state.ts index fa08e11c..846f6638 100644 --- a/hooks/use-email-state.ts +++ b/hooks/use-email-state.ts @@ -61,6 +61,84 @@ export const useEmailState = () => { } }, []); + // Normalize email content structure to ensure consistency + const normalizeEmailContent = useCallback((emailData: any): any => { + if (!emailData) return emailData; + + // Create a clone to avoid modifying the original + const normalizedEmail = { ...emailData }; + + // Log the incoming email structure + console.log(`[NORMALIZE_EMAIL] Processing email ${normalizedEmail.id || 'unknown'}: ${normalizedEmail.subject || 'No subject'}`); + + try { + // Handle content field normalization + if (!normalizedEmail.content) { + // Create content object if it doesn't exist + normalizedEmail.content = { html: '', text: '' }; + + // Try to populate content from html/text fields + if (normalizedEmail.html) { + normalizedEmail.content.html = normalizedEmail.html; + console.log(`[NORMALIZE_EMAIL] Populated content.html from email.html (${normalizedEmail.html.length} chars)`); + } + + if (normalizedEmail.text) { + normalizedEmail.content.text = normalizedEmail.text; + console.log(`[NORMALIZE_EMAIL] Populated content.text from email.text (${normalizedEmail.text.length} chars)`); + } + } + // If content is a string, convert to object format + else if (typeof normalizedEmail.content === 'string') { + const htmlContent = normalizedEmail.content; + normalizedEmail.content = { + html: htmlContent, + text: htmlContent.replace(/<[^>]*>/g, '') // Simple HTML to text conversion + }; + console.log(`[NORMALIZE_EMAIL] Converted string content to object (${htmlContent.length} chars)`); + } + // Ensure content object has both html and text properties + else if (typeof normalizedEmail.content === 'object') { + if (!normalizedEmail.content.html && normalizedEmail.content.text) { + // Convert text to simple HTML if only text exists + normalizedEmail.content.html = normalizedEmail.content.text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/\n/g, '
'); + console.log(`[NORMALIZE_EMAIL] Created HTML content from text (${normalizedEmail.content.text.length} chars)`); + } else if (!normalizedEmail.content.text && normalizedEmail.content.html) { + // Create plain text version if only HTML exists + normalizedEmail.content.text = normalizedEmail.content.html.replace(/<[^>]*>/g, ''); + console.log(`[NORMALIZE_EMAIL] Created text content from HTML (${normalizedEmail.content.html.length} chars)`); + } + } + + // Ensure html and text properties are also set for backward compatibility + if (normalizedEmail.content?.html && !normalizedEmail.html) { + normalizedEmail.html = normalizedEmail.content.html; + } + + if (normalizedEmail.content?.text && !normalizedEmail.text) { + normalizedEmail.text = normalizedEmail.content.text; + } + + console.log(`[NORMALIZE_EMAIL] Normalized email content structure successfully`, { + hasContentObj: !!normalizedEmail.content, + contentHtmlLength: normalizedEmail.content?.html?.length || 0, + contentTextLength: normalizedEmail.content?.text?.length || 0, + hasHtml: !!normalizedEmail.html, + hasText: !!normalizedEmail.text + }); + + return normalizedEmail; + } catch (error) { + console.error(`[NORMALIZE_EMAIL] Error normalizing email content:`, error); + // Return the original data if normalization fails + return emailData; + } + }, []); + // Load emails from the server const loadEmails = useCallback(async (page: number, perPage: number, isLoadMore: boolean = false) => { // CRITICAL FIX: Do important validation before setting loading state @@ -355,9 +433,11 @@ export const useEmailState = () => { if (existingEmail && existingEmail.contentFetched) { // Use the existing email if it has content already + // ENHANCEMENT: Apply content normalization before selecting the email + const normalizedEmail = normalizeEmailContent(existingEmail); dispatch({ type: 'SELECT_EMAIL', - payload: { emailId, accountId, folder, email: existingEmail } + payload: { emailId, accountId, folder, email: normalizedEmail } }); // Mark as read if not already @@ -386,10 +466,13 @@ export const useEmailState = () => { // Mark the email as read on the server markEmailAsRead(emailId, true, effectiveAccountId); + // ENHANCEMENT: Apply content normalization before selecting the email + const normalizedEmailData = normalizeEmailContent(emailData); + // Select the email dispatch({ type: 'SELECT_EMAIL', - payload: { emailId, accountId: effectiveAccountId, folder, email: emailData } + payload: { emailId, accountId: effectiveAccountId, folder, email: normalizedEmailData } }); } catch (error) { logEmailOp('ERROR', `Failed to select email: ${error instanceof Error ? error.message : String(error)}`); @@ -400,7 +483,7 @@ export const useEmailState = () => { } finally { dispatch({ type: 'SET_LOADING', payload: false }); } - }, [state.emails, logEmailOp]); + }, [state.emails, logEmailOp, normalizeEmailContent]); // Toggle email selection for multi-select const toggleEmailSelection = useCallback((emailId: string) => {