courrier preview

This commit is contained in:
alma 2025-05-01 17:32:55 +02:00
parent 0d4fc59904
commit e8989f2016

View File

@ -155,194 +155,407 @@ export const useEmailState = () => {
dispatch({ type: 'SET_LOADING', payload: true });
try {
// 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: ${page})`);
// Construct query parameters
const queryParams = new URLSearchParams({
folder: normalizedFolder,
page: page.toString(),
perPage: perPage.toString(),
accountId: effectiveAccountId
});
// Debug log existing emails count
if (isLoadMore) {
console.log(`[DEBUG-PAGINATION] Loading more emails. Current page: ${page}, existing emails: ${state.emails.length}`);
// CRITICAL FIX: Add more robust validation to prevent "toString of undefined" error
if (!state.currentFolder) {
logEmailOp('ERROR', 'Current folder is undefined, cannot load emails');
dispatch({
type: 'SET_ERROR',
payload: 'Invalid folder configuration'
});
dispatch({ type: 'SET_LOADING', payload: false });
return;
}
// Try to get cached emails first
logEmailOp('CACHE_CHECK', `Checking cache for ${prefixedFolder}, page: ${page}`);
const cachedEmails = await getCachedEmailsWithTimeout(
session.user.id,
prefixedFolder,
page,
perPage,
100,
effectiveAccountId
);
// Get normalized parameters using helper function with proper account ID handling
const accountId = state.selectedAccount ? state.selectedAccount.id : undefined;
if (cachedEmails) {
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)) {
// CRITICAL FIX: Double check we're using the right action type based on isLoadMore param
console.log(`[DEBUG-CACHE_HIT] Dispatching ${isLoadMore ? 'APPEND_EMAILS' : 'SET_EMAILS'} with ${cachedEmails.emails.length} emails`);
// Additional validation for accountId
if (accountId === undefined && state.currentFolder.includes(':')) {
// Try to extract accountId from folder string as fallback
const extractedAccountId = state.currentFolder.split(':')[0];
if (extractedAccountId) {
console.log(`[DEBUG-LOAD_EMAILS] Using extracted accountId ${extractedAccountId} from folder path as fallback`);
const { normalizedFolder, effectiveAccountId, prefixedFolder } =
normalizeFolderAndAccount(state.currentFolder, extractedAccountId);
logEmailOp('LOAD_EMAILS', `Loading emails for ${prefixedFolder} (account: ${effectiveAccountId}, isLoadMore: ${isLoadMore}, page: ${page})`);
// Dispatch appropriate action based on if we're loading more - DO NOT OVERRIDE isLoadMore!
dispatch({
type: isLoadMore ? 'APPEND_EMAILS' : 'SET_EMAILS',
payload: cachedEmails.emails
// Continue with the extracted account ID...
// Construct query parameters
const queryParams = new URLSearchParams({
folder: normalizedFolder,
page: page.toString(),
perPage: perPage.toString(),
accountId: effectiveAccountId
});
// Set pagination info from cache if available
if (cachedEmails.totalEmails) {
dispatch({ type: 'SET_TOTAL_EMAILS', payload: cachedEmails.totalEmails });
// Debug log existing emails count
if (isLoadMore) {
console.log(`[DEBUG-PAGINATION] Loading more emails. Current page: ${page}, existing emails: ${state.emails.length}`);
}
if (cachedEmails.totalPages) {
dispatch({ type: 'SET_TOTAL_PAGES', payload: cachedEmails.totalPages });
// Try to get cached emails first
logEmailOp('CACHE_CHECK', `Checking cache for ${prefixedFolder}, page: ${page}`);
const cachedEmails = await getCachedEmailsWithTimeout(
session.user.id,
prefixedFolder,
page,
perPage,
100,
effectiveAccountId
);
if (cachedEmails) {
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)) {
// CRITICAL FIX: Double check we're using the right action type based on isLoadMore param
console.log(`[DEBUG-CACHE_HIT] Dispatching ${isLoadMore ? 'APPEND_EMAILS' : 'SET_EMAILS'} with ${cachedEmails.emails.length} emails`);
// Dispatch appropriate action based on if we're loading more - DO NOT OVERRIDE isLoadMore!
dispatch({
type: isLoadMore ? 'APPEND_EMAILS' : 'SET_EMAILS',
payload: cachedEmails.emails
});
// Set pagination info from cache if available
if (cachedEmails.totalEmails) {
dispatch({ type: 'SET_TOTAL_EMAILS', payload: cachedEmails.totalEmails });
}
if (cachedEmails.totalPages) {
dispatch({ type: 'SET_TOTAL_PAGES', payload: cachedEmails.totalPages });
}
// Update available mailboxes if provided
if (cachedEmails.mailboxes && cachedEmails.mailboxes.length > 0) {
dispatch({ type: 'SET_MAILBOXES', payload: cachedEmails.mailboxes });
}
}
// CRITICAL FIX: If this was a loadMore operation, check the result after the dispatch
if (isLoadMore) {
setTimeout(() => {
console.log(`[DEBUG-CACHE_HIT_APPEND] After ${isLoadMore ? 'APPEND' : 'SET'}, email count is now: ${state.emails.length}`);
}, 0);
}
return;
}
// Fetch emails from API if no cache hit
logEmailOp('API_FETCH', `Fetching emails from API: ${queryParams.toString()}, isLoadMore: ${isLoadMore}`);
console.log(`[DEBUG-API_FETCH] Fetching from /api/courrier?${queryParams.toString()}`);
const response = await fetch(`/api/courrier?${queryParams.toString()}`);
if (!response.ok) {
// CRITICAL FIX: Try to recover from fetch errors by retrying with different pagination
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: page - 1 });
dispatch({ type: 'SET_LOADING', payload: false });
// Also reset total pages to try again
dispatch({ type: 'SET_TOTAL_PAGES', payload: page });
return;
}
const errorData = await response.json();
throw new Error(errorData.error || 'Failed to fetch emails');
}
const data = await response.json();
console.log(`[DEBUG-API_RESPONSE] Got response with ${data.emails?.length || 0} emails, totalPages: ${data.totalPages}, totalEmails: ${data.totalEmails}, isLoadMore: ${isLoadMore}`);
// CRITICAL FIX: Enhanced empty results handling
if (!data.emails || data.emails.length === 0) {
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 (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;
}
// If we're already at page 1, just update the state with no emails
if (!isLoadMore) {
logEmailOp('EMPTY_RESULTS', `No emails found in ${state.currentFolder}`);
dispatch({ type: 'SET_EMAILS', payload: [] });
dispatch({ type: 'SET_TOTAL_EMAILS', payload: 0 });
dispatch({ type: 'SET_TOTAL_PAGES', payload: 0 });
} else {
// For load more, just set loading to false but keep existing emails
dispatch({ type: 'SET_LOADING', payload: false });
}
return;
}
// Ensure all emails have proper account ID and folder format
if (Array.isArray(data.emails)) {
// Log email dates for debugging
if (data.emails.length > 0) {
logEmailOp('EMAIL_DATES', `First few email dates before processing:`,
data.emails.slice(0, 5).map((e: any) => ({
id: e.id.substring(0, 8),
subject: e.subject?.substring(0, 20),
date: e.date,
dateObj: new Date(e.date),
timestamp: new Date(e.date).getTime()
}))
);
}
data.emails.forEach((email: Email) => {
// If email doesn't have an accountId, set it to the effective one
if (!email.accountId) {
email.accountId = effectiveAccountId;
}
// Ensure folder has the proper prefix format
if (email.folder && !email.folder.includes(':')) {
email.folder = `${email.accountId}:${email.folder}`;
}
// Ensure date is a valid Date object (handle strings or timestamps)
if (email.date && !(email.date instanceof Date)) {
try {
// Convert to a proper Date object if it's a string or number
const dateObj = new Date(email.date);
// Verify it's a valid date
if (!isNaN(dateObj.getTime())) {
email.date = dateObj;
}
} catch (err) {
// If conversion fails, log and use current date as fallback
console.error(`Invalid date format for email ${email.id}: ${email.date}`);
email.date = new Date();
}
}
});
}
// CRITICAL FIX: Log what we're about to do
console.log(`[DEBUG-DISPATCH] About to dispatch ${isLoadMore ? 'APPEND_EMAILS' : 'SET_EMAILS'} with ${data.emails?.length || 0} emails`);
// Update state with fetched data
dispatch({
type: isLoadMore ? 'APPEND_EMAILS' : 'SET_EMAILS',
payload: Array.isArray(data.emails) ? data.emails : []
});
// Double-check that we've updated the email list correctly after dispatch
setTimeout(() => {
console.log(`[DEBUG-AFTER-DISPATCH] Email count is now: ${state.emails.length}, should include the ${data.emails?.length || 0} new emails we just loaded`);
}, 0);
if (data.totalEmails) {
dispatch({ type: 'SET_TOTAL_EMAILS', payload: data.totalEmails });
}
if (data.totalPages) {
dispatch({ type: 'SET_TOTAL_PAGES', payload: data.totalPages });
}
// Update available mailboxes if provided
if (cachedEmails.mailboxes && cachedEmails.mailboxes.length > 0) {
dispatch({ type: 'SET_MAILBOXES', payload: cachedEmails.mailboxes });
if (data.mailboxes && data.mailboxes.length > 0) {
dispatch({ type: 'SET_MAILBOXES', payload: data.mailboxes });
}
}
// CRITICAL FIX: If this was a loadMore operation, check the result after the dispatch
if (isLoadMore) {
setTimeout(() => {
console.log(`[DEBUG-CACHE_HIT_APPEND] After ${isLoadMore ? 'APPEND' : 'SET'}, email count is now: ${state.emails.length}`);
}, 0);
}
return;
}
// Fetch emails from API if no cache hit
logEmailOp('API_FETCH', `Fetching emails from API: ${queryParams.toString()}, isLoadMore: ${isLoadMore}`);
console.log(`[DEBUG-API_FETCH] Fetching from /api/courrier?${queryParams.toString()}`);
const response = await fetch(`/api/courrier?${queryParams.toString()}`);
if (!response.ok) {
// CRITICAL FIX: Try to recover from fetch errors by retrying with different pagination
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: page - 1 });
dispatch({ type: 'SET_LOADING', payload: false });
// Also reset total pages to try again
dispatch({ type: 'SET_TOTAL_PAGES', payload: page });
return;
}
const errorData = await response.json();
throw new Error(errorData.error || 'Failed to fetch emails');
}
const data = await response.json();
console.log(`[DEBUG-API_RESPONSE] Got response with ${data.emails?.length || 0} emails, totalPages: ${data.totalPages}, totalEmails: ${data.totalEmails}, isLoadMore: ${isLoadMore}`);
// CRITICAL FIX: Enhanced empty results handling
if (!data.emails || data.emails.length === 0) {
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 (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;
}
// If we're already at page 1, just update the state with no emails
if (!isLoadMore) {
logEmailOp('EMPTY_RESULTS', `No emails found in ${state.currentFolder}`);
dispatch({ type: 'SET_EMAILS', payload: [] });
dispatch({ type: 'SET_TOTAL_EMAILS', payload: 0 });
dispatch({ type: 'SET_TOTAL_PAGES', payload: 0 });
} else {
// For load more, just set loading to false but keep existing emails
dispatch({ type: 'SET_LOADING', payload: false });
// If we can't extract a valid accountId, throw an error
throw new Error("Cannot determine account ID for loading emails");
}
return;
}
// Ensure all emails have proper account ID and folder format
if (Array.isArray(data.emails)) {
// Log email dates for debugging
if (data.emails.length > 0) {
logEmailOp('EMAIL_DATES', `First few email dates before processing:`,
data.emails.slice(0, 5).map((e: any) => ({
id: e.id.substring(0, 8),
subject: e.subject?.substring(0, 20),
date: e.date,
dateObj: new Date(e.date),
timestamp: new Date(e.date).getTime()
}))
);
} else {
// Normal flow with valid accountId
const { normalizedFolder, effectiveAccountId, prefixedFolder } =
normalizeFolderAndAccount(state.currentFolder, accountId);
logEmailOp('LOAD_EMAILS', `Loading emails for ${prefixedFolder} (account: ${effectiveAccountId}, isLoadMore: ${isLoadMore}, page: ${page})`);
// Construct query parameters
const queryParams = new URLSearchParams({
folder: normalizedFolder,
page: page.toString(),
perPage: perPage.toString(),
accountId: effectiveAccountId
});
// Debug log existing emails count
if (isLoadMore) {
console.log(`[DEBUG-PAGINATION] Loading more emails. Current page: ${page}, existing emails: ${state.emails.length}`);
}
data.emails.forEach((email: Email) => {
// If email doesn't have an accountId, set it to the effective one
if (!email.accountId) {
email.accountId = effectiveAccountId;
}
// Try to get cached emails first
logEmailOp('CACHE_CHECK', `Checking cache for ${prefixedFolder}, page: ${page}`);
const cachedEmails = await getCachedEmailsWithTimeout(
session.user.id,
prefixedFolder,
page,
perPage,
100,
effectiveAccountId
);
if (cachedEmails) {
logEmailOp('CACHE_HIT', `Using cached data for ${prefixedFolder}, page: ${page}, emails: ${cachedEmails.emails?.length || 0}, isLoadMore: ${isLoadMore}`);
// Ensure folder has the proper prefix format
if (email.folder && !email.folder.includes(':')) {
email.folder = `${email.accountId}:${email.folder}`;
}
// Ensure date is a valid Date object (handle strings or timestamps)
if (email.date && !(email.date instanceof Date)) {
try {
// Convert to a proper Date object if it's a string or number
const dateObj = new Date(email.date);
// Verify it's a valid date
if (!isNaN(dateObj.getTime())) {
email.date = dateObj;
}
} catch (err) {
// If conversion fails, log and use current date as fallback
console.error(`Invalid date format for email ${email.id}: ${email.date}`);
email.date = new Date();
// Ensure cached data has emails array property
if (Array.isArray(cachedEmails.emails)) {
// CRITICAL FIX: Double check we're using the right action type based on isLoadMore param
console.log(`[DEBUG-CACHE_HIT] Dispatching ${isLoadMore ? 'APPEND_EMAILS' : 'SET_EMAILS'} with ${cachedEmails.emails.length} emails`);
// Dispatch appropriate action based on if we're loading more - DO NOT OVERRIDE isLoadMore!
dispatch({
type: isLoadMore ? 'APPEND_EMAILS' : 'SET_EMAILS',
payload: cachedEmails.emails
});
// Set pagination info from cache if available
if (cachedEmails.totalEmails) {
dispatch({ type: 'SET_TOTAL_EMAILS', payload: cachedEmails.totalEmails });
}
if (cachedEmails.totalPages) {
dispatch({ type: 'SET_TOTAL_PAGES', payload: cachedEmails.totalPages });
}
// Update available mailboxes if provided
if (cachedEmails.mailboxes && cachedEmails.mailboxes.length > 0) {
dispatch({ type: 'SET_MAILBOXES', payload: cachedEmails.mailboxes });
}
}
// CRITICAL FIX: If this was a loadMore operation, check the result after the dispatch
if (isLoadMore) {
setTimeout(() => {
console.log(`[DEBUG-CACHE_HIT_APPEND] After ${isLoadMore ? 'APPEND' : 'SET'}, email count is now: ${state.emails.length}`);
}, 0);
}
return;
}
// Fetch emails from API if no cache hit
logEmailOp('API_FETCH', `Fetching emails from API: ${queryParams.toString()}, isLoadMore: ${isLoadMore}`);
console.log(`[DEBUG-API_FETCH] Fetching from /api/courrier?${queryParams.toString()}`);
const response = await fetch(`/api/courrier?${queryParams.toString()}`);
if (!response.ok) {
// CRITICAL FIX: Try to recover from fetch errors by retrying with different pagination
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: page - 1 });
dispatch({ type: 'SET_LOADING', payload: false });
// Also reset total pages to try again
dispatch({ type: 'SET_TOTAL_PAGES', payload: page });
return;
}
const errorData = await response.json();
throw new Error(errorData.error || 'Failed to fetch emails');
}
const data = await response.json();
console.log(`[DEBUG-API_RESPONSE] Got response with ${data.emails?.length || 0} emails, totalPages: ${data.totalPages}, totalEmails: ${data.totalEmails}, isLoadMore: ${isLoadMore}`);
// CRITICAL FIX: Enhanced empty results handling
if (!data.emails || data.emails.length === 0) {
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 (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;
}
// If we're already at page 1, just update the state with no emails
if (!isLoadMore) {
logEmailOp('EMPTY_RESULTS', `No emails found in ${state.currentFolder}`);
dispatch({ type: 'SET_EMAILS', payload: [] });
dispatch({ type: 'SET_TOTAL_EMAILS', payload: 0 });
dispatch({ type: 'SET_TOTAL_PAGES', payload: 0 });
} else {
// For load more, just set loading to false but keep existing emails
dispatch({ type: 'SET_LOADING', payload: false });
}
return;
}
// Ensure all emails have proper account ID and folder format
if (Array.isArray(data.emails)) {
// Log email dates for debugging
if (data.emails.length > 0) {
logEmailOp('EMAIL_DATES', `First few email dates before processing:`,
data.emails.slice(0, 5).map((e: any) => ({
id: e.id.substring(0, 8),
subject: e.subject?.substring(0, 20),
date: e.date,
dateObj: new Date(e.date),
timestamp: new Date(e.date).getTime()
}))
);
}
data.emails.forEach((email: Email) => {
// If email doesn't have an accountId, set it to the effective one
if (!email.accountId) {
email.accountId = effectiveAccountId;
}
// Ensure folder has the proper prefix format
if (email.folder && !email.folder.includes(':')) {
email.folder = `${email.accountId}:${email.folder}`;
}
// Ensure date is a valid Date object (handle strings or timestamps)
if (email.date && !(email.date instanceof Date)) {
try {
// Convert to a proper Date object if it's a string or number
const dateObj = new Date(email.date);
// Verify it's a valid date
if (!isNaN(dateObj.getTime())) {
email.date = dateObj;
}
} catch (err) {
// If conversion fails, log and use current date as fallback
console.error(`Invalid date format for email ${email.id}: ${email.date}`);
email.date = new Date();
}
}
});
}
// CRITICAL FIX: Log what we're about to do
console.log(`[DEBUG-DISPATCH] About to dispatch ${isLoadMore ? 'APPEND_EMAILS' : 'SET_EMAILS'} with ${data.emails?.length || 0} emails`);
// Update state with fetched data
dispatch({
type: isLoadMore ? 'APPEND_EMAILS' : 'SET_EMAILS',
payload: Array.isArray(data.emails) ? data.emails : []
});
}
// CRITICAL FIX: Log what we're about to do
console.log(`[DEBUG-DISPATCH] About to dispatch ${isLoadMore ? 'APPEND_EMAILS' : 'SET_EMAILS'} with ${data.emails?.length || 0} emails`);
// Update state with fetched data
dispatch({
type: isLoadMore ? 'APPEND_EMAILS' : 'SET_EMAILS',
payload: Array.isArray(data.emails) ? data.emails : []
});
// Double-check that we've updated the email list correctly after dispatch
setTimeout(() => {
console.log(`[DEBUG-AFTER-DISPATCH] Email count is now: ${state.emails.length}, should include the ${data.emails?.length || 0} new emails we just loaded`);
}, 0);
if (data.totalEmails) {
dispatch({ type: 'SET_TOTAL_EMAILS', payload: data.totalEmails });
}
if (data.totalPages) {
dispatch({ type: 'SET_TOTAL_PAGES', payload: data.totalPages });
}
// Update available mailboxes if provided
if (data.mailboxes && data.mailboxes.length > 0) {
dispatch({ type: 'SET_MAILBOXES', payload: data.mailboxes });
// Double-check that we've updated the email list correctly after dispatch
setTimeout(() => {
console.log(`[DEBUG-AFTER-DISPATCH] Email count is now: ${state.emails.length}, should include the ${data.emails?.length || 0} new emails we just loaded`);
}, 0);
if (data.totalEmails) {
dispatch({ type: 'SET_TOTAL_EMAILS', payload: data.totalEmails });
}
if (data.totalPages) {
dispatch({ type: 'SET_TOTAL_PAGES', payload: data.totalPages });
}
// Update available mailboxes if provided
if (data.mailboxes && data.mailboxes.length > 0) {
dispatch({ type: 'SET_MAILBOXES', payload: data.mailboxes });
}
}
} catch (err) {
logEmailOp('ERROR', `Failed to load emails: ${err instanceof Error ? err.message : String(err)}`);