From 21accd5224047d060843eabe51cb5aa7fa08ed05 Mon Sep 17 00:00:00 2001 From: alma Date: Wed, 30 Apr 2025 12:57:32 +0200 Subject: [PATCH] courrier multi account restore compose --- app/api/courrier/route.ts | 70 +++++------ app/courrier/page.tsx | 81 ++++++------ hooks/use-courrier.ts | 253 ++++++++++++++++++++++---------------- 3 files changed, 214 insertions(+), 190 deletions(-) diff --git a/app/api/courrier/route.ts b/app/api/courrier/route.ts index 4d73ef0b..e51b3d3e 100644 --- a/app/api/courrier/route.ts +++ b/app/api/courrier/route.ts @@ -40,69 +40,63 @@ export async function GET(request: Request) { const searchQuery = searchParams.get("search") || ""; const accountId = searchParams.get("accountId") || ""; - // Extract account ID from folder name if present and none was explicitly provided - const folderAccountId = folder.includes(':') ? folder.split(':')[0] : ""; + // CRITICAL FIX: Log exact parameters received by the API + console.log(`[API] Received request with: folder=${folder}, accountId=${accountId}, page=${page}`); - // Use the most specific account ID available - // First from the folder, then from the explicit parameter, then default - let effectiveAccountId = folderAccountId || accountId || 'default'; + // CRITICAL FIX: More robust parameter normalization + // 1. If folder contains an account prefix, extract it but DO NOT use it + // 2. Always prioritize the explicit accountId parameter + let normalizedFolder = folder; + let effectiveAccountId = accountId || 'default'; - // CRITICAL FIX: If effectiveAccountId is still 'default', try to find the first account for the user - if (effectiveAccountId === 'default') { - try { - const accounts = await prisma.mailCredentials.findMany({ - where: { userId: session.user.id }, - orderBy: { createdAt: 'asc' }, - take: 1, - select: { id: true } - }); - - if (accounts && accounts.length > 0) { - effectiveAccountId = accounts[0].id; - console.log(`No specific account provided, using first available account: ${effectiveAccountId}`); - } - } catch (error) { - console.error("Error finding default account:", error); - // Continue with 'default' if there's an error - } + if (folder.includes(':')) { + const parts = folder.split(':'); + const folderAccountId = parts[0]; + normalizedFolder = parts[1]; + + console.log(`[API] Folder has prefix (${folderAccountId}), normalized to ${normalizedFolder}`); + // We intentionally DO NOT use folderAccountId here - the explicit accountId parameter takes precedence } - // Normalize folder name by removing account prefix if present - const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder; - - // Log the request details for debugging - console.log(`Email request: user=${session.user.id}, folder=${normalizedFolder}, account=${effectiveAccountId}, page=${page}`); + // CRITICAL FIX: Enhanced logging for parameter resolution + console.log(`[API] Using normalized parameters: folder=${normalizedFolder}, accountId=${effectiveAccountId}`); // Try to get from Redis cache first, but only if it's not a search query if (!searchQuery) { + // CRITICAL FIX: Use consistent cache key format with the correct account ID + console.log(`[API] Checking Redis cache for ${session.user.id}:${effectiveAccountId}:${normalizedFolder}:${page}:${perPage}`); const cachedEmails = await getCachedEmailList( session.user.id, - effectiveAccountId, - normalizedFolder, + effectiveAccountId, // Use effective account ID for consistent cache key + normalizedFolder, // Use normalized folder name without prefix page, perPage ); if (cachedEmails) { - console.log(`Using Redis cached emails for ${session.user.id}:${effectiveAccountId}:${normalizedFolder}:${page}:${perPage}`); + console.log(`[API] Using Redis cached emails for ${session.user.id}:${effectiveAccountId}:${normalizedFolder}:${page}:${perPage}`); return NextResponse.json(cachedEmails); } } - console.log(`Redis cache miss for ${session.user.id}:${effectiveAccountId}:${normalizedFolder}:${page}:${perPage}, fetching emails from IMAP`); + console.log(`[API] Redis cache miss for ${session.user.id}:${effectiveAccountId}:${normalizedFolder}:${page}:${perPage}, fetching emails from IMAP`); // Use the email service to fetch emails with the normalized folder and effective account ID + // CRITICAL FIX: Pass parameters in the correct order and with proper values const emailsResult = await getEmails( - session.user.id, - normalizedFolder, - page, - perPage, - effectiveAccountId + session.user.id, // userId + normalizedFolder, // folder (without prefix) + page, // page + perPage, // perPage + effectiveAccountId // accountId ); + // CRITICAL FIX: Log when emails are returned from IMAP + console.log(`[API] Successfully fetched ${emailsResult.emails.length} emails from IMAP for account ${effectiveAccountId}`); + // The result is already cached in the getEmails function return NextResponse.json(emailsResult); } catch (error: any) { - console.error("Error fetching emails:", error); + console.error("[API] Error fetching emails:", error); return NextResponse.json( { error: "Failed to fetch emails", message: error.message }, { status: 500 } diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index 8900439a..6e9a9ae1 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -610,11 +610,16 @@ export default function CourrierPage() { // Update handleMailboxChange to ensure consistent folder naming and prevent race conditions const handleMailboxChange = (folder: string, accountId?: string) => { // Enhanced logging to trace the flow - logEmailOp('MAILBOX-CHANGE-V4', `Starting mailbox change to folder=${folder}, accountId=${accountId || 'undefined'}`); + logEmailOp('MAILBOX-CHANGE', `Starting mailbox change to folder=${folder}, accountId=${accountId || 'undefined'}`); + // CRITICAL FIX: Account ID is REQUIRED for proper folder switching if (!accountId || accountId === 'loading-account') { - logEmailOp('MAILBOX-CHANGE-V4', 'No valid accountId provided, using default flow', { folder }); - changeFolder(folder); + logEmailOp('MAILBOX-CHANGE', 'ERROR: No valid accountId provided, cannot switch folders reliably', { folder }); + toast({ + title: "Cannot switch folders", + description: "A valid account ID is required to switch folders", + variant: "destructive", + }); return; } @@ -624,9 +629,10 @@ export default function CourrierPage() { // Clear emails during transition to avoid UI flicker/confusion setEmails([]); + // Find the account to ensure it exists const account = accounts.find(a => a.id.toString() === accountId.toString()); if (!account) { - logEmailOp('MAILBOX-CHANGE-V4', `ERROR: Account not found: ${accountId}`); + logEmailOp('MAILBOX-CHANGE', `ERROR: Account not found: ${accountId}`); toast({ title: "Account not found", description: `The account ${accountId} could not be found.`, @@ -636,75 +642,60 @@ export default function CourrierPage() { return; } - logEmailOp('MAILBOX-CHANGE-V4', `Found account: ${account.email}`, { - folderCount: account.folders?.length || 0, - folder: folder + logEmailOp('MAILBOX-CHANGE', `Account found: ${account.email}`, { + accountId: account.id }); - // Extract the base folder name if it already has an account prefix - let baseFolder = folder; - let folderAccountId = null; + // CRITICAL FIX: Simplify and clarify parameter handling + // Extract base folder name and ensure we have a clean normalized folder + let baseFolder: string; if (folder.includes(':')) { const parts = folder.split(':'); - folderAccountId = parts[0]; + const folderAccountId = parts[0]; baseFolder = parts[1]; - logEmailOp('MAILBOX-CHANGE-V4', `Parsed folder: accountId=${folderAccountId}, baseFolder=${baseFolder}`); + logEmailOp('MAILBOX-CHANGE', `Parsed folder: accountId=${folderAccountId}, baseFolder=${baseFolder}`); - // CRITICAL FIX: If the folder has an account prefix that doesn't match the requested account, - // log a warning and ALWAYS use the requested accountId - if (folderAccountId !== accountId.toString()) { - logEmailOp('MAILBOX-CHANGE-V4', `WARNING: Folder prefix mismatch - FIXING`, { - folderAccount: folderAccountId, - requestedAccount: accountId, - originalFolder: folder, - baseFolder: baseFolder - }); - // Force the correct account ID to be used - folderAccountId = accountId.toString(); + // CRITICAL FIX: If folder has a different account prefix than requested, log warning + if (folderAccountId !== accountId) { + logEmailOp('MAILBOX-CHANGE', `WARNING: Folder prefix (${folderAccountId}) doesn't match requested account (${accountId})`); } + } else { + baseFolder = folder; + logEmailOp('MAILBOX-CHANGE', `No folder prefix, using baseFolder=${baseFolder}`); } - // ALWAYS create a consistent folder name with REQUESTED account prefix + // CRITICAL FIX: ALWAYS create consistent folder name with REQUESTED account prefix const prefixedFolder = `${accountId}:${baseFolder}`; - logEmailOp('MAILBOX-CHANGE-V4', `Normalized folder name: ${prefixedFolder}`); + logEmailOp('MAILBOX-CHANGE', `Standardized folder: ${prefixedFolder}`); - // Check if we're already on this folder to avoid unnecessary refreshes - if (currentFolder === prefixedFolder) { - logEmailOp('MAILBOX-CHANGE-V4', `Already on folder ${prefixedFolder}, skipping change`); - setLoading(false); - return; - } - - // CRITICAL FIX: Lock in the selected folder BEFORE changing it in the system - // This prevents any race conditions where the folder gets reset to INBOX + // CRITICAL FIX: Update the selected account BEFORE changing folders + // This prevents potential race conditions during account switching setSelectedAccount(account); + // CRITICAL FIX: Update selected folder map with the new account's folder + // This ensures UI consistency with the selected folder setSelectedFolders(prev => { const updated = { ...prev, [accountId]: prefixedFolder }; - logEmailOp('MAILBOX-CHANGE-V4', `Updated selected folders map`, updated); + logEmailOp('MAILBOX-CHANGE', `Updated selected folders map`, updated); return updated; }); - // EXTRA SAFETY: Log exactly what we're passing to changeFolder - logEmailOp('MAILBOX-CHANGE-V4', `CALLING changeFolder with EXACT PARAMS:`, { - folder: prefixedFolder, - accountId: accountId, - folderType: typeof prefixedFolder, - accountIdType: typeof accountId - }); + // CRITICAL FIX: Log the EXACT parameters being passed to changeFolder + logEmailOp('MAILBOX-CHANGE', `Calling changeFolder with: folder=${prefixedFolder}, accountId=${accountId}`); - // Now use the changeFolder function from the hook with our properly formatted folder name + // CRITICAL FIX: Call changeFolder with both parameters explicitly + // The accountId parameter is critical for successful account switching changeFolder(prefixedFolder, accountId) .then(() => { - logEmailOp('MAILBOX-CHANGE-V4', `Successfully changed to folder ${prefixedFolder}`); + logEmailOp('MAILBOX-CHANGE', `Successfully changed to folder ${prefixedFolder} for account ${accountId}`); }) .catch(error => { - logEmailOp('MAILBOX-CHANGE-V4', `ERROR: Failed to change to folder ${prefixedFolder}`, { error }); + logEmailOp('MAILBOX-CHANGE', `ERROR: Failed to change to folder ${prefixedFolder}`, { error }); toast({ title: "Error changing folders", description: `Failed to change to folder ${baseFolder}. ${error instanceof Error ? error.message : String(error)}`, diff --git a/hooks/use-courrier.ts b/hooks/use-courrier.ts index 8ccc5b5b..929fbddf 100644 --- a/hooks/use-courrier.ts +++ b/hooks/use-courrier.ts @@ -91,32 +91,39 @@ export const useCourrier = () => { setError(null); try { - // CRITICAL FIX: Extract normalized folder name for API requests - // The currentFolder should already have the account prefix in format "accountId:folder" - // We need to extract the base folder name for the API request + // CRITICAL FIX: When an account ID is explicitly provided, ALWAYS prioritize it + // over any account ID that might be extracted from the currentFolder let normalizedFolder = currentFolder; - - // CRITICAL FIX: Handle account ID determination more robustly - // If specific account ID is provided, always use it - // Otherwise extract from currentFolder if possible let effectiveAccountId: string; - if (accountId) { - // Use explicitly provided accountId - effectiveAccountId = accountId; - } else if (currentFolder.includes(':')) { - // Extract from folder format + // Start by logging exactly what parameters we received + console.log(`[loadEmails] Called with isLoadMore=${isLoadMore}, accountId=${accountId || 'undefined'}, currentFolder=${currentFolder}`); + + if (currentFolder.includes(':')) { const parts = currentFolder.split(':'); - effectiveAccountId = parts[0]; - normalizedFolder = parts[1]; + const folderAccountId = parts[0]; + const baseFolderName = parts[1]; + + // CRITICAL FIX: If an account ID is explicitly provided, use it with highest priority + // This ensures account switching always works correctly + if (accountId) { + console.log(`[loadEmails] Explicit accountId provided (${accountId}), overriding folder prefix (${folderAccountId})`); + effectiveAccountId = accountId; + normalizedFolder = baseFolderName; + } else { + // No explicit account ID, use the one from the folder + effectiveAccountId = folderAccountId; + normalizedFolder = baseFolderName; + } } else { - // Default case - effectiveAccountId = 'default'; + // Folder doesn't have a prefix + normalizedFolder = currentFolder; + effectiveAccountId = accountId || 'default'; } - console.log(`Load emails - using normalized folder: ${normalizedFolder}, effectiveAccountId: ${effectiveAccountId}`); + console.log(`[loadEmails] Using normalized folder: ${normalizedFolder}, effectiveAccountId: ${effectiveAccountId}`); - // Construct query parameters + // Construct query parameters with normalized values const queryParams = new URLSearchParams({ folder: normalizedFolder, // Use normalized folder without account prefix for API page: page.toString(), @@ -130,14 +137,16 @@ export const useCourrier = () => { // Always add accountId to query params queryParams.set('accountId', effectiveAccountId); + // Log the exact query parameters being used + console.log(`[loadEmails] Query parameters: ${queryParams.toString()}`); + // Try to get cached emails first const currentRequestPage = page; - // CRITICAL FIX: Use the correct cache key format - // We pass the full prefixed folder to ensure proper cache key consistency + // CRITICAL FIX: Use consistent cache key format with explicit account ID const folderForCache = `${effectiveAccountId}:${normalizedFolder}`; - console.log(`Getting cached emails for user ${session.user.id}, folder ${folderForCache}, page ${currentRequestPage}, accountId ${effectiveAccountId}`); + console.log(`[loadEmails] Checking cache with key: ${folderForCache}, accountId=${effectiveAccountId}`); const cachedEmails = await getCachedEmailsWithTimeout( session.user.id, // userId: string folderForCache, // folder: string - use consistently prefixed folder for cache key @@ -307,7 +316,8 @@ export const useCourrier = () => { // Change folder and load emails from that folder const changeFolder = useCallback(async (folder: string, accountId?: string) => { - console.log(`Changing folder to ${folder} for account ${accountId || 'default'}`); + console.log(`[changeFolder] Called with folder=${folder}, accountId=${accountId || 'default'}`); + try { // Reset selected email and selection state immediately to avoid race conditions setSelectedEmail(null); @@ -315,22 +325,38 @@ export const useCourrier = () => { setEmails([]); // Clear existing emails right away setIsLoading(true); // Show loading state immediately - // CRITICAL FIX: Extract account ID from folder name if present and none was explicitly provided - const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId; + // CRITICAL FIX: Clear and precise parameter handling with detailed logging + let normalizedFolder: string; + let effectiveAccountId: string; + let prefixedFolder: string; - // Use the most specific account ID available - const effectiveAccountId = folderAccountId || accountId || 'default'; + // Parse input folder parameter + if (folder.includes(':')) { + // Folder has a prefix, extract components + const parts = folder.split(':'); + const folderAccountId = parts[0]; + normalizedFolder = parts[1]; + + // CRITICAL FIX: If an explicit accountId is provided, it ALWAYS takes precedence + // This is the key to fixing account switching issues + if (accountId) { + console.log(`[changeFolder] Explicit accountId (${accountId}) overrides folder prefix (${folderAccountId})`); + effectiveAccountId = accountId; + } else { + console.log(`[changeFolder] Using account ID from folder prefix: ${folderAccountId}`); + effectiveAccountId = folderAccountId; + } + } else { + // Folder has no prefix + normalizedFolder = folder; + effectiveAccountId = accountId || 'default'; + console.log(`[changeFolder] No folder prefix, using accountId=${effectiveAccountId}, folder=${normalizedFolder}`); + } - // Normalize folder name by removing account prefix if present - const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder; + // CRITICAL FIX: Always create a consistently formatted folder name with the EFFECTIVE account prefix + prefixedFolder = `${effectiveAccountId}:${normalizedFolder}`; - // Always create a consistently formatted folder name with account prefix - const prefixedFolder = `${effectiveAccountId}:${normalizedFolder}`; - - console.log(`Folder change: original=${folder}, normalized=${normalizedFolder}, accountId=${effectiveAccountId}, prefixed=${prefixedFolder}`); - - // CRITICAL FIX: Store the current account ID in state for all subsequent operations - // This ensures operations like markAsRead use the correct account context + console.log(`[changeFolder] Normalized parameters: folder=${normalizedFolder}, accountId=${effectiveAccountId}, prefixedFolder=${prefixedFolder}`); // Reset search query when changing folders setSearchQuery(''); @@ -338,24 +364,29 @@ export const useCourrier = () => { // Reset to page 1 setPage(1); - // CRITICAL FIX: We set the currentFolder state AFTER we have prepared all parameters - // This ensures any effects or functions triggered by currentFolder change have the correct context + // CRITICAL FIX: Set currentFolder state and verify it was set correctly + console.log(`[changeFolder] Setting currentFolder to: ${prefixedFolder}`); setCurrentFolder(prefixedFolder); - // Use a small delay to ensure state updates have propagated - // This helps prevent race conditions when multiple folders are clicked quickly - await new Promise(resolve => setTimeout(resolve, 100)); + // CRITICAL FIX: Use a small delay to ensure state updates have propagated + // This helps prevent race conditions during account switching + await new Promise(resolve => setTimeout(resolve, 200)); - // CRITICAL FIX: Wait for the loadEmails operation to complete before considering the folder change done - // This prevents multiple concurrent folder changes from interfering with each other + // CRITICAL FIX: Double-check the folder after state update to ensure consistency + console.log(`[changeFolder] After state update, currentFolder=${currentFolder}, loading emails with explicit accountId=${effectiveAccountId}`); + + // CRITICAL FIX: Always pass the effective account ID explicitly to loadEmails + // This ensures account context is maintained even if currentFolder hasn't updated yet await loadEmails(false, effectiveAccountId); + + console.log(`[changeFolder] Finished changing to folder=${prefixedFolder}`); } catch (error) { - console.error(`Error changing to folder ${folder}:`, error); + console.error(`[changeFolder] Error changing to folder ${folder}:`, error); setError(error instanceof Error ? error.message : 'Unknown error'); } finally { setIsLoading(false); } - }, [loadEmails]); + }, [loadEmails, currentFolder]); // Load emails when page changes for pagination only useEffect(() => { @@ -494,48 +525,58 @@ export const useCourrier = () => { // Select an email to view const handleEmailSelect = useCallback(async (emailId: string, accountId: string, folderOverride: string) => { // Enhanced logging for better debugging - console.log(`Selecting email ${emailId} from account [${accountId}] in folder [${folderOverride}]`); + console.log(`[handleEmailSelect] Selecting email ${emailId} from account [${accountId}] in folder [${folderOverride}]`); // Skip processing if emailId is empty (used when deselecting emails) if (!emailId) { - console.log('No email ID provided, clearing selection'); + console.log('[handleEmailSelect] No email ID provided, clearing selection'); setSelectedEmail(null); return; } + // CRITICAL FIX: Validate required parameters + if (!accountId) { + console.error('[handleEmailSelect] Account ID is required for email selection'); + toast({ + variant: "destructive", + title: "Error", + description: "Missing account information when selecting email" + }); + return; + } + setIsLoading(true); try { - // CRITICAL FIX: Always use the provided accountId, never use default - // This ensures we consistently use the correct account throughout the email selection process - if (!accountId) { - throw new Error('Account ID is required for email selection'); - } - - // Normalize folder name handling - ensure consistent format + // CRITICAL FIX: Normalize folder name with detailed logging let normalizedFolder: string; let prefixedFolder: string; if (folderOverride.includes(':')) { // Extract parts if folder already has a prefix const parts = folderOverride.split(':'); + const folderAccountId = parts[0]; normalizedFolder = parts[1]; - // CRITICAL FIX: Always use the explicitly provided accountId - // This ensures we're looking in the right account when an email is clicked + console.log(`[handleEmailSelect] Folder has prefix '${folderAccountId}', normalized to ${normalizedFolder}`); + + // CRITICAL FIX: ALWAYS use the provided accountId, never use the one from the folder + if (folderAccountId !== accountId) { + console.log(`[handleEmailSelect] WARNING: Folder account ID (${folderAccountId}) doesn't match provided account ID (${accountId})`); + } + + // Create folder name with consistent account ID prefixedFolder = `${accountId}:${normalizedFolder}`; } else { - // No prefix, add one using the provided account ID + // No prefix, add one with the provided account ID normalizedFolder = folderOverride; prefixedFolder = `${accountId}:${normalizedFolder}`; + console.log(`[handleEmailSelect] Folder has no prefix, created prefixed version: ${prefixedFolder}`); } - console.log(`Email selection with normalized values: folder=${normalizedFolder}, prefixed=${prefixedFolder}, accountId=${accountId}`); + console.log(`[handleEmailSelect] Looking for email with ID=${emailId}, account=${accountId}, folder=${prefixedFolder}`); - // CRITICAL FIX: First search for email in current list using the EXACT account provided - // This ensures we don't mix emails from different accounts - console.log(`Looking for email with ID=${emailId}, account=${accountId}, normalized folder=${normalizedFolder}, prefixed=${prefixedFolder}`); - - // First, try to find by exact match with the provided account and folder + // CRITICAL FIX: Find email by exact match with the provided account ID + // This prevents mixing emails across accounts let email = emails.find(e => e.id === emailId && e.accountId === accountId && @@ -546,17 +587,18 @@ export const useCourrier = () => { ) ); - // CRITICAL FIX: If not found, we do NOT try finding by ID only - // This prevents mixing emails across accounts - if (!email) { - console.log(`Email ${emailId} not found in current list for account ${accountId} (searched ${emails.length} emails). Fetching from API.`); + if (email) { + console.log(`[handleEmailSelect] Found matching email in current list`); + } else { + console.log(`[handleEmailSelect] Email ${emailId} not found in current list for account ${accountId}, fetching from API`); + try { - // Use the provided account ID and normalized folder for the API request - console.log(`Fetching email ${emailId} directly from API with normalizedFolder=${normalizedFolder}, accountId=${accountId}`); + // CRITICAL FIX: Always use explicit parameters for API request + console.log(`[handleEmailSelect] Fetching email ${emailId} from API with folder=${normalizedFolder}, accountId=${accountId}`); const fullEmail = await fetchEmailContent(emailId, accountId, normalizedFolder); - // CRITICAL FIX: Always set the accountId correctly if (fullEmail) { + // CRITICAL FIX: Ensure the email has proper account and folder context fullEmail.accountId = accountId; // Make sure folder has the proper prefix for consistent lookup @@ -564,17 +606,14 @@ export const useCourrier = () => { fullEmail.folder = `${accountId}:${fullEmail.folder}`; } - console.log(`Successfully fetched email from API:`, { - id: fullEmail.id, - account: fullEmail.accountId, - folder: fullEmail.folder - }); + console.log(`[handleEmailSelect] Successfully fetched email from API`); setSelectedEmail(fullEmail); + } else { + throw new Error('Email content not found'); } } catch (error) { - // Type the error properly const fetchError = error instanceof Error ? error : new Error(String(error)); - console.error(`Error fetching email from API: ${fetchError.message}`); + console.error(`[handleEmailSelect] Error fetching email from API: ${fetchError.message}`); throw fetchError; } return; @@ -582,53 +621,53 @@ export const useCourrier = () => { // If content is not fetched, get the full content if (!email.contentFetched) { - console.log(`Email found but content not fetched. Getting full content for ${emailId}`); + console.log(`[handleEmailSelect] Email found but content not fetched. Getting full content for ${emailId}`); + try { - // CRITICAL FIX: Use the provided accountId for fetching content, not the one from the email - // This ensures consistent account context + console.log(`[handleEmailSelect] Fetching content with explicit parameters: emailId=${emailId}, accountId=${accountId}, folder=${normalizedFolder}`); - console.log(`Fetching content for email ${emailId} with accountId=${accountId}, folder=${normalizedFolder}`); - - // Use the provided accountId and normalized folder for fetching + // CRITICAL FIX: Use explicit parameters for API request const fullEmail = await fetchEmailContent( emailId, accountId, normalizedFolder ); - // CRITICAL FIX: Ensure the returned email has the correct account ID if (fullEmail) { + // CRITICAL FIX: Force correct account ID fullEmail.accountId = accountId; + + // CRITICAL FIX: Create an updated email with explicit account context + const updatedEmail = { + ...email, + accountId: accountId, // Use provided accountId + folder: prefixedFolder, // Use consistent prefixed folder + content: fullEmail.content, + attachments: fullEmail.attachments, + contentFetched: true + }; + + // Update the email in the list - only match emails with the same ID AND account + setEmails(emails.map(e => + (e.id === emailId && e.accountId === accountId) ? updatedEmail : e + )); + + setSelectedEmail(updatedEmail); + console.log(`[handleEmailSelect] Email content loaded successfully`); } - - // Merge the full content with the email - const updatedEmail = { - ...email, - accountId, // CRITICAL FIX: Use the provided accountId - folder: prefixedFolder, // CRITICAL FIX: Use the consistently prefixed folder - content: fullEmail.content, - attachments: fullEmail.attachments, - contentFetched: true - }; - - // Update the email in the list - setEmails(emails.map(e => e.id === emailId && e.accountId === accountId ? updatedEmail : e)); - setSelectedEmail(updatedEmail); - console.log(`Successfully updated email with content`); } catch (error) { - // Type the error properly const contentError = error instanceof Error ? error : new Error(String(error)); - console.error(`Error fetching email content: ${contentError.message}`); + console.error(`[handleEmailSelect] Error fetching email content: ${contentError.message}`); throw contentError; } } else { - console.log(`Email found with content already fetched, selecting directly`); + console.log(`[handleEmailSelect] Email found with content already fetched, selecting directly`); - // CRITICAL FIX: Ensure the email has the correct account ID before selecting + // CRITICAL FIX: Even if already fetched, ensure consistent account context email = { ...email, - accountId, // Always use the provided accountId - folder: prefixedFolder // Always use the consistently prefixed folder + accountId: accountId, // Force the correct account ID + folder: prefixedFolder // Force the consistent folder prefix }; setSelectedEmail(email); @@ -636,13 +675,13 @@ export const useCourrier = () => { // Mark the email as read if it's not already if (!email.flags.seen) { - console.log(`Marking email ${emailId} as read for account ${accountId}`); + console.log(`[handleEmailSelect] Marking email ${emailId} as read for account ${accountId}`); markEmailAsRead(emailId, true, accountId).catch(err => { - console.error(`Failed to mark email as read: ${err.message}`); + console.error(`[handleEmailSelect] Failed to mark email as read: ${err.message}`); }); } } catch (err) { - console.error(`Error selecting email: ${err instanceof Error ? err.message : String(err)}`); + console.error(`[handleEmailSelect] Error: ${err instanceof Error ? err.message : String(err)}`); toast({ variant: "destructive", title: "Error",