diff --git a/app/api/courrier/refresh/route.ts b/app/api/courrier/refresh/route.ts new file mode 100644 index 00000000..d0ed56bc --- /dev/null +++ b/app/api/courrier/refresh/route.ts @@ -0,0 +1,49 @@ +import { NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/app/api/auth/[...nextauth]/route'; +import { getEmails } from '@/lib/services/email-service'; +import { invalidateFolderCache } from '@/lib/redis'; +import { refreshEmailsInBackground } from '@/lib/services/prefetch-service'; + +/** + * API endpoint to force refresh email data + * This is useful when the user wants to manually refresh or + * when the app detects that it's been a while since the last refresh + */ +export async function POST(request: Request) { + try { + // Authenticate user + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json( + { error: "Not authenticated" }, + { status: 401 } + ); + } + + // Extract folder from request body + const { folder = 'INBOX' } = await request.json(); + + // First invalidate the cache for this folder + await invalidateFolderCache(session.user.id, folder); + + // Then trigger a background refresh + refreshEmailsInBackground(session.user.id, folder, 1, 20); + + // Also prefetch page 2 if this is the inbox + if (folder === 'INBOX') { + refreshEmailsInBackground(session.user.id, folder, 2, 20); + } + + return NextResponse.json({ + success: true, + message: `Refresh scheduled for folder: ${folder}` + }); + } catch (error) { + console.error('Error scheduling refresh:', error); + return NextResponse.json( + { error: "Failed to schedule refresh" }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/components/email/EmailList.tsx b/components/email/EmailList.tsx index ec17772f..94480b2c 100644 --- a/components/email/EmailList.tsx +++ b/components/email/EmailList.tsx @@ -62,38 +62,63 @@ export default function EmailList({ } // If near bottom (within 200px) and more emails are available, load more - if (scrollHeight - scrollTop - clientHeight < 200 && hasMoreEmails && !isLoading && !isLoadingMore) { + // Added additional checks to prevent loading loop + const isNearBottom = scrollHeight - scrollTop - clientHeight < 200; + if (isNearBottom && hasMoreEmails && !isLoading && !isLoadingMore) { setIsLoadingMore(true); // Use timeout to debounce load requests scrollTimeoutRef.current = setTimeout(() => { + // Clear the timeout reference before loading + scrollTimeoutRef.current = null; onLoadMore(); // Reset loading state after a delay setTimeout(() => { setIsLoadingMore(false); - }, 1000); - }, 100); + }, 1500); // Increased from 1000ms to 1500ms to prevent quick re-triggering + }, 200); // Increased from 100ms to 200ms for better debouncing } }, [hasMoreEmails, isLoading, isLoadingMore, onLoadMore]); // Restore scroll position when emails are loaded useEffect(() => { - if (emails.length > prevEmailsLengthRef.current && scrollRef.current && scrollPosition > 0) { - // Maintain scroll position when new emails are added - scrollRef.current.scrollTop = scrollPosition; + // Only attempt to restore position if: + // 1. We have more emails than before + // 2. We have a scroll reference + // 3. We have a saved scroll position + // 4. We're not in the middle of a loading operation + if (emails.length > prevEmailsLengthRef.current && + scrollRef.current && + scrollPosition > 0 && + !isLoading) { + // Use requestAnimationFrame to ensure the DOM has updated + requestAnimationFrame(() => { + if (scrollRef.current) { + scrollRef.current.scrollTop = scrollPosition; + console.log(`Restored scroll position to ${scrollPosition}`); + } + }); } + // Always update the reference for next comparison prevEmailsLengthRef.current = emails.length; - }, [emails.length, scrollPosition]); + }, [emails.length, scrollPosition, isLoading]); - // Clean up timeouts on unmount + // Add listener for custom reset scroll event useEffect(() => { - return () => { - if (scrollTimeoutRef.current) { - clearTimeout(scrollTimeoutRef.current); + const handleResetScroll = () => { + if (scrollRef.current) { + scrollRef.current.scrollTop = 0; + setScrollPosition(0); } }; + + window.addEventListener('reset-email-scroll', handleResetScroll); + + return () => { + window.removeEventListener('reset-email-scroll', handleResetScroll); + }; }, []); const handleSearch = (e: React.FormEvent) => {