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) => {