diff --git a/app/hooks/use-courrier.ts b/app/hooks/use-courrier.ts index a51fa4ea..93f804b8 100644 --- a/app/hooks/use-courrier.ts +++ b/app/hooks/use-courrier.ts @@ -1,136 +1,227 @@ -/** - * 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); - } -}; +import { useState } from 'react'; -/** - * Load emails for the current folder - */ -const loadEmails = async ( - folderOverride?: string, - pageOverride?: number, - perPageOverride?: number, - accountIdOverride?: string -) => { - const folderToUse = folderOverride || currentFolder; +export type MailFolder = string; + +export interface Email { + id: string; + accountId?: string; + folder?: string; + subject: string; + from: string; + to: string; + date: string; + flags: { + seen: boolean; + flagged: boolean; + answered: boolean; + draft: boolean; + }; + body?: string; + bodyHtml?: string; +} + +export interface EmailData { + to: string; + cc?: string; + bcc?: string; + subject: string; + body: string; + attachments?: Array<{ + name: string; + content: string; + type: string; + }>; +} + +// Hook for managing email operations +export const useCourrier = () => { + // State for email data + const [emails, setEmails] = useState([]); + const [selectedEmail, setSelectedEmail] = useState(null); + const [selectedEmailIds, setSelectedEmailIds] = useState([]); + const [currentFolder, setCurrentFolder] = useState('INBOX'); + const [mailboxes, setMailboxes] = useState([]); - // Enhanced folder and account handling - let normalizedFolder = folderToUse; - let normalizedAccountId = accountIdOverride; + // State for UI + const [isLoading, setIsLoading] = useState(false); + const [isSending, setIsSending] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); + const [error, setError] = useState(null); + const [searchQuery, setSearchQuery] = useState(''); + const [page, setPage] = useState(1); + const [perPage, setPerPage] = useState(20); + const [totalPages, setTotalPages] = useState(0); - // Extract account ID from folder if it has a prefix and no explicit account ID was provided - if (folderToUse.includes(':')) { - const [folderAccountId, baseFolderName] = folderToUse.split(':'); - console.log(`Folder has prefix: accountId=${folderAccountId}, baseName=${baseFolderName}`); - - // If no explicit account ID was provided, use the one from the folder name - if (!normalizedAccountId) { - normalizedAccountId = folderAccountId; - console.log(`Using account ID from folder prefix: ${normalizedAccountId}`); - } - // If both exist but don't match, log a warning - else if (normalizedAccountId !== folderAccountId) { - console.warn(`⚠️ Mismatch between folder account prefix (${folderAccountId}) and provided accountId (${normalizedAccountId})`); - console.warn(`Using provided accountId (${normalizedAccountId}) but this may cause unexpected behavior`); - } - } - - const pageToUse = pageOverride || page; - const perPageToUse = perPageOverride || perPage; - - console.log(`Loading emails: folder=${folderToUse}, page=${pageToUse}, accountId=${normalizedAccountId || '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 (normalizedAccountId) { - url += `&accountId=${encodeURIComponent(normalizedAccountId)}`; - } - - // 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}`; - } - console.error(`API error: ${errorText}`); - throw new Error(errorText); - } - - const data = await response.json(); - console.log(`Received ${data.emails?.length || 0} emails from API`); - - if (pageToUse === 1 || !pageOverride) { - // Replace emails when loading first page - console.log(`Setting ${data.emails?.length || 0} emails (replacing existing)`); - setEmails(data.emails || []); - } else { - // Append emails when loading subsequent pages - console.log(`Appending ${data.emails?.length || 0} emails to existing list`); - setEmails(prev => [...prev, ...(data.emails || [])]); - } - - // Update pagination info - setTotalPages(data.totalPages || 0); - if (data.mailboxes && data.mailboxes.length > 0) { - console.log(`Received ${data.mailboxes.length} mailboxes from API`); - 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'}`); - // Set empty emails array on error to prevent UI issues - if (pageToUse === 1) { + /** + * 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); } - return null; - } finally { - setIsLoading(false); - } + }; + + /** + * Load emails for the current folder + */ + const loadEmails = async ( + folderOverride?: string, + pageOverride?: number, + perPageOverride?: number, + accountIdOverride?: string + ) => { + const folderToUse = folderOverride || currentFolder; + + // Enhanced folder and account handling + let normalizedFolder = folderToUse; + let normalizedAccountId = accountIdOverride; + + // Extract account ID from folder if it has a prefix and no explicit account ID was provided + if (folderToUse.includes(':')) { + const [folderAccountId, baseFolderName] = folderToUse.split(':'); + console.log(`Folder has prefix: accountId=${folderAccountId}, baseName=${baseFolderName}`); + + // If no explicit account ID was provided, use the one from the folder name + if (!normalizedAccountId) { + normalizedAccountId = folderAccountId; + console.log(`Using account ID from folder prefix: ${normalizedAccountId}`); + } + // If both exist but don't match, log a warning + else if (normalizedAccountId !== folderAccountId) { + console.warn(`⚠️ Mismatch between folder account prefix (${folderAccountId}) and provided accountId (${normalizedAccountId})`); + console.warn(`Using provided accountId (${normalizedAccountId}) but this may cause unexpected behavior`); + } + } + + const pageToUse = pageOverride || page; + const perPageToUse = perPageOverride || perPage; + + console.log(`Loading emails: folder=${folderToUse}, page=${pageToUse}, accountId=${normalizedAccountId || '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 (normalizedAccountId) { + url += `&accountId=${encodeURIComponent(normalizedAccountId)}`; + } + + // 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}`; + } + console.error(`API error: ${errorText}`); + throw new Error(errorText); + } + + const data = await response.json(); + console.log(`Received ${data.emails?.length || 0} emails from API`); + + if (pageToUse === 1 || !pageOverride) { + // Replace emails when loading first page + console.log(`Setting ${data.emails?.length || 0} emails (replacing existing)`); + setEmails(data.emails || []); + } else { + // Append emails when loading subsequent pages + console.log(`Appending ${data.emails?.length || 0} emails to existing list`); + setEmails(prev => [...prev, ...(data.emails || [])]); + } + + // Update pagination info + setTotalPages(data.totalPages || 0); + if (data.mailboxes && data.mailboxes.length > 0) { + console.log(`Received ${data.mailboxes.length} mailboxes from API`); + 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'}`); + // Set empty emails array on error to prevent UI issues + if (pageToUse === 1) { + setEmails([]); + } + return null; + } finally { + setIsLoading(false); + } + }; + + return { + // State + emails, + selectedEmail, + selectedEmailIds, + currentFolder, + mailboxes, + isLoading, + isSending, + isDeleting, + error, + searchQuery, + page, + perPage, + totalPages, + + // Actions + setEmails, + setSelectedEmail, + setSelectedEmailIds, + setCurrentFolder, + setMailboxes, + setIsLoading, + setIsSending, + setIsDeleting, + setError, + setSearchQuery, + setPage, + setPerPage, + setTotalPages, + + // Methods + changeFolder, + loadEmails + }; }; \ No newline at end of file