courrier correct panel 2 scroll up
This commit is contained in:
parent
b2c788818f
commit
41559a4d66
@ -151,36 +151,9 @@ export default function CourrierPage() {
|
||||
setLoading(true);
|
||||
|
||||
// First check if Redis is ready before making API calls
|
||||
// Use a cache mechanism to reduce frequency of Redis status checks
|
||||
const redisCheckCacheKey = 'neah_redis_status_check';
|
||||
const cachedRedisCheck = localStorage.getItem(redisCheckCacheKey);
|
||||
let redisStatus = { ready: false };
|
||||
|
||||
if (cachedRedisCheck) {
|
||||
try {
|
||||
const { status, timestamp } = JSON.parse(cachedRedisCheck);
|
||||
// Only use cache if it's less than 2 minutes old
|
||||
if (Date.now() - timestamp < 2 * 60 * 1000) {
|
||||
redisStatus = status;
|
||||
console.log('Using cached Redis status check');
|
||||
}
|
||||
} catch (e) {
|
||||
// Invalid JSON in cache, ignore and fetch fresh status
|
||||
}
|
||||
}
|
||||
|
||||
// Only check Redis status if we don't have a recent cached result
|
||||
if (!redisStatus.ready) {
|
||||
redisStatus = await fetch('/api/redis/status')
|
||||
.then(res => res.json())
|
||||
.catch(() => ({ ready: false }));
|
||||
|
||||
// Cache the result
|
||||
localStorage.setItem(redisCheckCacheKey, JSON.stringify({
|
||||
status: redisStatus,
|
||||
timestamp: Date.now()
|
||||
}));
|
||||
}
|
||||
const redisStatus = await fetch('/api/redis/status')
|
||||
.then(res => res.json())
|
||||
.catch(() => ({ ready: false }));
|
||||
|
||||
if (!isMounted) return;
|
||||
|
||||
@ -273,28 +246,15 @@ export default function CourrierPage() {
|
||||
const nextPage = page + 1;
|
||||
setPage(nextPage);
|
||||
|
||||
console.log(`Requesting page ${nextPage} for folder ${currentFolder}`);
|
||||
|
||||
// Immediately trigger a load for this page rather than relying on the useEffect
|
||||
// This helps ensure we get the data faster
|
||||
loadEmails(true).catch(error => {
|
||||
console.error(`Error loading page ${nextPage}:`, error);
|
||||
});
|
||||
|
||||
// Also prefetch additional pages to make scrolling smoother
|
||||
if (session?.user?.id) {
|
||||
// Prefetch next page beyond the one we're loading
|
||||
const pagesToPrefetch = 2; // Prefetch 2 pages ahead
|
||||
|
||||
// Small delay to let the current request start first
|
||||
setTimeout(() => {
|
||||
console.log(`Prefetching pages ${nextPage + 1} to ${nextPage + pagesToPrefetch}`);
|
||||
prefetchFolderEmails(session.user.id, currentFolder, pagesToPrefetch, nextPage + 1)
|
||||
.catch(err => {
|
||||
console.error(`Error prefetching additional pages for ${currentFolder}:`, err);
|
||||
});
|
||||
}, 200);
|
||||
// Prefetch next 2 pages beyond the current next page
|
||||
prefetchFolderEmails(session.user.id, currentFolder, 2, nextPage + 1).catch(err => {
|
||||
console.error(`Error prefetching additional pages for ${currentFolder}:`, err);
|
||||
});
|
||||
}
|
||||
|
||||
// Note: loadEmails will be called automatically due to the page dependency in useEffect
|
||||
}
|
||||
};
|
||||
|
||||
@ -400,8 +360,7 @@ export default function CourrierPage() {
|
||||
return (
|
||||
<>
|
||||
<SimplifiedLoadingFix />
|
||||
{/* Only render RedisCacheStatus in development mode to avoid unnecessary status checks */}
|
||||
{process.env.NODE_ENV === 'development' && <RedisCacheStatus />}
|
||||
<RedisCacheStatus />
|
||||
|
||||
{/* Main layout */}
|
||||
<main className="w-full h-screen bg-black">
|
||||
|
||||
@ -44,20 +44,10 @@ export default function EmailList({
|
||||
const [scrollPosition, setScrollPosition] = useState(0);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
||||
const [lastLoadTime, setLastLoadTime] = useState(0);
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const loadMoreTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const prevEmailsLengthRef = useRef(emails.length);
|
||||
|
||||
// Clear any pending timeouts on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
|
||||
if (loadMoreTimeoutRef.current) clearTimeout(loadMoreTimeoutRef.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Debounced scroll handler for better performance
|
||||
const handleScroll = useCallback((event: React.UIEvent<HTMLDivElement>) => {
|
||||
const target = event.target as HTMLDivElement;
|
||||
@ -71,37 +61,25 @@ export default function EmailList({
|
||||
clearTimeout(scrollTimeoutRef.current);
|
||||
}
|
||||
|
||||
// If near bottom (within 300px) and more emails are available, load more
|
||||
// If near bottom (within 200px) and more emails are available, load more
|
||||
// Added additional checks to prevent loading loop
|
||||
const isNearBottom = scrollHeight - scrollTop - clientHeight < 300;
|
||||
|
||||
// Don't trigger load if we're already loading or if the last load was too recent (throttle)
|
||||
const now = Date.now();
|
||||
const timeSinceLastLoad = now - lastLoadTime;
|
||||
const tooSoonToLoadAgain = timeSinceLastLoad < 2000; // 2 seconds throttle
|
||||
|
||||
if (isNearBottom && hasMoreEmails && !isLoading && !isLoadingMore && !tooSoonToLoadAgain) {
|
||||
const isNearBottom = scrollHeight - scrollTop - clientHeight < 200;
|
||||
if (isNearBottom && hasMoreEmails && !isLoading && !isLoadingMore) {
|
||||
setIsLoadingMore(true);
|
||||
setLastLoadTime(now);
|
||||
|
||||
// Use timeout to debounce load requests
|
||||
scrollTimeoutRef.current = setTimeout(() => {
|
||||
// Clear the timeout reference before loading
|
||||
scrollTimeoutRef.current = null;
|
||||
|
||||
console.log('Loading more emails from scroll trigger');
|
||||
onLoadMore();
|
||||
|
||||
// Reset loading state after a delay
|
||||
if (loadMoreTimeoutRef.current) clearTimeout(loadMoreTimeoutRef.current);
|
||||
loadMoreTimeoutRef.current = setTimeout(() => {
|
||||
loadMoreTimeoutRef.current = null;
|
||||
console.log('Resetting loading more state after timeout');
|
||||
setTimeout(() => {
|
||||
setIsLoadingMore(false);
|
||||
}, 2000); // Reduced from 3000ms to 2000ms to avoid long loading states
|
||||
}, 200); // Reduced from 300ms to 200ms for better responsiveness
|
||||
}, 1500); // Increased from 1000ms to 1500ms to prevent quick re-triggering
|
||||
}, 200); // Increased from 100ms to 200ms for better debouncing
|
||||
}
|
||||
}, [hasMoreEmails, isLoading, isLoadingMore, onLoadMore, lastLoadTime]);
|
||||
}, [hasMoreEmails, isLoading, isLoadingMore, onLoadMore]);
|
||||
|
||||
// Restore scroll position when emails are loaded
|
||||
useEffect(() => {
|
||||
@ -112,13 +90,8 @@ export default function EmailList({
|
||||
// 4. We're not in the middle of a loading operation
|
||||
if (emails.length > prevEmailsLengthRef.current &&
|
||||
scrollRef.current &&
|
||||
scrollPosition > 0) {
|
||||
// If emails have been loaded, force reset the loading state
|
||||
if (isLoadingMore) {
|
||||
console.log('Emails loaded, resetting loading state');
|
||||
setIsLoadingMore(false);
|
||||
}
|
||||
|
||||
scrollPosition > 0 &&
|
||||
!isLoading) {
|
||||
// Use requestAnimationFrame to ensure the DOM has updated
|
||||
requestAnimationFrame(() => {
|
||||
if (scrollRef.current) {
|
||||
@ -130,26 +103,7 @@ export default function EmailList({
|
||||
|
||||
// Always update the reference for next comparison
|
||||
prevEmailsLengthRef.current = emails.length;
|
||||
}, [emails.length, scrollPosition, isLoadingMore]);
|
||||
|
||||
// Add safety mechanism to reset loading state if we get stuck
|
||||
useEffect(() => {
|
||||
// If we have more emails now but still in loading state, reset it
|
||||
if (emails.length > prevEmailsLengthRef.current && isLoadingMore) {
|
||||
console.log('Safety reset: Clearing loading state after emails updated');
|
||||
setIsLoadingMore(false);
|
||||
}
|
||||
|
||||
// Add a timeout-based safety mechanism - reduced from 5000ms to 3000ms
|
||||
const safetyTimeout = setTimeout(() => {
|
||||
if (isLoadingMore) {
|
||||
console.log('Safety timeout: Resetting stuck loading state');
|
||||
setIsLoadingMore(false);
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
return () => clearTimeout(safetyTimeout);
|
||||
}, [emails.length, isLoadingMore]);
|
||||
}, [emails.length, scrollPosition, isLoading]);
|
||||
|
||||
// Add listener for custom reset scroll event
|
||||
useEffect(() => {
|
||||
@ -303,20 +257,15 @@ export default function EmailList({
|
||||
|
||||
{/* Loading indicator */}
|
||||
{(isLoading || isLoadingMore) && (
|
||||
<div className="flex items-center justify-center p-4 bg-gray-50">
|
||||
<Loader2 className="h-4 w-4 text-blue-500 animate-spin mr-2" />
|
||||
<span className="text-sm text-gray-500">Loading more emails...</span>
|
||||
<div className="flex items-center justify-center p-4">
|
||||
<Loader2 className="h-4 w-4 text-blue-500 animate-spin" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Load more button - only show when near bottom but not auto-loading */}
|
||||
{hasMoreEmails && !isLoading && !isLoadingMore && (
|
||||
<button
|
||||
onClick={() => {
|
||||
console.log('Manual load more triggered');
|
||||
setLastLoadTime(Date.now());
|
||||
onLoadMore();
|
||||
}}
|
||||
onClick={onLoadMore}
|
||||
className="w-full py-2 text-gray-500 hover:bg-gray-100 text-sm"
|
||||
>
|
||||
Load more emails
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useToast } from './use-toast';
|
||||
import { formatEmailForReplyOrForward } from '@/lib/utils/email-formatter';
|
||||
@ -52,14 +52,6 @@ export interface EmailData {
|
||||
|
||||
export type MailFolder = string;
|
||||
|
||||
// Near the top of the file, before the useCourrier hook
|
||||
interface EmailResponse {
|
||||
emails: Email[];
|
||||
total: number;
|
||||
totalPages: number;
|
||||
hasMore: boolean;
|
||||
}
|
||||
|
||||
// Hook for managing email operations
|
||||
export const useCourrier = () => {
|
||||
// State for email data
|
||||
@ -79,81 +71,175 @@ export const useCourrier = () => {
|
||||
const [perPage, setPerPage] = useState(20);
|
||||
const [totalEmails, setTotalEmails] = useState(0);
|
||||
const [totalPages, setTotalPages] = useState(0);
|
||||
const [hasMore, setHasMore] = useState(false);
|
||||
|
||||
// Auth and notifications
|
||||
const { data: session } = useSession();
|
||||
const { toast } = useToast();
|
||||
|
||||
// Add the missing refs
|
||||
const loadingRequestsRef = useRef<Set<string>>(new Set());
|
||||
const loadMoreRef = useRef<number>(0);
|
||||
|
||||
// Load emails from the server
|
||||
const loadEmails = useCallback(
|
||||
async (folder = currentFolder, pageToLoad = page, resetList = true, isInitial = false) => {
|
||||
if (!session?.user?.id || isLoading) return;
|
||||
|
||||
// Track this request to avoid duplicates
|
||||
const requestKey = `${folder}_${pageToLoad}`;
|
||||
if (loadingRequestsRef.current.has(requestKey)) {
|
||||
console.log(`Skipping duplicate request for ${requestKey}`);
|
||||
const loadEmails = useCallback(async (isLoadMore = false) => {
|
||||
if (!session?.user?.id) return;
|
||||
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
// Keep reference to the current page for this request
|
||||
const currentRequestPage = page;
|
||||
|
||||
try {
|
||||
// First try Redis cache with low timeout
|
||||
const cachedEmails = await getCachedEmailsWithTimeout(session.user.id, currentFolder, currentRequestPage, perPage, 100);
|
||||
if (cachedEmails) {
|
||||
// Ensure cached data has emails array property
|
||||
if (Array.isArray(cachedEmails.emails)) {
|
||||
if (isLoadMore) {
|
||||
// When loading more, always append to the existing list
|
||||
setEmails(prevEmails => {
|
||||
// Create a Set of existing email IDs to avoid duplicates
|
||||
const existingIds = new Set(prevEmails.map(email => email.id));
|
||||
// Filter out any duplicates before appending
|
||||
const newEmails = cachedEmails.emails.filter((email: Email) => !existingIds.has(email.id));
|
||||
|
||||
// Log pagination info
|
||||
console.log(`Added ${newEmails.length} cached emails from page ${currentRequestPage} to existing ${prevEmails.length} emails`);
|
||||
|
||||
// Combine emails and sort them by date (newest first)
|
||||
const combinedEmails = [...prevEmails, ...newEmails];
|
||||
return combinedEmails.sort((a: Email, b: Email) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
||||
});
|
||||
} else {
|
||||
// For initial load, replace emails
|
||||
console.log(`Setting ${cachedEmails.emails.length} cached emails for page ${currentRequestPage}`);
|
||||
// Ensure emails are sorted by date (newest first)
|
||||
setEmails(cachedEmails.emails.sort((a: Email, b: Email) => new Date(b.date).getTime() - new Date(a.date).getTime()));
|
||||
}
|
||||
|
||||
// Set pagination info from cache if available
|
||||
if (cachedEmails.totalEmails) setTotalEmails(cachedEmails.totalEmails);
|
||||
if (cachedEmails.totalPages) setTotalPages(cachedEmails.totalPages);
|
||||
|
||||
// Update available mailboxes if provided
|
||||
if (cachedEmails.mailboxes && cachedEmails.mailboxes.length > 0) {
|
||||
setMailboxes(cachedEmails.mailboxes);
|
||||
}
|
||||
} else if (Array.isArray(cachedEmails)) {
|
||||
// Direct array response
|
||||
if (isLoadMore) {
|
||||
setEmails(prevEmails => {
|
||||
// Create a Set of existing email IDs to avoid duplicates
|
||||
const existingIds = new Set(prevEmails.map(email => email.id));
|
||||
// Filter out any duplicates before appending
|
||||
const newEmails = cachedEmails.filter((email: Email) => !existingIds.has(email.id));
|
||||
|
||||
// Log pagination info
|
||||
console.log(`Added ${newEmails.length} cached emails from page ${currentRequestPage} to existing ${prevEmails.length} emails`);
|
||||
|
||||
// Combine emails and sort them by date (newest first)
|
||||
const combinedEmails = [...prevEmails, ...newEmails];
|
||||
return combinedEmails.sort((a: Email, b: Email) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
||||
});
|
||||
} else {
|
||||
// For initial load, replace emails
|
||||
console.log(`Setting ${cachedEmails.length} cached emails for page ${currentRequestPage}`);
|
||||
// Ensure emails are sorted by date (newest first)
|
||||
setEmails(cachedEmails.sort((a: Email, b: Email) => new Date(b.date).getTime() - new Date(a.date).getTime()));
|
||||
}
|
||||
} else {
|
||||
console.warn('Invalid cache format:', cachedEmails);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
|
||||
// Still refresh in background for fresh data
|
||||
refreshEmailsInBackground(session.user.id, currentFolder, currentRequestPage, perPage).catch(err => {
|
||||
console.error('Background refresh error:', err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
loadingRequestsRef.current.add(requestKey);
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
// Get emails for the current folder
|
||||
const response = await getEmails(session.user.id, folder, pageToLoad);
|
||||
|
||||
// Update state based on response
|
||||
if (resetList) {
|
||||
setEmails(response.emails);
|
||||
} else {
|
||||
setEmails(prev => [...prev, ...response.emails]);
|
||||
}
|
||||
|
||||
setTotalEmails(response.total);
|
||||
setTotalPages(response.totalPages);
|
||||
setHasMore(response.hasMore);
|
||||
setPage(pageToLoad);
|
||||
|
||||
if (folder !== currentFolder) {
|
||||
setCurrentFolder(folder);
|
||||
}
|
||||
|
||||
// Clear errors
|
||||
setError(null);
|
||||
} catch (error) {
|
||||
console.error('Error loading emails:', error);
|
||||
setError(error instanceof Error ? error.message : 'Failed to load emails');
|
||||
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error",
|
||||
description: "Failed to load emails"
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
// Clear the loading request tracker
|
||||
loadingRequestsRef.current.delete(requestKey);
|
||||
// Build query params
|
||||
const queryParams = new URLSearchParams({
|
||||
folder: currentFolder,
|
||||
page: currentRequestPage.toString(),
|
||||
perPage: perPage.toString()
|
||||
});
|
||||
|
||||
if (searchQuery) {
|
||||
queryParams.set('search', searchQuery);
|
||||
}
|
||||
},
|
||||
[session?.user?.id, currentFolder, page, isLoading, toast]
|
||||
);
|
||||
|
||||
// Fetch emails from API
|
||||
const response = await fetch(`/api/courrier?${queryParams.toString()}`);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || 'Failed to fetch emails');
|
||||
}
|
||||
|
||||
const data: EmailListResult = await response.json();
|
||||
|
||||
// Update state with the fetched data
|
||||
if (isLoadMore) {
|
||||
setEmails(prev => {
|
||||
// Create a Set of existing email IDs to avoid duplicates
|
||||
const existingIds = new Set(prev.map(email => email.id));
|
||||
// Filter out any duplicates before appending
|
||||
const newEmails = data.emails.filter((email: Email) => !existingIds.has(email.id));
|
||||
|
||||
// Log pagination info
|
||||
console.log(`Added ${newEmails.length} fetched emails from page ${currentRequestPage} to existing ${prev.length} emails`);
|
||||
|
||||
// Combine emails and sort them by date (newest first)
|
||||
const combinedEmails = [...prev, ...newEmails];
|
||||
return combinedEmails.sort((a: Email, b: Email) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
||||
});
|
||||
} else {
|
||||
// Ensure we always set an array even if API returns invalid data
|
||||
console.log(`Setting ${data.emails?.length || 0} fetched emails for page ${currentRequestPage}`);
|
||||
// Ensure emails are sorted by date (newest first)
|
||||
if (Array.isArray(data.emails)) {
|
||||
setEmails(data.emails.sort((a: Email, b: Email) => new Date(b.date).getTime() - new Date(a.date).getTime()));
|
||||
} else {
|
||||
setEmails([]);
|
||||
}
|
||||
}
|
||||
|
||||
setTotalEmails(data.totalEmails);
|
||||
setTotalPages(data.totalPages);
|
||||
|
||||
// Update available mailboxes if provided
|
||||
if (data.mailboxes && data.mailboxes.length > 0) {
|
||||
setMailboxes(data.mailboxes);
|
||||
}
|
||||
|
||||
// Clear selection if not loading more
|
||||
if (!isLoadMore) {
|
||||
setSelectedEmail(null);
|
||||
setSelectedEmailIds([]);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error loading emails for page ${currentRequestPage}:`, err);
|
||||
// Set emails to empty array on error to prevent runtime issues
|
||||
if (!isLoadMore) {
|
||||
setEmails([]);
|
||||
}
|
||||
setError(err instanceof Error ? err.message : 'Failed to load emails');
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error",
|
||||
description: err instanceof Error ? err.message : 'Failed to load emails'
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [currentFolder, page, perPage, searchQuery, session?.user?.id, toast]);
|
||||
|
||||
// Load emails when folder or page changes
|
||||
useEffect(() => {
|
||||
if (session?.user?.id) {
|
||||
// If page is greater than 1, we're loading more emails
|
||||
const isLoadingMore = page > 1;
|
||||
|
||||
// Add a small delay to prevent rapid consecutive loads
|
||||
const loadTimer = setTimeout(() => {
|
||||
loadEmails(currentFolder, page, false, false);
|
||||
}, 50);
|
||||
loadEmails(isLoadingMore);
|
||||
|
||||
// If we're loading the first page, publish an event to reset scroll position
|
||||
if (page === 1 && typeof window !== 'undefined') {
|
||||
@ -161,10 +247,8 @@ export const useCourrier = () => {
|
||||
const event = new CustomEvent('reset-email-scroll');
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
|
||||
return () => clearTimeout(loadTimer);
|
||||
}
|
||||
}, [currentFolder, page, session?.user?.id, loadEmails]);
|
||||
}, [currentFolder, page, perPage, session?.user?.id, loadEmails]);
|
||||
|
||||
// Fetch a single email's content
|
||||
const fetchEmailContent = useCallback(async (emailId: string) => {
|
||||
@ -451,75 +535,6 @@ export const useCourrier = () => {
|
||||
return formatEmailForReplyOrForward(email, type);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Fetches emails from the API
|
||||
*/
|
||||
const getEmails = async (userId: string, folder: string, page: number): Promise<EmailResponse> => {
|
||||
// Build query params
|
||||
const queryParams = new URLSearchParams({
|
||||
folder: folder,
|
||||
page: page.toString(),
|
||||
perPage: perPage.toString()
|
||||
});
|
||||
|
||||
if (searchQuery) {
|
||||
queryParams.set('search', searchQuery);
|
||||
}
|
||||
|
||||
// Fetch emails from API
|
||||
const response = await fetch(`/api/courrier?${queryParams.toString()}`);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || 'Failed to fetch emails');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return {
|
||||
emails: Array.isArray(data.emails) ? data.emails : [],
|
||||
total: data.totalEmails || 0,
|
||||
totalPages: data.totalPages || 0,
|
||||
hasMore: data.totalPages > page
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Prefetches emails for a specific folder
|
||||
*/
|
||||
const prefetchFolderEmails = async (userId: string, folder: string, startPage: number, endPage: number) => {
|
||||
try {
|
||||
for (let p = startPage; p <= endPage; p++) {
|
||||
await getEmails(userId, folder, p);
|
||||
// Add small delay between requests
|
||||
if (p < endPage) await new Promise(r => setTimeout(r, 500));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error prefetching emails:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Update loadMoreEmails
|
||||
const loadMoreEmails = useCallback(async () => {
|
||||
if (isLoading || !hasMore || !session) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't allow loading more if we've loaded too recently
|
||||
const now = Date.now();
|
||||
const lastLoadTime = loadMoreRef.current || 0;
|
||||
if (now - lastLoadTime < 1000) { // Throttle to once per second
|
||||
console.log('Throttling loadMoreEmails - too many requests');
|
||||
return;
|
||||
}
|
||||
|
||||
// Track when we last attempted to load more
|
||||
loadMoreRef.current = now;
|
||||
|
||||
// Load the next page
|
||||
console.log(`Loading more emails for ${currentFolder}, page ${page + 1}`);
|
||||
return loadEmails(currentFolder, page + 1, false, false);
|
||||
}, [isLoading, hasMore, session, currentFolder, page, loadEmails]);
|
||||
|
||||
// Return all the functionality and state values
|
||||
return {
|
||||
// Data
|
||||
@ -537,7 +552,6 @@ export const useCourrier = () => {
|
||||
perPage,
|
||||
totalEmails,
|
||||
totalPages,
|
||||
hasMore,
|
||||
|
||||
// Functions
|
||||
loadEmails,
|
||||
@ -554,6 +568,5 @@ export const useCourrier = () => {
|
||||
setPage,
|
||||
setPerPage,
|
||||
setSearchQuery,
|
||||
loadMoreEmails,
|
||||
};
|
||||
};
|
||||
@ -19,7 +19,7 @@ export async function getCachedEmailsWithTimeout(
|
||||
folder: string,
|
||||
page: number,
|
||||
perPage: number,
|
||||
timeoutMs: number = 200
|
||||
timeoutMs: number = 100
|
||||
): Promise<any | null> {
|
||||
return new Promise((resolve) => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
@ -73,45 +73,16 @@ export async function refreshEmailsInBackground(
|
||||
page: number = 1,
|
||||
perPage: number = 20
|
||||
): Promise<void> {
|
||||
// Track ongoing refreshes to avoid duplicates
|
||||
const refreshKey = `${userId}:${folder}:${page}`;
|
||||
|
||||
// Only use small timeouts for INBOX, other folders can wait longer
|
||||
const priority = folder.toUpperCase() === 'INBOX' && page <= 2 ? 100 : 500;
|
||||
|
||||
// Use setTimeout to ensure this runs after current execution context
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
// Skip if we've recently refreshed this data (use a module-scope cache)
|
||||
// We don't need to refresh the same data too frequently
|
||||
const cacheKey = `${userId}:${folder}:${page}:refreshed`;
|
||||
const lastRefreshed = (window as any)[cacheKey] || 0;
|
||||
const now = Date.now();
|
||||
|
||||
// Don't refresh if it's been less than 30 seconds for inbox, 2 minutes for other folders
|
||||
const minInterval = folder.toUpperCase() === 'INBOX' ? 30000 : 120000;
|
||||
if (now - lastRefreshed < minInterval) {
|
||||
console.log(`Skipping refresh for ${folder}:${page} - last refreshed ${Math.round((now - lastRefreshed)/1000)}s ago`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Background refresh for ${userId}:${folder}:${page}:${perPage}`);
|
||||
const freshData = await getEmails(userId, folder, page, perPage);
|
||||
console.log(`Background refresh completed for ${userId}:${folder}:${page} with ${freshData.emails.length} emails`);
|
||||
|
||||
// Mark as refreshed
|
||||
(window as any)[cacheKey] = now;
|
||||
|
||||
// For inbox first page only, prefetch page 2 but with a longer delay
|
||||
if (folder.toUpperCase() === 'INBOX' && page === 1) {
|
||||
setTimeout(() => {
|
||||
refreshEmailsInBackground(userId, folder, 2, perPage);
|
||||
}, 1000);
|
||||
}
|
||||
console.log(`Background refresh completed for ${userId}:${folder}`);
|
||||
} catch (error) {
|
||||
console.error('Background refresh error:', error);
|
||||
}
|
||||
}, priority);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,57 +175,30 @@ export async function prefetchFolderEmails(
|
||||
try {
|
||||
console.log(`Prefetching ${pages} pages of emails for folder ${folder} starting from page ${startPage}`);
|
||||
|
||||
// Limit the number of pages to prefetch to reduce server load
|
||||
const maxPages = 3;
|
||||
const actualPages = Math.min(pages, maxPages);
|
||||
|
||||
// Calculate the range of pages to prefetch
|
||||
const pagesToFetch = Array.from(
|
||||
{ length: actualPages },
|
||||
{ length: pages },
|
||||
(_, i) => startPage + i
|
||||
);
|
||||
|
||||
console.log(`Will prefetch pages: ${pagesToFetch.join(', ')}`);
|
||||
|
||||
// Fetch pages sequentially with delays to avoid overwhelming the server
|
||||
// Focus on the first page first, which is most important
|
||||
const fetchPage = async (pageIndex: number) => {
|
||||
if (pageIndex >= pagesToFetch.length) return;
|
||||
|
||||
const page = pagesToFetch[pageIndex];
|
||||
|
||||
try {
|
||||
// Skip if we've recently prefetched this page
|
||||
const cacheKey = `${userId}:${folder}:${page}:prefetched`;
|
||||
const lastPrefetched = (window as any)[cacheKey] || 0;
|
||||
const now = Date.now();
|
||||
|
||||
// Don't prefetch if it's been less than 1 minute
|
||||
if (now - lastPrefetched < 60000) {
|
||||
console.log(`Skipping prefetch for ${folder}:${page} - prefetched ${Math.round((now - lastPrefetched)/1000)}s ago`);
|
||||
// Continue with next page
|
||||
setTimeout(() => fetchPage(pageIndex + 1), 100);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Prefetching page ${page} of ${folder}`);
|
||||
const result = await getEmails(userId, folder, page, 20);
|
||||
console.log(`Successfully prefetched page ${page} of ${folder} with ${result.emails.length} emails`);
|
||||
|
||||
// Mark as prefetched
|
||||
(window as any)[cacheKey] = now;
|
||||
|
||||
// Fetch next page with delay
|
||||
setTimeout(() => fetchPage(pageIndex + 1), 500);
|
||||
} catch (err) {
|
||||
console.error(`Error prefetching page ${page} of ${folder}:`, err);
|
||||
// Try next page anyway after a longer delay
|
||||
setTimeout(() => fetchPage(pageIndex + 1), 1000);
|
||||
}
|
||||
};
|
||||
// Fetch multiple pages in parallel
|
||||
await Promise.allSettled(
|
||||
pagesToFetch.map(page =>
|
||||
getEmails(userId, folder, page, 20)
|
||||
.then(result => {
|
||||
console.log(`Successfully prefetched and cached page ${page} of ${folder} with ${result.emails.length} emails`);
|
||||
return result;
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(`Error prefetching page ${page} of ${folder}:`, err);
|
||||
return null;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Start fetching the first page
|
||||
fetchPage(0);
|
||||
console.log(`Completed prefetching ${pages} pages of ${folder}`);
|
||||
} catch (error) {
|
||||
console.error(`Error prefetching folder ${folder}:`, error);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user