'use client'; import React, { useState, useRef, useEffect, useCallback } from 'react'; import { Loader2, Mail, Search, X } from 'lucide-react'; import { Email } from '@/hooks/use-courrier'; import EmailListItem from '@/components/email/EmailListItem'; import EmailListHeader from '@/components/email/EmailListHeader'; import BulkActionsToolbar from '@/components/email/BulkActionsToolbar'; import { Input } from '@/components/ui/input'; interface EmailListProps { emails: Email[]; selectedEmailIds: string[]; selectedEmail: Email | null; currentFolder: string; isLoading: boolean; totalEmails: number; hasMoreEmails: boolean; onSelectEmail: (emailId: string) => void; onToggleSelect: (emailId: string) => void; onToggleSelectAll: () => void; onBulkAction: (action: 'delete' | 'mark-read' | 'mark-unread' | 'archive') => void; onToggleStarred: (emailId: string) => void; onLoadMore: () => void; onSearch?: (query: string) => void; } export default function EmailList({ emails, selectedEmailIds, selectedEmail, currentFolder, isLoading, totalEmails, hasMoreEmails, onSelectEmail, onToggleSelect, onToggleSelectAll, onBulkAction, onToggleStarred, onLoadMore, onSearch }: EmailListProps) { const [scrollPosition, setScrollPosition] = useState(0); const [searchQuery, setSearchQuery] = useState(''); const [isLoadingMore, setIsLoadingMore] = useState(false); const scrollRef = useRef(null); const scrollTimeoutRef = useRef(null); const prevEmailsLengthRef = useRef(emails.length); // Debounced scroll handler for better performance const handleScroll = useCallback((event: React.UIEvent) => { const target = event.target as HTMLDivElement; const { scrollTop, scrollHeight, clientHeight } = target; // Save scroll position for restoration setScrollPosition(scrollTop); // Clear any existing timeout to prevent rapid firing if (scrollTimeoutRef.current) { clearTimeout(scrollTimeoutRef.current); } // If near bottom (within 200px) and more emails are available, load more // 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); }, 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(() => { // 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, isLoading]); // Add listener for custom reset scroll event useEffect(() => { 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) => { e.preventDefault(); onSearch?.(searchQuery); // Reset scroll to top when searching if (scrollRef.current) { scrollRef.current.scrollTop = 0; } }; const clearSearch = () => { setSearchQuery(''); onSearch?.(''); }; const scrollToTop = () => { if (scrollRef.current) { // Use smooth scrolling for better UX scrollRef.current.scrollTo({ top: 0, behavior: 'smooth' }); } }; // Render loading state if (isLoading && emails.length === 0) { return (
); } // Render empty state if (emails.length === 0) { return (

{searchQuery ? 'No emails match your search' : currentFolder === 'INBOX' ? "Your inbox is empty. You're all caught up!" : 'No emails in this folder'}

); } // Are all emails selected const allSelected = selectedEmailIds.length === emails.length && emails.length > 0; // Are some (but not all) emails selected const someSelected = selectedEmailIds.length > 0 && selectedEmailIds.length < emails.length; return (
{/* Search header */}
setSearchQuery(e.target.value)} /> {searchQuery && ( )}
{selectedEmailIds.length > 0 && ( )}
{/* Back to top button */} {scrollPosition > 300 && ( )} {/* Email list */} {emails.map((email) => ( onSelectEmail(email.id)} onToggleSelect={(e: React.MouseEvent) => { e.stopPropagation(); onToggleSelect(email.id); }} onToggleStarred={(e: React.MouseEvent) => { e.stopPropagation(); onToggleStarred(email.id); }} /> ))} {/* Loading indicator */} {(isLoading || isLoadingMore) && (
)} {/* Load more button - only show when near bottom but not auto-loading */} {hasMoreEmails && !isLoading && !isLoadingMore && ( )}
); }