NeahNew/app/hooks/use-courrier.ts
2025-05-05 17:25:00 +02:00

227 lines
6.8 KiB
TypeScript

import { useState } from 'react';
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<Email[]>([]);
const [selectedEmail, setSelectedEmail] = useState<Email | null>(null);
const [selectedEmailIds, setSelectedEmailIds] = useState<string[]>([]);
const [currentFolder, setCurrentFolder] = useState<MailFolder>('INBOX');
const [mailboxes, setMailboxes] = useState<string[]>([]);
// State for UI
const [isLoading, setIsLoading] = useState(false);
const [isSending, setIsSending] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [error, setError] = useState<string | null>(null);
const [searchQuery, setSearchQuery] = useState('');
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(20);
const [totalPages, setTotalPages] = useState(0);
/**
* 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;
// 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
};
};