diff --git a/hooks/use-email-state.ts b/hooks/use-email-state.ts index 94f767b9..c925f2b4 100644 --- a/hooks/use-email-state.ts +++ b/hooks/use-email-state.ts @@ -62,47 +62,48 @@ export const useEmailState = () => { }, []); // Load emails from the server - const loadEmails = useCallback(async (isLoadMore = false, accountId?: string) => { + const loadEmails = useCallback(async (page: number, perPage: number, isLoadMore: boolean = false) => { if (!session?.user?.id) return; // CRITICAL FIX: Always log the isLoadMore parameter - console.log(`[DEBUG-LOAD_EMAILS] Called with isLoadMore=${isLoadMore}, page=${state.page}, currentEmails=${state.emails.length}`); + console.log(`[DEBUG-LOAD_EMAILS] Called with isLoadMore=${isLoadMore}, page=${page}, currentEmails=${state.emails.length}`); dispatch({ type: 'SET_LOADING', payload: true }); try { - // Get normalized parameters using helper function + // Get normalized parameters using helper function with proper account ID handling + const accountId = state.selectedAccount ? state.selectedAccount.id : undefined; const { normalizedFolder, effectiveAccountId, prefixedFolder } = normalizeFolderAndAccount(state.currentFolder, accountId); - logEmailOp('LOAD_EMAILS', `Loading emails for ${prefixedFolder} (account: ${effectiveAccountId}, isLoadMore: ${isLoadMore}, page: ${state.page})`); + logEmailOp('LOAD_EMAILS', `Loading emails for ${prefixedFolder} (account: ${effectiveAccountId}, isLoadMore: ${isLoadMore}, page: ${page})`); // Construct query parameters const queryParams = new URLSearchParams({ folder: normalizedFolder, - page: state.page.toString(), - perPage: state.perPage.toString(), + page: page.toString(), + perPage: perPage.toString(), accountId: effectiveAccountId }); // Debug log existing emails count if (isLoadMore) { - console.log(`[DEBUG-PAGINATION] Loading more emails. Current page: ${state.page}, existing emails: ${state.emails.length}`); + console.log(`[DEBUG-PAGINATION] Loading more emails. Current page: ${page}, existing emails: ${state.emails.length}`); } // Try to get cached emails first - logEmailOp('CACHE_CHECK', `Checking cache for ${prefixedFolder}, page: ${state.page}`); + logEmailOp('CACHE_CHECK', `Checking cache for ${prefixedFolder}, page: ${page}`); const cachedEmails = await getCachedEmailsWithTimeout( session.user.id, prefixedFolder, - state.page, - state.perPage, + page, + perPage, 100, effectiveAccountId ); if (cachedEmails) { - logEmailOp('CACHE_HIT', `Using cached data for ${prefixedFolder}, page: ${state.page}, emails: ${cachedEmails.emails?.length || 0}, isLoadMore: ${isLoadMore}`); + logEmailOp('CACHE_HIT', `Using cached data for ${prefixedFolder}, page: ${page}, emails: ${cachedEmails.emails?.length || 0}, isLoadMore: ${isLoadMore}`); // Ensure cached data has emails array property if (Array.isArray(cachedEmails.emails)) { @@ -147,14 +148,14 @@ export const useEmailState = () => { if (!response.ok) { // CRITICAL FIX: Try to recover from fetch errors by retrying with different pagination - if (isLoadMore && state.page > 1) { - logEmailOp('ERROR_RECOVERY', `Failed to fetch emails for page ${state.page}, attempting to recover by decrementing page`); - console.log(`[DEBUG-ERROR] API returned ${response.status} for page ${state.page}`); + if (isLoadMore && page > 1) { + logEmailOp('ERROR_RECOVERY', `Failed to fetch emails for page ${page}, attempting to recover by decrementing page`); + console.log(`[DEBUG-ERROR] API returned ${response.status} for page ${page}`); // If we're loading more and there's an error, just decrement the page to avoid getting stuck - dispatch({ type: 'SET_PAGE', payload: state.page - 1 }); + dispatch({ type: 'SET_PAGE', payload: page - 1 }); dispatch({ type: 'SET_LOADING', payload: false }); // Also reset total pages to try again - dispatch({ type: 'SET_TOTAL_PAGES', payload: state.page }); + dispatch({ type: 'SET_TOTAL_PAGES', payload: page }); return; } @@ -167,10 +168,10 @@ export const useEmailState = () => { // CRITICAL FIX: Enhanced empty results handling if (!data.emails || data.emails.length === 0) { - console.log(`[DEBUG-EMPTY] No emails in response for page ${state.page}`); + console.log(`[DEBUG-EMPTY] No emails in response for page ${page}`); // If we're at a page > 1 and got no results, the paging is off, so try again with page 1 - if (state.page > 1 && !isLoadMore) { - logEmailOp('EMPTY_RESULTS', `No emails returned for page ${state.page}, resetting to page 1`); + if (page > 1 && !isLoadMore) { + logEmailOp('EMPTY_RESULTS', `No emails returned for page ${page}, resetting to page 1`); dispatch({ type: 'SET_PAGE', payload: 1 }); dispatch({ type: 'SET_LOADING', payload: false }); return; @@ -271,7 +272,7 @@ export const useEmailState = () => { description: err instanceof Error ? err.message : 'Failed to load emails' }); } - }, [session?.user?.id, state.currentFolder, state.page, state.perPage, state.emails.length, toast, logEmailOp]); + }, [session?.user?.id, state.currentFolder, state.selectedAccount, state.page, state.perPage, state.emails.length, toast, logEmailOp]); // Change folder const changeFolder = useCallback(async (folder: string, accountId?: string) => { @@ -459,7 +460,7 @@ export const useEmailState = () => { } // Reload emails to get updated state - loadEmails(); + loadEmails(state.page, state.perPage, true); return true; } catch (error) { @@ -507,7 +508,7 @@ export const useEmailState = () => { dispatch({ type: 'CLEAR_SELECTED_EMAILS' }); // Reload emails to get updated list - loadEmails(); + loadEmails(state.page, state.perPage, true); toast({ title: "Emails Deleted", @@ -555,7 +556,7 @@ export const useEmailState = () => { }); // Refresh emails to show the sent email - loadEmails(); + loadEmails(state.page, state.perPage, true); return { success: true, ...result }; } catch (error) { @@ -644,55 +645,38 @@ export const useEmailState = () => { // Handle loading more emails const handleLoadMore = useCallback(() => { - logEmailOp('LOAD_MORE', `Current state - page: ${state.page}, totalPages: ${state.totalPages}, isLoading: ${state.isLoading}, current emails: ${state.emails.length}`); - - // Skip if we're already at the last page - if (state.page >= state.totalPages) { - logEmailOp('LOAD_MORE', 'Already at the last page, skipping load more'); - return; - } - - // Skip if we're already loading - if (state.isLoading) { - logEmailOp('LOAD_MORE', 'Already loading, skipping load more request'); - return; - } - - // Debounce: prevent multiple triggers within 1 second - const now = Date.now(); - if (now - loadMoreTriggerTimeRef.current < 1000) { - logEmailOp('LOAD_MORE', 'Debouncing load more request'); + // Don't load more if already loading or if there are no more pages + if (state.isLoading || state.page >= state.totalPages) { + console.log(`[LOAD_MORE] Skipping load more - already loading: ${state.isLoading}, page: ${state.page}, totalPages: ${state.totalPages}`); return; } - // Update the trigger time - loadMoreTriggerTimeRef.current = now; + // Log the current state + console.log(`[LOAD_MORE] Loading more emails for ${state.currentFolder}, currentPage: ${state.page}, totalPages: ${state.totalPages}, current email count: ${state.emails.length}`); - // CRITICAL FIX: Set nextPage value and log for debugging + // Set loading state immediately to prevent double-loading + dispatch({ + type: 'SET_LOADING', + payload: true + }); + + // Calculate next page const nextPage = state.page + 1; - console.log(`[DEBUG-LOAD_MORE] Incrementing page from ${state.page} to ${nextPage}, current emails count: ${state.emails.length}`); - // CRITICAL FIX: First set loading state to true to prevent other effects from firing - dispatch({ type: 'SET_LOADING', payload: true }); + // Update the page state - fix type issue + dispatch({ + type: 'SET_PAGE', + payload: nextPage + }); - // CRITICAL FIX: Get folder info for consistency - const { effectiveAccountId } = normalizeFolderAndAccount(state.currentFolder); - - // CRITICAL FIX: Directly call loadEmails with isLoadMore=true instead of relying on the useEffect - // This bypasses the issues with the folder change effect interfering - console.log(`[DEBUG-LOAD_MORE] Directly calling loadEmails with isLoadMore=true, page=${nextPage}`); - - // We need to set the page number first, so loadEmails uses the right page - dispatch({ type: 'SET_PAGE', payload: nextPage }); - - // CRITICAL FIX: Update the lastPageLoaded ref to prevent duplicate loading + // CRITICAL FIX: Update the lastLoadedPage ref to track pagination state lastPageLoadedRef.current = nextPage; - // Now load the emails with the new page number - setTimeout(() => { - loadEmails(true, effectiveAccountId); - }, 0); - }, [state.page, state.totalPages, state.isLoading, state.emails.length, state.currentFolder, logEmailOp, dispatch, loadEmails]); + // Load the next page + loadEmails(nextPage, state.perPage, true).then(() => { + console.log(`[LOAD_MORE] Completed loading more emails for page ${nextPage}`); + }); + }, [state.isLoading, state.page, state.totalPages, state.currentFolder, state.emails.length, state.perPage, dispatch, loadEmails]); // Effect to load emails when folder changes useEffect(() => { @@ -727,18 +711,22 @@ export const useEmailState = () => { // CRITICAL FIX: Only load initial emails if we're on page 1 or the folder changed if (state.page === 1 || folderChanged) { // Load emails with the correct account ID (not appending since this is a folder change) - loadEmails(false, effectiveAccountId); + loadEmails(state.page, state.perPage, false); } } - }, [session?.user?.id, state.currentFolder, state.page, loadEmails, logEmailOp, dispatch]); + }, [session?.user?.id, state.currentFolder, state.page, state.perPage, loadEmails, logEmailOp, dispatch]); // Effect to load more emails when page changes useEffect(() => { - // Skip if no user ID - if (!session?.user?.id) return; + if (!session?.user?.id || !state.currentFolder) return; - // Skip if still on page 1 or the current folder is empty - if (state.page <= 1 || !state.currentFolder) return; + // Make sure we're on at least page 1 + if (state.page < 1) { + dispatch({ type: 'SET_PAGE', payload: 1 }); + return; + } + + console.log(`[DEBUG-PAGE_EFFECT] Page changed to ${state.page}`); // CRITICAL FIX: Don't run this effect at all if we're already loading if (state.isLoading) { @@ -746,36 +734,33 @@ export const useEmailState = () => { return; } - // CRITICAL FIX: Get current folder data - const { effectiveAccountId, prefixedFolder } = normalizeFolderAndAccount(state.currentFolder); + // Normalize folder and get account ID + const { effectiveAccountId } = normalizeFolderAndAccount(state.currentFolder); - // Debug log - console.log(`[DEBUG-PAGE_EFFECT] Page changed to ${state.page}, lastLoaded=${lastPageLoadedRef.current}, folder=${prefixedFolder}, emails=${state.emails.length}`); - - // Skip if this page was already loaded for this folder - if (lastPageLoadedRef.current === state.page && prevFolderRef.current === state.currentFolder) { - console.log(`[DEBUG-PAGE_EFFECT] Skipping reload of already loaded page ${state.page} for folder ${prefixedFolder}`); + // Check if this is a duplicate page load + if (state.page === lastPageLoadedRef.current) { + console.log(`[DEBUG-PAGE_EFFECT] Skipping - already loaded page ${state.page}`); return; } - // CRITICAL FIX: Set the ref immediately to prevent further executions + // Skip loads for zero-based pages + if (state.page === 0) { + console.log(`[DEBUG-PAGE_EFFECT] Skipping load for invalid page ${state.page}`); + return; + } + + // Update our reference to prevent duplicate loads lastPageLoadedRef.current = state.page; - // Make sure we also track the current folder - prevFolderRef.current = state.currentFolder; - - logEmailOp('PAGINATION', `Loading MORE emails for page ${state.page} for folder ${prefixedFolder} (total emails so far: ${state.emails.length})`); - // CRITICAL FIX: Always use isLoadMore=true when page > 1 console.log(`[DEBUG-PAGE_EFFECT] Calling loadEmails with isLoadMore=true for page ${state.page}`); - loadEmails(true, effectiveAccountId); + loadEmails(state.page, state.perPage, true); // Do NOT include state.emails.length here to prevent infinite loops - }, [session?.user?.id, state.page, state.currentFolder, state.isLoading, loadEmails, logEmailOp]); + }, [session?.user?.id, state.page, state.currentFolder, state.isLoading, state.perPage, loadEmails, logEmailOp]); // Fetch unread counts from API const fetchUnreadCounts = useCallback(async () => { - // Don't fetch if user is not logged in if (!session?.user) return; // Don't fetch if we're already fetching @@ -783,8 +768,14 @@ export const useEmailState = () => { // Skip fetching if an email was viewed recently (within last 5 seconds) const now = Date.now(); - const lastViewedTimestamp = (window as any).__lastViewedEmailTimestamp || 0; - if (lastViewedTimestamp && now - lastViewedTimestamp < 5000) { + // Initialize the ref to the current time if it's null + if (lastEmailViewedRef.current === null) { + lastEmailViewedRef.current = now; + } + + // Now we can safely use it since we've initialized it + if (now - lastEmailViewedRef.current < 5000) { + console.log('Skipping unread count update - email viewed recently'); return; }