From b384ad11934c5dbe813ab964fc30bdb3267476f6 Mon Sep 17 00:00:00 2001 From: alma Date: Wed, 30 Apr 2025 17:12:19 +0200 Subject: [PATCH] courrier formatting --- app/courrier/page.tsx | 34 +++++++++++++++++++++++++++--- hooks/use-email-state.ts | 35 ++++++++++++++++--------------- lib/reducers/emailReducer.ts | 40 +++++++++++++++++++++++++++++------- 3 files changed, 83 insertions(+), 26 deletions(-) diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index 003dad41..d8717cb6 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -749,11 +749,39 @@ export default function CourrierPage() { const distanceToBottom = scrollHeight - scrollTop - clientHeight; const scrollPercentage = (scrollTop + clientHeight) / scrollHeight; - console.log(`[DEBUG-WRAPPER-SCROLL] Distance to bottom: ${distanceToBottom}px, percentage: ${Math.round(scrollPercentage * 100)}%, hasMoreEmails: ${page < totalPages}, isLoading: ${isLoading}`); + // Store last scroll position to detect direction + const lastScrollTop = target.dataset.lastScrollTop ? + parseInt(target.dataset.lastScrollTop) : 0; + const scrollingDown = scrollTop > lastScrollTop; - // If near bottom and not already loading and has more emails - if (distanceToBottom < 300 && !isLoading && page < totalPages) { + // Update last scroll position + target.dataset.lastScrollTop = scrollTop.toString(); + + // Prevent frequent log spam with a timestamp check + const now = Date.now(); + const lastLog = parseInt(target.dataset.lastLogTime || '0'); + if (now - lastLog > 500) { // Log at most every 500ms + console.log(`[DEBUG-WRAPPER-SCROLL] Distance: ${distanceToBottom}px, %: ${Math.round(scrollPercentage * 100)}%, direction: ${scrollingDown ? 'down' : 'up'}, more: ${page < totalPages}, loading: ${isLoading}`); + target.dataset.lastLogTime = now.toString(); + } + + // Check throttle to prevent multiple rapid triggers + const lastTrigger = parseInt(target.dataset.lastTriggerTime || '0'); + const throttleTime = 1000; // 1 second throttle + + // Only trigger if: + // 1. Scrolling DOWN (not up) + // 2. Near the bottom + // 3. Not currently loading + // 4. More emails exist + // 5. Not throttled + if (scrollingDown && + distanceToBottom < 300 && + !isLoading && + page < totalPages && + now - lastTrigger > throttleTime) { console.log(`[DEBUG-WRAPPER-TRIGGER] Calling handleLoadMore from wrapper`); + target.dataset.lastTriggerTime = now.toString(); handleLoadMore(); } }} diff --git a/hooks/use-email-state.ts b/hooks/use-email-state.ts index 3a7fe00a..aa1c2bff 100644 --- a/hooks/use-email-state.ts +++ b/hooks/use-email-state.ts @@ -619,32 +619,25 @@ export const useEmailState = () => { // DEBUG: Log current state console.log(`[DEBUG-LOAD_MORE] Current state - page: ${state.page}, totalPages: ${state.totalPages}, isLoading: ${state.isLoading}`); + // CRITICAL: Don't try to load more if we're already at the last page + // This prevents unnecessary API calls when scrolling up and down at the end of the list + if (state.page >= state.totalPages && state.totalPages > 0) { + console.log(`[DEBUG-LOAD_MORE] Already at last page (${state.page}/${state.totalPages}), not loading more`); + return; + } + if (state.isLoading) { logEmailOp('LOAD_MORE', `Skipping load more request - already loading`); console.log(`[DEBUG-LOAD_MORE] Skipping because isLoading is true`); return; } - if (state.page >= state.totalPages && state.totalPages > 0) { - logEmailOp('LOAD_MORE', `No more emails to load: page ${state.page}/${state.totalPages}`); - console.log(`[DEBUG-LOAD_MORE] At last page - page: ${state.page}, totalPages: ${state.totalPages}`); - // CRITICAL FIX: Re-fetch the last page to force checking for new emails - const { effectiveAccountId } = normalizeFolderAndAccount(state.currentFolder); - setTimeout(() => { - // Re-fetch the last page in a delay to ensure the user can see there are no more emails - logEmailOp('LOAD_MORE', `Re-checking for emails at page ${state.page}`); - console.log(`[DEBUG-LOAD_MORE] Re-checking emails at page ${state.page}`); - loadEmails(true, effectiveAccountId); - }, 2000); - return; - } - // Normal case - load the next page console.log(`[DEBUG-LOAD_MORE] Incrementing page from ${state.page} to ${state.page + 1}`); logEmailOp('LOAD_MORE', `Loading more emails: page ${state.page + 1}/${state.totalPages || '?'}`); dispatch({ type: 'INCREMENT_PAGE' }); // The actual loading will be handled by the useEffect that watches page changes - }, [state.page, state.totalPages, state.isLoading, state.currentFolder, loadEmails, logEmailOp]); + }, [state.page, state.totalPages, state.isLoading, logEmailOp]); // Effect to load emails when folder changes useEffect(() => { @@ -665,6 +658,16 @@ export const useEmailState = () => { // Debug log console.log(`[DEBUG-PAGE_EFFECT] Page changed to ${state.page}, calling loadEmails`); + // Prevent duplicate effect triggers with ref tracking + const currentEffectPage = state.page; + + // CRITICAL: We need to avoid triggering duplicate loads + // Check if we're already loading to prevent duplicate calls + if (state.isLoading) { + console.log(`[DEBUG-PAGE_EFFECT] Skipping loadEmails - already loading`); + return; + } + // Extract account ID for consistency const { effectiveAccountId } = normalizeFolderAndAccount(state.currentFolder); @@ -673,7 +676,7 @@ export const useEmailState = () => { // Load more emails with the correct account ID loadEmails(true, effectiveAccountId); } - }, [session?.user?.id, state.page, state.currentFolder, loadEmails, logEmailOp]); + }, [session?.user?.id, state.page, state.currentFolder, loadEmails, logEmailOp, state.isLoading]); // Fetch unread counts from API const fetchUnreadCounts = useCallback(async () => { diff --git a/lib/reducers/emailReducer.ts b/lib/reducers/emailReducer.ts index 6224c825..32d71ff4 100644 --- a/lib/reducers/emailReducer.ts +++ b/lib/reducers/emailReducer.ts @@ -189,17 +189,30 @@ export function emailReducer(state: EmailState, action: EmailAction): EmailState ); } - // Sort by date, newest first + // CRITICAL FIX: Enhanced sorting function that ensures proper date handling const sortedEmails = unsortedEmails.sort((a, b) => { - const dateA = new Date(a.date).getTime(); - const dateB = new Date(b.date).getTime(); + // Convert all dates to timestamps for comparison + let dateA: number, dateB: number; + + try { + dateA = a.date instanceof Date ? a.date.getTime() : new Date(a.date).getTime(); + } catch (e) { + dateA = 0; // Default to oldest if invalid + } + + try { + dateB = b.date instanceof Date ? b.date.getTime() : new Date(b.date).getTime(); + } catch (e) { + dateB = 0; // Default to oldest if invalid + } // Handle invalid dates if (isNaN(dateA) && isNaN(dateB)) return 0; if (isNaN(dateA)) return 1; // Put invalid dates at the end if (isNaN(dateB)) return -1; - return dateB - dateA; // Newest first + // Sort newest first + return dateB - dateA; }); // For debugging - log a few emails after sorting @@ -256,15 +269,28 @@ export function emailReducer(state: EmailState, action: EmailAction): EmailState // Combine and sort emails by date (newest first) const combinedEmails = [...state.emails, ...newEmails].sort( (a, b) => { - const dateA = new Date(a.date).getTime(); - const dateB = new Date(b.date).getTime(); + // Convert all dates to timestamps for comparison + let dateA: number, dateB: number; + + try { + dateA = a.date instanceof Date ? a.date.getTime() : new Date(a.date).getTime(); + } catch (e) { + dateA = 0; // Default to oldest if invalid + } + + try { + dateB = b.date instanceof Date ? b.date.getTime() : new Date(b.date).getTime(); + } catch (e) { + dateB = 0; // Default to oldest if invalid + } // Handle invalid dates if (isNaN(dateA) && isNaN(dateB)) return 0; if (isNaN(dateA)) return 1; // Put invalid dates at the end if (isNaN(dateB)) return -1; - return dateB - dateA; // Newest first + // Sort newest first + return dateB - dateA; } );