diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx
index 5a5856dd..6ae2d10a 100644
--- a/app/courrier/page.tsx
+++ b/app/courrier/page.tsx
@@ -562,32 +562,62 @@ export default function CourrierPage() {
setShowComposeModal(true);
};
- // Update handleMailboxChange to properly handle per-account folders
+ // Update handleMailboxChange to ensure consistent folder naming and prevent race conditions
const handleMailboxChange = (folder: string, accountId?: string) => {
- if (accountId && accountId !== 'loading-account') {
- const account = accounts.find(a => a.id === accountId);
- if (!account) {
- toast({
- title: "Account not found",
- description: `The account ${accountId} could not be found.`,
- variant: "destructive",
- });
- return;
- }
- // Only allow navigation to folders in selectedAccount.folders
- if (!account.folders.includes(folder)) {
- toast({
- title: "Folder not found",
- description: `The folder ${folder} does not exist for this account.`,
- variant: "destructive",
- });
- return;
- }
- setSelectedFolders(prev => ({ ...prev, [accountId]: folder }));
- changeFolder(folder, accountId);
- } else {
- changeFolder(folder, accountId);
+ if (!accountId || accountId === 'loading-account') {
+ // Use a default behavior if no valid accountId is provided
+ console.warn('No valid accountId provided for folder change');
+ changeFolder(folder);
+ return;
}
+
+ const account = accounts.find(a => a.id === accountId);
+ if (!account) {
+ toast({
+ title: "Account not found",
+ description: `The account ${accountId} could not be found.`,
+ variant: "destructive",
+ });
+ return;
+ }
+
+ // Ensure folder has account prefix
+ const prefixedFolder = folder.includes(':') ? folder : `${accountId}:${folder}`;
+
+ // Ensure folder exists in account.folders (either in prefixed or unprefixed form)
+ const folderExists = account.folders?.some(f =>
+ f === prefixedFolder || f === folder ||
+ // Handle case where folder is base name and account folders are prefixed
+ (folder.includes(':') ? false : `${accountId}:${folder}` === f)
+ );
+
+ if (!folderExists && folder !== 'INBOX') {
+ toast({
+ title: "Folder not found",
+ description: `The folder ${folder} does not exist for this account.`,
+ variant: "destructive",
+ });
+ return;
+ }
+
+ // Update UI state first to prevent flickering
+ setSelectedAccount(account);
+
+ // Use a callback to ensure we have the latest state when updating selectedFolders
+ setSelectedFolders(prev => {
+ const updated = { ...prev, [accountId]: prefixedFolder };
+ console.log('Updated selectedFolders:', updated);
+ return updated;
+ });
+
+ // Set loading state to provide feedback
+ setLoading(true);
+
+ // Reset page when changing folders
+ setPage(1);
+
+ // Make sure we pass the prefixed folder to change folder
+ changeFolder(prefixedFolder, accountId);
};
// Update the folder button rendering to show selected state based on account
@@ -899,168 +929,4 @@ export default function CourrierPage() {
{searchQuery
? `No results found for "${searchQuery}"`
- : `Your ${currentFolder.toLowerCase()} is empty`}
-
-
-
- ) : (
- handleEmailSelect(emailId, selectedAccount?.id || '', currentFolder)}
- onToggleSelect={toggleEmailSelection}
- onToggleSelectAll={toggleSelectAll}
- onToggleStarred={toggleStarred}
- onLoadMore={handleLoadMore}
- hasMoreEmails={page < totalPages}
- currentFolder={currentFolder}
- isLoading={isLoading}
- totalEmails={emails.length}
- onBulkAction={handleBulkAction}
- />
- )}
-
-
- )}
-
-
-
- {/* Panel 3: Email Detail - Always visible */}
-
- {/* Content for Panel 3 based on state but always visible */}
-
- {selectedEmail ? (
-
{
- handleEmailSelect('', '', '');
- // Ensure sidebar stays visible
- setSidebarOpen(true);
- }}
- onReply={handleReply}
- onReplyAll={handleReplyAll}
- onForward={handleForward}
- onToggleStar={() => toggleStarred(selectedEmail.id)}
- />
- ) : (
-
-
-
Select an email to view or
-
-
-
- )}
-
-
-
-
-
-
- {/* Modals and Dialogs */}
- setShowDeleteConfirm(false)}
- />
-
- {/* Compose Email Dialog */}
-
-
- {/* Edit Password Modal */}
-
-
- {/* Delete Account Dialog */}
- { if (!open) setShowDeleteDialog(false); }}>
-
-
- Delete Account
-
- Are you sure you want to delete this account? This action cannot be undone.
-
-
-
- setShowDeleteDialog(false)}>Cancel
-
-
-
-
-
-
- >
- );
-}
\ No newline at end of file
+ : `Your ${currentFolder.toLowerCase()} is empty`
\ No newline at end of file
diff --git a/app/hooks/use-courrier.ts b/app/hooks/use-courrier.ts
new file mode 100644
index 00000000..ba95a97e
--- /dev/null
+++ b/app/hooks/use-courrier.ts
@@ -0,0 +1,105 @@
+/**
+ * Change the current folder and load emails from that folder
+ */
+const changeFolder = async (folder: string, accountId?: string) => {
+ console.log(`Changing folder to ${folder} for account ${accountId || 'default'}`);
+ try {
+ // Reset selected email
+ setSelectedEmail(null);
+ setSelectedEmailIds([]);
+
+ // Record the new folder
+ setCurrentFolder(folder);
+
+ // Reset search query when changing folders
+ setSearchQuery('');
+
+ // Reset to page 1
+ setPage(1);
+
+ // Clear existing emails before loading new ones to prevent UI flicker
+ setEmails([]);
+
+ // Show loading state
+ setIsLoading(true);
+
+ // Load emails for the new folder with a deliberate delay to allow state to update
+ await new Promise(resolve => setTimeout(resolve, 100));
+ await loadEmails(folder, 1, 20, accountId);
+ } catch (error) {
+ console.error(`Error changing to folder ${folder}:`, error);
+ setError(`Failed to load emails from ${folder}: ${error instanceof Error ? error.message : 'Unknown error'}`);
+ } finally {
+ setIsLoading(false);
+ }
+};
+
+/**
+ * Load emails for the current folder
+ */
+const loadEmails = async (
+ folderOverride?: string,
+ pageOverride?: number,
+ perPageOverride?: number,
+ accountIdOverride?: string
+) => {
+ const folderToUse = folderOverride || currentFolder;
+ const pageToUse = pageOverride || page;
+ const perPageToUse = perPageOverride || perPage;
+ const accountIdToUse = accountIdOverride !== undefined ? accountIdOverride :
+ folderToUse.includes(':') ? folderToUse.split(':')[0] : undefined;
+
+ console.log(`Loading emails: folder=${folderToUse}, page=${pageToUse}, accountId=${accountIdToUse || 'default'}`);
+
+ try {
+ setIsLoading(true);
+ setError('');
+
+ // Construct the API URL with a unique timestamp to prevent caching
+ let url = `/api/courrier/emails?folder=${encodeURIComponent(folderToUse)}&page=${pageToUse}&perPage=${perPageToUse}`;
+
+ // Add accountId parameter if specified
+ if (accountIdToUse) {
+ url += `&accountId=${encodeURIComponent(accountIdToUse)}`;
+ }
+
+ // Add cache-busting timestamp
+ url += `&_t=${Date.now()}`;
+
+ console.log(`Fetching emails from API: ${url}`);
+ const response = await fetch(url);
+
+ if (!response.ok) {
+ let errorText;
+ try {
+ const errorData = await response.json();
+ errorText = errorData.error || `Server error: ${response.status}`;
+ } catch {
+ errorText = `HTTP error: ${response.status}`;
+ }
+ throw new Error(errorText);
+ }
+
+ const data = await response.json();
+
+ if (pageOverride === 1 || !pageOverride) {
+ // Replace emails when loading first page
+ setEmails(data.emails);
+ } else {
+ // Append emails when loading subsequent pages
+ setEmails(prev => [...prev, ...data.emails]);
+ }
+
+ // Update pagination info
+ setTotalPages(data.totalPages);
+ setMailboxes(data.mailboxes || []);
+
+ return data;
+ } catch (error) {
+ console.error('Error loading emails:', error);
+ setError(`Failed to load emails: ${error instanceof Error ? error.message : 'Unknown error'}`);
+ return null;
+ } finally {
+ setIsLoading(false);
+ }
+};
\ No newline at end of file
diff --git a/components/email/EmailSidebar.tsx b/components/email/EmailSidebar.tsx
index 80055b2b..d34ca6ea 100644
--- a/components/email/EmailSidebar.tsx
+++ b/components/email/EmailSidebar.tsx
@@ -140,18 +140,20 @@ export default function EmailSidebar({
return folder.charAt(0).toUpperCase() + folder.slice(1).toLowerCase();
};
- // Render folder button with exact same styling as in courrier page
+ // Improve the renderFolderButton function to ensure consistent handling
const renderFolderButton = (folder: string, accountId: string) => {
- // Get the account prefix from the folder name
- const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId;
+ // Ensure folder always has accountId prefix for consistency
+ const prefixedFolder = folder.includes(':') ? folder : `${accountId}:${folder}`;
+
+ // Extract the base folder name and account ID for display and checking
+ const [folderAccountId, baseFolder] = prefixedFolder.includes(':')
+ ? prefixedFolder.split(':')
+ : [accountId, folder];
// Only show folders that belong to this account
if (folderAccountId !== accountId) return null;
- const isSelected = selectedFolders[accountId] === folder;
-
- // Get the base folder name for display
- const baseFolder = folder.includes(':') ? folder.split(':')[1] : folder;
+ const isSelected = selectedFolders[accountId] === prefixedFolder;
return (