diff --git a/hooks/use-courrier.ts b/hooks/use-courrier.ts index 929fbddf..dcdf4496 100644 --- a/hooks/use-courrier.ts +++ b/hooks/use-courrier.ts @@ -328,7 +328,6 @@ export const useCourrier = () => { // CRITICAL FIX: Clear and precise parameter handling with detailed logging let normalizedFolder: string; let effectiveAccountId: string; - let prefixedFolder: string; // Parse input folder parameter if (folder.includes(':')) { @@ -354,7 +353,7 @@ export const useCourrier = () => { } // CRITICAL FIX: Always create a consistently formatted folder name with the EFFECTIVE account prefix - prefixedFolder = `${effectiveAccountId}:${normalizedFolder}`; + const prefixedFolder = `${effectiveAccountId}:${normalizedFolder}`; console.log(`[changeFolder] Normalized parameters: folder=${normalizedFolder}, accountId=${effectiveAccountId}, prefixedFolder=${prefixedFolder}`); @@ -364,29 +363,29 @@ export const useCourrier = () => { // Reset to page 1 setPage(1); - // CRITICAL FIX: Set currentFolder state and verify it was set correctly + // Set currentFolder state console.log(`[changeFolder] Setting currentFolder to: ${prefixedFolder}`); setCurrentFolder(prefixedFolder); - // 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: 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}`); + // CRITICAL FIX: Wait for state updates to propagate + setTimeout(async () => { + try { + // 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(`[changeFolder] Error in delayed loadEmails:`, error); + setError(error instanceof Error ? error.message : 'Error loading emails'); + setIsLoading(false); + } + }, 50); } catch (error) { console.error(`[changeFolder] Error changing to folder ${folder}:`, error); setError(error instanceof Error ? error.message : 'Unknown error'); - } finally { setIsLoading(false); } - }, [loadEmails, currentFolder]); + }, [loadEmails, setSearchQuery, setPage, setCurrentFolder, setEmails, setSelectedEmail, setSelectedEmailIds, setIsLoading, setError]); // Load emails when page changes for pagination only useEffect(() => { diff --git a/lib/services/prefetch-service.ts b/lib/services/prefetch-service.ts index 0c387b22..1d436ff0 100644 --- a/lib/services/prefetch-service.ts +++ b/lib/services/prefetch-service.ts @@ -68,41 +68,57 @@ export async function getCachedEmailsWithTimeout( return null; } - // Extract account ID from folder name if present and none was explicitly provided - const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId; - - // Use the most specific account ID available - const effectiveAccountId = folderAccountId || accountId || 'default'; + // CRITICAL FIX: Proper folder and account ID normalization + // This is critical for consistent cache keys + let effectiveAccountId: string; + let normalizedFolder: string; - // Normalize folder name by removing account prefix if present - // This ensures consistent cache key format regardless of how folder name is passed - const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder; + // First, handle the folder format + if (folder.includes(':')) { + // Extract parts if folder already has a prefix + const parts = folder.split(':'); + const folderAccountId = parts[0]; + normalizedFolder = parts[1]; + + // CRITICAL FIX: If explicit accountId is provided, it ALWAYS takes precedence + // This ensures account switching works correctly + if (accountId) { + console.log(`[getCachedEmailsWithTimeout] Using provided accountId (${accountId}) over folder prefix (${folderAccountId})`); + effectiveAccountId = accountId; + } else { + effectiveAccountId = folderAccountId; + } + } else { + // No folder prefix, use the folder name as is + normalizedFolder = folder; + effectiveAccountId = accountId || 'default'; + } // Log the normalization for debugging - if (folder !== normalizedFolder) { - console.log(`Normalized folder name from ${folder} to ${normalizedFolder} for cache lookup with account ID ${effectiveAccountId}`); - } + console.log(`[getCachedEmailsWithTimeout] Normalized: folder=${normalizedFolder}, accountId=${effectiveAccountId} (from ${folder})`); return new Promise((resolve) => { const timeoutId = setTimeout(() => { - console.log(`Cache access timeout for ${userId}:${normalizedFolder}:${page}:${perPage} for account ${effectiveAccountId}`); + console.log(`Cache access timeout for ${userId}:${effectiveAccountId}:${normalizedFolder}:${page}:${perPage}`); resolve(null); }, timeoutMs); + // CRITICAL FIX: Use the normalized parameters consistently + // This ensures we're looking up the right cache entries getCachedEmailList(userId, effectiveAccountId, normalizedFolder, page, perPage) .then(result => { clearTimeout(timeoutId); if (result) { - console.log(`Using cached data for ${userId}:${normalizedFolder}:${page}:${perPage} for account ${effectiveAccountId}`); + console.log(`[getCachedEmailsWithTimeout] Cache hit for ${userId}:${effectiveAccountId}:${normalizedFolder}:${page}:${perPage}`); resolve(result); } else { - console.log(`Redis cache miss for ${userId}:${effectiveAccountId}:${normalizedFolder}:${page}:${perPage}, fetching emails from IMAP`); + console.log(`[getCachedEmailsWithTimeout] Cache miss for ${userId}:${effectiveAccountId}:${normalizedFolder}:${page}:${perPage}`); resolve(null); } }) .catch(err => { clearTimeout(timeoutId); - console.error('Error accessing cache:', err); + console.error('[getCachedEmailsWithTimeout] Error accessing cache:', err); resolve(null); }); }); @@ -119,16 +135,35 @@ export async function refreshEmailsInBackground( perPage: number = 20, accountId?: string ): Promise { - // Extract account ID from folder name if present and none was explicitly provided - const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId; + // CRITICAL FIX: Apply consistent account ID and folder name normalization + let effectiveAccountId: string; + let normalizedFolder: string; - // Use the most specific account ID available - const effectiveAccountId = folderAccountId || accountId || 'default'; + // First, handle the folder format + if (folder.includes(':')) { + // Extract parts if folder already has a prefix + const parts = folder.split(':'); + const folderAccountId = parts[0]; + normalizedFolder = parts[1]; + + // CRITICAL FIX: If explicit accountId is provided, it ALWAYS takes precedence + if (accountId) { + console.log(`[refreshEmailsInBackground] Using provided accountId (${accountId}) over folder prefix (${folderAccountId})`); + effectiveAccountId = accountId; + } else { + effectiveAccountId = folderAccountId; + } + } else { + // No folder prefix + normalizedFolder = folder; + effectiveAccountId = accountId || 'default'; + } - // Normalize folder name by removing account prefix if present - const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder; + console.log(`[refreshEmailsInBackground] Normalized: folder=${normalizedFolder}, accountId=${effectiveAccountId} (from ${folder})`); - const prefetchKey = `refresh:${normalizedFolder}:${page}:${effectiveAccountId}`; + // CRITICAL FIX: Include accountId in the prefetch key for better tracking + // This ensures we don't mix account operations during background refresh + const prefetchKey = `refresh:${effectiveAccountId}:${normalizedFolder}:${page}`; // Skip if already in progress or in cooldown if (!shouldPrefetch(userId, prefetchKey)) { @@ -138,11 +173,14 @@ export async function refreshEmailsInBackground( // Use setTimeout to ensure this runs after current execution context setTimeout(async () => { try { - console.log(`Background refresh for ${userId}:${normalizedFolder}:${page}:${perPage} for account ${effectiveAccountId}`); + console.log(`[refreshEmailsInBackground] Starting refresh for ${userId}, account=${effectiveAccountId}, folder=${normalizedFolder}, page=${page}`); + + // CRITICAL FIX: Pass normalized parameters to ensure consistent API calls const freshData = await getEmails(userId, normalizedFolder, page, perPage, effectiveAccountId); - console.log(`Background refresh completed for ${userId}:${normalizedFolder} for account ${effectiveAccountId}`); + + console.log(`[refreshEmailsInBackground] Completed for ${userId}, account=${effectiveAccountId}, folder=${normalizedFolder}`); } catch (error) { - console.error('Background refresh error:', error); + console.error(`[refreshEmailsInBackground] Error: ${error instanceof Error ? error.message : String(error)}`); } finally { markPrefetchCompleted(userId, prefetchKey); }