'use client';
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useSession } from 'next-auth/react';
import {
Mail, Loader2, AlertCircle,
MoreVertical, Settings, Plus as PlusIcon, Trash2, Edit,
Inbox, Send, Star, Trash, Plus, ChevronLeft, ChevronRight,
Search, ChevronDown, Folder, ChevronUp, Reply, Forward, ReplyAll,
MoreHorizontal, FolderOpen, X, Paperclip, MessageSquare, Copy, EyeOff,
AlertOctagon, Archive, RefreshCw
} from 'lucide-react';
import { Dialog, DialogContent } from '@/components/ui/dialog';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { ScrollArea } from '@/components/ui/scroll-area';
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
import { Checkbox } from '@/components/ui/checkbox';
import { Label } from '@/components/ui/label';
import { toast } from '@/components/ui/use-toast';
// Import components
import EmailSidebar from '@/components/email/EmailSidebar';
import EmailList from '@/components/email/EmailList';
import EmailSidebarContent from '@/components/email/EmailSidebarContent';
import EmailDetailView from '@/components/email/EmailDetailView';
import ComposeEmail from '@/components/email/ComposeEmail';
import { DeleteConfirmDialog, LoginNeededAlert } from '@/components/email/EmailDialogs';
// Import the custom hook
import { useCourrier, EmailData } from '@/hooks/use-courrier';
// Import the prefetching function
import { prefetchFolderEmails } from '@/lib/services/prefetch-service';
// Import the RedisCacheStatus component
import { RedisCacheStatus } from '@/components/debug/RedisCacheStatus';
// Simplified version for this component
function SimplifiedLoadingFix() {
// In production, don't render anything
if (process.env.NODE_ENV === 'production') {
return null;
}
// Simple debugging component
return (
Debug: Email app loaded
);
}
interface SidebarAccount {
id: number | string;
name: string;
email: string;
color: string;
folders?: string[];
}
export default function CourrierPage() {
const router = useRouter();
const { data: session } = useSession();
// Get all the email functionality from the hook
const {
emails = [],
selectedEmail,
selectedEmailIds,
currentFolder,
mailboxes,
isLoading,
isSending,
error,
searchQuery,
page,
totalPages,
loadEmails,
handleEmailSelect,
markEmailAsRead,
toggleStarred,
sendEmail,
deleteEmails,
toggleEmailSelection,
toggleSelectAll,
changeFolder,
searchEmails,
formatEmailForAction,
setPage,
} = useCourrier();
// UI state
const [showComposeModal, setShowComposeModal] = useState(false);
const [composeType, setComposeType] = useState<'new' | 'reply' | 'reply-all' | 'forward'>('new');
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const [showLoginNeeded, setShowLoginNeeded] = useState(false);
const [sidebarOpen, setSidebarOpen] = useState(true);
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
const [accountsDropdownOpen, setAccountsDropdownOpen] = useState(true);
const [currentView, setCurrentView] = useState('INBOX');
const [unreadCount, setUnreadCount] = useState(0);
const [loading, setLoading] = useState(false);
const [prefetchStarted, setPrefetchStarted] = useState(false);
const [showFolders, setShowFolders] = useState(true);
const [showAddAccountForm, setShowAddAccountForm] = useState(false);
// Email accounts for the sidebar
const [accounts, setAccounts] = useState([
{ id: 0, name: 'All', email: '', color: 'bg-gray-500' },
{ id: 1, name: 'Loading...', email: '', color: 'bg-blue-500', folders: mailboxes }
]);
const [selectedAccount, setSelectedAccount] = useState(null);
// Update account folders when mailboxes change
useEffect(() => {
console.log('Mailboxes updated:', mailboxes);
setAccounts(prev => {
const updated = [...prev];
if (updated[1]) {
updated[1].folders = mailboxes;
}
console.log('Updated accounts with new mailboxes:', updated);
return updated;
});
}, [mailboxes]);
// Debug accounts state
useEffect(() => {
console.log('Current accounts state:', accounts);
}, [accounts]);
// Calculate unread count (this would be replaced with actual data in production)
useEffect(() => {
// Example: counting unread emails in the inbox
const unreadInInbox = (emails || []).filter(email => {
// Access the 'read' property safely, handling both old and new email formats
// Use type assertion to avoid TypeScript errors
const emailAny = email as any;
return (!emailAny.read && emailAny.read !== undefined) ||
(emailAny.flags && !emailAny.flags.seen) ||
false;
}).filter(email => currentFolder === 'INBOX').length;
setUnreadCount(unreadInInbox);
}, [emails, currentFolder]);
// Ensure accounts section is never empty
useEffect(() => {
// If accounts array becomes empty (bug), restore default accounts
if (!accounts || accounts.length === 0) {
console.warn('Accounts array is empty, restoring defaults');
setAccounts([
{ id: 0, name: 'All', email: '', color: 'bg-gray-500' },
{ id: 1, name: 'Loading...', email: '', color: 'bg-blue-500', folders: mailboxes }
]);
}
}, [accounts, mailboxes]);
// Initialize session and start prefetching
useEffect(() => {
// Flag to prevent multiple initialization attempts
let isMounted = true;
let initAttempted = false;
const initSession = async () => {
if (initAttempted) return;
initAttempted = true;
try {
setLoading(true);
// First check if Redis is ready before making API calls
const redisStatus = await fetch('/api/redis/status')
.then(res => res.json())
.catch(() => ({ ready: false }));
if (!isMounted) return;
// Call the session API to check email credentials and start prefetching
const response = await fetch('/api/courrier/session');
const data = await response.json();
console.log('[DEBUG] Session API response:', {
authenticated: data.authenticated,
hasEmailCredentials: data.hasEmailCredentials,
email: data.email,
allAccountsExists: !!data.allAccounts,
allAccountsIsArray: Array.isArray(data.allAccounts),
allAccountsLength: data.allAccounts?.length || 0,
mailboxesExists: !!data.mailboxes,
mailboxesIsArray: Array.isArray(data.mailboxes),
mailboxesLength: data.mailboxes?.length || 0
});
if (!isMounted) return;
if (data.authenticated) {
if (data.hasEmailCredentials) {
console.log('Session initialized, prefetch status:', data.prefetchStarted ? 'running' : 'not started');
setPrefetchStarted(Boolean(data.prefetchStarted));
// Update accounts with the default email as fallback
const updatedAccounts = [
{ id: 0, name: 'All', email: '', color: 'bg-gray-500' }
];
// Check if we have multiple accounts returned
if (data.allAccounts && Array.isArray(data.allAccounts) && data.allAccounts.length > 0) {
console.log('Multiple accounts found:', data.allAccounts.length);
// Add each account from the server
data.allAccounts.forEach((account: { id?: string | number, email: string, display_name?: string, color?: string }, index: number) => {
console.log(`[DEBUG] Processing account: ${account.email}, display_name: ${account.display_name}, has folders: ${!!data.mailboxes}`);
const accountWithFolders = {
id: account.id || index + 1,
name: account.display_name || account.email,
email: account.email,
color: account.color || 'bg-blue-500',
folders: data.mailboxes || []
};
console.log(`[DEBUG] Adding account with ${accountWithFolders.folders.length} folders:`, accountWithFolders.folders);
updatedAccounts.push(accountWithFolders);
});
} else if (data.email) {
// Fallback to single account if allAccounts is not available
console.log(`[DEBUG] Fallback to single account: ${data.email}`);
updatedAccounts.push({
id: 1,
name: data.email,
email: data.email,
color: 'bg-blue-500',
folders: data.mailboxes || []
});
}
console.log('Setting accounts:', updatedAccounts);
setAccounts(updatedAccounts);
// Preload first page of emails for faster initial rendering
if (session?.user?.id) {
await loadEmails();
// If the user hasn't opened this page recently, trigger a background refresh
if (data.lastVisit && Date.now() - data.lastVisit > 5 * 60 * 1000) {
// It's been more than 5 minutes, refresh in background
try {
const refreshResponse = await fetch('/api/courrier/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ folder: currentFolder })
});
console.log('Background refresh triggered');
} catch (error) {
console.error('Failed to trigger background refresh', error);
}
}
}
} else {
// User is authenticated but doesn't have email credentials
setShowLoginNeeded(true);
}
}
} catch (error) {
console.error('Error initializing session:', error);
} finally {
if (isMounted) {
setLoading(false);
}
}
};
if (session?.user?.id) {
initSession();
}
return () => {
isMounted = false;
};
}, [session?.user?.id, loadEmails]);
// Helper to get folder icons
const getFolderIcon = (folder: string) => {
const folderLower = folder.toLowerCase();
if (folderLower.includes('inbox')) {
return Inbox;
} else if (folderLower.includes('sent')) {
return Send;
} else if (folderLower.includes('trash')) {
return Trash;
} else if (folderLower.includes('archive')) {
return Archive;
} else if (folderLower.includes('draft')) {
return Edit;
} else if (folderLower.includes('spam') || folderLower.includes('junk')) {
return AlertOctagon;
} else {
return Folder;
}
};
// Helper to format folder names
const formatFolderName = (folder: string) => {
return folder.charAt(0).toUpperCase() + folder.slice(1).toLowerCase();
};
// Check for more emails
const hasMoreEmails = page < totalPages;
// Handle loading more emails on scroll
const handleLoadMore = () => {
if (hasMoreEmails && !isLoading) {
// Increment the page
const nextPage = page + 1;
setPage(nextPage);
// Also prefetch additional pages to make scrolling smoother
if (session?.user?.id) {
// 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
}
};
// Handle bulk actions
const handleBulkAction = async (action: 'delete' | 'mark-read' | 'mark-unread' | 'archive') => {
if (selectedEmailIds.length === 0) return;
switch (action) {
case 'delete':
setShowDeleteConfirm(true);
break;
case 'mark-read':
// Mark all selected emails as read
for (const emailId of selectedEmailIds) {
await markEmailAsRead(emailId, true);
}
break;
case 'mark-unread':
// Mark all selected emails as unread
for (const emailId of selectedEmailIds) {
await markEmailAsRead(emailId, false);
}
break;
case 'archive':
// Archive functionality would be implemented here
break;
}
};
// Handle email actions
const handleReply = () => {
if (!selectedEmail) return;
setComposeType('reply');
setShowComposeModal(true);
};
const handleReplyAll = () => {
if (!selectedEmail) return;
setComposeType('reply-all');
setShowComposeModal(true);
};
const handleForward = () => {
if (!selectedEmail) return;
setComposeType('forward');
setShowComposeModal(true);
};
const handleComposeNew = () => {
setComposeType('new');
setShowComposeModal(true);
};
// Handle mailbox change with prefetching
const handleMailboxChange = (folder: string) => {
// Reset to page 1 when changing folders
setPage(1);
// Change folder in the state
changeFolder(folder);
setCurrentView(folder);
// Start prefetching additional pages for this folder
if (session?.user?.id && folder) {
// First two pages are most important - prefetch immediately
prefetchFolderEmails(session.user.id, folder, 3).catch(err => {
console.error(`Error prefetching ${folder}:`, err);
});
}
};
// Handle sending email
const handleSendEmail = async (emailData: EmailData) => {
return await sendEmail(emailData);
};
// Handle delete confirmation
const handleDeleteConfirm = async () => {
await deleteEmails(selectedEmailIds);
setShowDeleteConfirm(false);
};
// Check login on mount
useEffect(() => {
// Check if the user is logged in after a short delay
const timer = setTimeout(() => {
if (error?.includes('Not authenticated') || error?.includes('No email credentials found')) {
setShowLoginNeeded(true);
}
}, 2000);
return () => clearTimeout(timer);
}, [error]);
// Go to login page
const handleGoToLogin = () => {
router.push('/courrier/login');
};
return (
<>
{/* Main layout */}
{/* Sidebar */}
{/* Courrier Title */}
COURRIER
{/* Compose button and refresh button */}
{/* Scrollable area for accounts and folders */}
{/* Accounts Section */}
{/* Form for adding a new account */}
{showAddAccountForm && (
Add IMAP Account
)}
{accountsDropdownOpen && (
{accounts.map(account => (
{/* Show folders for this account if it's selected and folders are shown */}
{selectedAccount?.id === account.id && showFolders && account.folders && (