courrier multi account restore compose

This commit is contained in:
alma 2025-04-29 12:00:36 +02:00
parent 7c9535cb25
commit a9012dec69

View File

@ -91,9 +91,26 @@ export const useCourrier = () => {
setError(null); setError(null);
try { try {
// CRITICAL FIX: Extract normalized folder name for API requests
// The currentFolder should already have the account prefix in format "accountId:folder"
// We need to extract the base folder name for the API request
let normalizedFolder = currentFolder;
let effectiveAccountId = accountId || 'default';
if (currentFolder.includes(':')) {
const parts = currentFolder.split(':');
// If no explicit accountId was provided, use the one from the folder name
if (!accountId) {
effectiveAccountId = parts[0];
}
normalizedFolder = parts[1];
}
console.log(`Load emails - using normalized folder: ${normalizedFolder}, effectiveAccountId: ${effectiveAccountId}`);
// Construct query parameters // Construct query parameters
const queryParams = new URLSearchParams({ const queryParams = new URLSearchParams({
folder: currentFolder, folder: normalizedFolder, // Use normalized folder without account prefix for API
page: page.toString(), page: page.toString(),
perPage: perPage.toString() perPage: perPage.toString()
}); });
@ -102,24 +119,22 @@ export const useCourrier = () => {
queryParams.set('search', searchQuery); queryParams.set('search', searchQuery);
} }
// Add accountId to query params if provided // Always add accountId to query params
if (accountId) { queryParams.set('accountId', effectiveAccountId);
queryParams.set('accountId', accountId);
}
// Try to get cached emails first // Try to get cached emails first
const currentRequestPage = page; const currentRequestPage = page;
// FIXED: Use the correct parameter order for getCachedEmailsWithTimeout // FIXED: Use the correct parameter order and normalized values
// Function signature: (userId: string, folder: string, page: number, perPage: number, timeoutMs: number = 100, accountId?: string) // Function signature: (userId: string, folder: string, page: number, perPage: number, timeoutMs: number = 100, accountId?: string)
console.log(`Getting cached emails for user ${session.user.id}, folder ${currentFolder}, page ${currentRequestPage}, accountId ${accountId || 'default'}`); console.log(`Getting cached emails for user ${session.user.id}, folder ${currentFolder}, normalizedFolder: ${normalizedFolder}, page ${currentRequestPage}, accountId ${effectiveAccountId}`);
const cachedEmails = await getCachedEmailsWithTimeout( const cachedEmails = await getCachedEmailsWithTimeout(
session.user.id, // userId: string session.user.id, // userId: string
currentFolder, // folder: string currentFolder, // folder: string - use full prefixed folder for cache key
currentRequestPage, // page: number currentRequestPage, // page: number
perPage, // perPage: number perPage, // perPage: number
100, // timeoutMs: number 100, // timeoutMs: number
accountId // accountId?: string effectiveAccountId // accountId?: string - always pass the effective account ID
); );
if (cachedEmails) { if (cachedEmails) {
@ -184,12 +199,14 @@ export const useCourrier = () => {
setIsLoading(false); setIsLoading(false);
// Still refresh in background for fresh data // Still refresh in background for fresh data
// CRITICAL FIX: Use the same effective account ID for background refresh
console.log(`Starting background refresh with folder ${currentFolder}, account ${effectiveAccountId}`);
refreshEmailsInBackground( refreshEmailsInBackground(
session.user.id, session.user.id,
currentFolder, currentFolder,
currentRequestPage, currentRequestPage,
perPage, perPage,
accountId // Make sure accountId is passed effectiveAccountId // Always use the effective account ID
).catch(err => { ).catch(err => {
console.error('Background refresh error:', err); console.error('Background refresh error:', err);
}); });
@ -197,6 +214,7 @@ export const useCourrier = () => {
} }
// Fetch emails from API // Fetch emails from API
console.log(`Fetching emails from API with params: ${queryParams.toString()}`);
const response = await fetch(`/api/courrier?${queryParams.toString()}`); const response = await fetch(`/api/courrier?${queryParams.toString()}`);
if (!response.ok) { if (!response.ok) {
@ -206,6 +224,21 @@ export const useCourrier = () => {
const data: EmailListResult = await response.json(); const data: EmailListResult = await response.json();
// CRITICAL FIX: Ensure all emails have the proper account ID and folder format for consistent lookup
if (Array.isArray(data.emails)) {
data.emails.forEach(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}`;
}
});
}
// Update state with the fetched data // Update state with the fetched data
if (isLoadMore) { if (isLoadMore) {
setEmails(prev => { setEmails(prev => {
@ -266,6 +299,7 @@ export const useCourrier = () => {
const changeFolder = useCallback(async (folder: string, accountId?: string) => { const changeFolder = useCallback(async (folder: string, accountId?: string) => {
console.log(`Changing folder to ${folder} for account ${accountId || 'default'}`); console.log(`Changing folder to ${folder} for account ${accountId || 'default'}`);
try { try {
// CRITICAL FIX: Better folder and account ID handling
// Extract account ID from folder name if present and none was explicitly provided // Extract account ID from folder name if present and none was explicitly provided
const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId; const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId;
@ -275,14 +309,18 @@ export const useCourrier = () => {
// Normalize folder name by removing account prefix if present // Normalize folder name by removing account prefix if present
const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder; const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder;
console.log(`Folder change normalized: ${normalizedFolder}, account: ${effectiveAccountId}`); // Always create a consistently formatted folder name with account prefix
const prefixedFolder = `${effectiveAccountId}:${normalizedFolder}`;
console.log(`Folder change: original=${folder}, normalized=${normalizedFolder}, accountId=${effectiveAccountId}, prefixed=${prefixedFolder}`);
// Reset selected email // Reset selected email
setSelectedEmail(null); setSelectedEmail(null);
setSelectedEmailIds([]); setSelectedEmailIds([]);
// Record the new folder (preserving account prefix if present) // Use the consistently prefixed folder name for state
setCurrentFolder(folder); // CRITICAL FIX: Always use the properly prefixed folder name in state
setCurrentFolder(prefixedFolder);
// Reset search query when changing folders // Reset search query when changing folders
setSearchQuery(''); setSearchQuery('');
@ -301,6 +339,8 @@ export const useCourrier = () => {
await new Promise(resolve => setTimeout(resolve, 100)); await new Promise(resolve => setTimeout(resolve, 100));
// Call loadEmails with correct boolean parameter type and account ID // Call loadEmails with correct boolean parameter type and account ID
// CRITICAL FIX: Pass the properly formatted folder name to the cache lookup functions
console.log(`Loading emails for prefixed folder: ${prefixedFolder} with accountId: ${effectiveAccountId}`);
await loadEmails(false, effectiveAccountId); await loadEmails(false, effectiveAccountId);
} catch (error) { } catch (error) {
console.error(`Error changing to folder ${folder}:`, error); console.error(`Error changing to folder ${folder}:`, error);
@ -421,56 +461,170 @@ export const useCourrier = () => {
// Select an email to view // Select an email to view
const handleEmailSelect = useCallback(async (emailId: string, accountId: string, folderOverride: string) => { const handleEmailSelect = useCallback(async (emailId: string, accountId: string, folderOverride: string) => {
console.log(`Selecting email ${emailId} from account ${accountId} in folder ${folderOverride}`); // Enhanced logging for better debugging
console.log(`Selecting email ${emailId} from account [${accountId}] in folder [${folderOverride}]`);
// Skip processing if emailId is empty (used when deselecting emails)
if (!emailId) {
console.log('No email ID provided, clearing selection');
setSelectedEmail(null);
return;
}
setIsLoading(true); setIsLoading(true);
try { try {
// Normalize account ID if not provided // Normalize account ID if not provided
const effectiveAccountId = accountId || 'default'; const effectiveAccountId = accountId || 'default';
// Normalize folder name by removing account prefix if present // Normalize folder name handling - ensure consistent format
const normalizedFolder = folderOverride.includes(':') ? folderOverride.split(':')[1] : folderOverride; let normalizedFolder: string;
let prefixedFolder: string;
// Find the email in the current list if (folderOverride.includes(':')) {
const email = emails.find(e => // Extract parts if folder already has a prefix
const parts = folderOverride.split(':');
const folderAccountId = parts[0];
normalizedFolder = parts[1];
// CRITICAL FIX: Always use the provided accountId instead of the one in the folder
// This ensures we're looking in the right account when an email is clicked
prefixedFolder = `${effectiveAccountId}:${normalizedFolder}`;
if (folderAccountId !== effectiveAccountId) {
console.log(`WARNING: Folder account prefix mismatch. Folder has ${folderAccountId}, but using ${effectiveAccountId}`);
}
} else {
// No prefix, add one
normalizedFolder = folderOverride;
prefixedFolder = `${effectiveAccountId}:${normalizedFolder}`;
}
console.log(`Email selection with normalized values: folder=${normalizedFolder}, prefixed=${prefixedFolder}, accountId=${effectiveAccountId}`);
// More flexible email finding with detailed logging
console.log(`Looking for email with ID=${emailId}, account=${effectiveAccountId}, normalized folder=${normalizedFolder}, prefixed=${prefixedFolder}`);
// First, try to find by exact match with account and folder
let email = emails.find(e =>
e.id === emailId && e.id === emailId &&
(e.accountId === effectiveAccountId) && e.accountId === effectiveAccountId &&
(e.folder === normalizedFolder || e.folder === folderOverride) (
e.folder === prefixedFolder ||
e.folder === normalizedFolder ||
e.folder === folderOverride ||
// Also check for case where email folder has its own prefix but with the same normalized folder
(e.folder?.includes(':') && e.folder.split(':')[1] === normalizedFolder)
)
); );
// If not found, try to find just by ID as fallback
if (!email) { if (!email) {
console.log(`Email ${emailId} not found in current list. Fetching from API.`); console.log(`No exact match found. Looking for email just by ID=${emailId}`);
const fullEmail = await fetchEmailContent(emailId, effectiveAccountId, normalizedFolder); email = emails.find(e => e.id === emailId);
setSelectedEmail(fullEmail);
if (email) {
console.log(`Found email by ID only. Account=${email.accountId}, folder=${email.folder}`);
}
}
if (!email) {
console.log(`Email ${emailId} not found in current list (searched ${emails.length} emails). Fetching from API.`);
try {
// CRITICAL FIX: Pass both normalized folder and account ID to fetch
// Using normalized folder (without prefix) for API compatibility
console.log(`Fetching email ${emailId} directly from API with normalizedFolder=${normalizedFolder}, accountId=${effectiveAccountId}`);
const fullEmail = await fetchEmailContent(emailId, effectiveAccountId, normalizedFolder);
// Ensure the returned email has the proper accountId and prefixed folder name
if (fullEmail) {
if (!fullEmail.accountId) {
fullEmail.accountId = effectiveAccountId;
}
// Make sure folder has the proper prefix for consistent lookup
if (fullEmail.folder && !fullEmail.folder.includes(':')) {
fullEmail.folder = `${fullEmail.accountId}:${fullEmail.folder}`;
}
}
console.log(`Successfully fetched email from API:`, {
id: fullEmail.id,
account: fullEmail.accountId,
folder: fullEmail.folder
});
setSelectedEmail(fullEmail);
} catch (error) {
// Type the error properly
const fetchError = error instanceof Error ? error : new Error(String(error));
console.error(`Error fetching email from API: ${fetchError.message}`);
throw fetchError;
}
return; return;
} }
// If content is not fetched, get the full content // If content is not fetched, get the full content
if (!email.contentFetched) { if (!email.contentFetched) {
console.log(`Fetching content for email ${emailId}`); console.log(`Email found but content not fetched. Getting full content for ${emailId}`);
const fullEmail = await fetchEmailContent(emailId, effectiveAccountId, normalizedFolder); try {
// CRITICAL FIX: Extract normalized folder from email's folder if it has a prefix
// Merge the full content with the email const emailFolder = email.folder || normalizedFolder;
const updatedEmail = { let emailNormalizedFolder = emailFolder;
...email,
content: fullEmail.content, if (emailFolder.includes(':')) {
attachments: fullEmail.attachments, emailNormalizedFolder = emailFolder.split(':')[1];
contentFetched: true }
};
// Always use the email's own accountId if available
// Update the email in the list const emailAccountId = email.accountId || effectiveAccountId;
setEmails(emails.map(e => e.id === emailId ? updatedEmail : e));
setSelectedEmail(updatedEmail); console.log(`Fetching content for email ${emailId} with accountId=${emailAccountId}, folder=${emailNormalizedFolder}`);
// Use the email's own accountId and normalized folder for fetching
const fullEmail = await fetchEmailContent(
emailId,
emailAccountId,
emailNormalizedFolder
);
// Ensure the returned email has consistent format
if (fullEmail && !fullEmail.accountId) {
fullEmail.accountId = emailAccountId;
}
// Merge the full content with the email
const updatedEmail = {
...email,
accountId: emailAccountId, // Ensure account ID is preserved
folder: email.folder, // Preserve original folder name with prefix
content: fullEmail.content,
attachments: fullEmail.attachments,
contentFetched: true
};
// Update the email in the list
setEmails(emails.map(e => e.id === emailId ? updatedEmail : e));
setSelectedEmail(updatedEmail);
console.log(`Successfully updated email with content`);
} catch (error) {
// Type the error properly
const contentError = error instanceof Error ? error : new Error(String(error));
console.error(`Error fetching email content: ${contentError.message}`);
throw contentError;
}
} else { } else {
console.log(`Email found with content already fetched, selecting directly`);
setSelectedEmail(email); setSelectedEmail(email);
} }
// Mark the email as read if it's not already // Mark the email as read if it's not already
if (!email.flags.seen) { if (!email.flags.seen) {
markEmailAsRead(emailId, true); console.log(`Marking email ${emailId} as read`);
markEmailAsRead(emailId, true).catch(err => {
console.error(`Failed to mark email as read: ${err.message}`);
});
} }
} catch (err) { } catch (err) {
console.error('Error selecting email:', err); console.error(`Error selecting email: ${err instanceof Error ? err.message : String(err)}`);
toast({ toast({
variant: "destructive", variant: "destructive",
title: "Error", title: "Error",