compose mime

This commit is contained in:
alma 2025-04-25 09:38:06 +02:00
parent b025baa6c1
commit 96d6d50e50

View File

@ -42,21 +42,16 @@ export interface Account {
}
export interface Email {
id: number;
accountId: number;
id: string;
from: string;
fromName: string;
fromName?: string;
to: string;
subject: string;
body: string;
content: string;
date: string;
read: boolean;
starred: boolean;
folder: string;
cc?: string;
bcc?: string;
flags?: string[];
raw: string;
attachments?: { name: string; url: string }[];
}
interface Attachment {
@ -113,7 +108,7 @@ function EmailContent({ email }: { email: Email }) {
setIsLoading(true);
try {
if (!email.body) {
if (!email.content) {
if (mounted) {
setContent(<div className="text-gray-500">No content available</div>);
setIsLoading(false);
@ -121,7 +116,7 @@ function EmailContent({ email }: { email: Email }) {
return;
}
const formattedEmail = email.body.trim();
const formattedEmail = email.content.trim();
if (!formattedEmail) {
if (mounted) {
setContent(<div className="text-gray-500">No content available</div>);
@ -167,7 +162,7 @@ function EmailContent({ email }: { email: Email }) {
return () => {
mounted = false;
};
}, [email?.body]);
}, [email?.content]);
if (isLoading) {
return (
@ -263,12 +258,12 @@ function ReplyContent({ email, type }: { email: Email; type: 'reply' | 'reply-al
async function loadReplyContent() {
try {
if (!email.body) {
if (!email.content) {
if (mounted) setContent('');
return;
}
const decoded = await decodeEmail(email.body);
const decoded = await decodeEmail(email.content);
if (mounted) {
let formattedContent = '';
@ -278,7 +273,7 @@ function ReplyContent({ email, type }: { email: Email; type: 'reply' | 'reply-al
<div class="forwarded-message">
<p>---------- Forwarded message ---------</p>
<p>From: ${decoded.from || ''}</p>
<p>Date: ${formatDate(decoded.date)}</p>
<p>Date: ${formatDate(decoded.date ? new Date(decoded.date) : null)}</p>
<p>Subject: ${decoded.subject || ''}</p>
<p>To: ${decoded.to || ''}</p>
<br>
@ -288,7 +283,7 @@ function ReplyContent({ email, type }: { email: Email; type: 'reply' | 'reply-al
} else {
formattedContent = `
<div class="quoted-message">
<p>On ${formatDate(decoded.date)}, ${decoded.from || ''} wrote:</p>
<p>On ${formatDate(decoded.date ? new Date(decoded.date) : null)}, ${decoded.from || ''} wrote:</p>
<blockquote>
${decoded.html || `<pre>${decoded.text || ''}</pre>`}
</blockquote>
@ -313,7 +308,7 @@ function ReplyContent({ email, type }: { email: Email; type: 'reply' | 'reply-al
return () => {
mounted = false;
};
}, [email.body, type]);
}, [email.content, type]);
if (error) {
return <div className="text-red-500">{error}</div>;
@ -336,14 +331,14 @@ function EmailPreview({ email }: { email: Email }) {
let mounted = true;
async function loadPreview() {
if (!email?.body) {
if (!email?.content) {
if (mounted) setPreview('No content available');
return;
}
setIsLoading(true);
try {
const decoded = await decodeEmail(email.body);
const decoded = await decodeEmail(email.content);
if (mounted) {
if (decoded.text) {
setPreview(decoded.text.substring(0, 150) + '...');
@ -371,7 +366,7 @@ function EmailPreview({ email }: { email: Email }) {
return () => {
mounted = false;
};
}, [email?.body]);
}, [email?.content]);
if (isLoading) {
return <span className="text-gray-400">Loading preview...</span>;
@ -1078,45 +1073,51 @@ export default function CourrierPage() {
}, [availableFolders]);
// Update the email list item to match header checkbox alignment
const renderEmailListItem = (email: Email) => {
return (
<div
key={email.id}
className={`flex items-center gap-3 px-4 py-2 hover:bg-gray-50/80 cursor-pointer ${
selectedEmail?.id === email.id ? 'bg-blue-50/50' : ''
} ${!email.read ? 'bg-blue-50/20' : ''}`}
onClick={() => handleEmailSelect(email.id)}
>
const renderEmailListItem = (email: Email) => (
<div
key={email.id}
className={`p-3 hover:bg-gray-50/50 transition-colors cursor-pointer flex items-start gap-3 ${
selectedEmail?.id === email.id ? 'bg-gray-50/80' : ''
}`}
onClick={() => handleEmailSelect(email.id)}
>
<div className="flex-none pt-1">
<Checkbox
checked={selectedEmails.includes(email.id.toString())}
onCheckedChange={(checked) => {
const e = { target: { checked }, stopPropagation: () => {} } as React.ChangeEvent<HTMLInputElement>;
handleEmailCheckbox(e, email.id);
}}
checked={selectedEmails.includes(email.id)}
onCheckedChange={(checked) => toggleEmailSelection(email.id)}
onClick={(e) => e.stopPropagation()}
className="mt-0.5"
/>
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2 min-w-0">
<span className={`text-sm truncate ${!email.read ? 'font-semibold text-gray-900' : 'text-gray-600'}`}>
{email.fromName || email.from}
</span>
</div>
<span className="text-xs text-gray-500 whitespace-nowrap">
{formatDate(new Date(email.date))}
</span>
</div>
<h3 className="text-sm text-gray-900 truncate">
{email.subject || '(No subject)'}
</h3>
<div className="text-xs text-gray-500 line-clamp-2">
{generateEmailPreview(email)}
</div>
</div>
</div>
);
};
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between gap-2 mb-1">
<div className="flex items-center gap-2 min-w-0">
<span className={`text-sm truncate ${!email.read ? 'font-semibold text-gray-900' : 'text-gray-600'}`}>
{email.fromName || email.from}
</span>
{!email.read && (
<span className="w-1.5 h-1.5 bg-blue-600 rounded-full flex-shrink-0"></span>
)}
</div>
<span className="text-xs text-gray-500 whitespace-nowrap">
{formatDate(new Date(email.date))}
</span>
</div>
<h3 className={`text-sm truncate mb-0.5 ${!email.read ? 'text-gray-900' : 'text-gray-600'}`}>
{email.subject || '(No subject)'}
</h3>
<EmailPreview email={email} />
</div>
<div className="flex-none flex items-center gap-1">
{email.starred && (
<Star className="h-4 w-4 text-yellow-400 fill-yellow-400" />
)}
{email.attachments && email.attachments.length > 0 && (
<Paperclip className="h-4 w-4 text-gray-400" />
)}
</div>
</div>
);
const handleMailboxChange = async (newMailbox: string) => {
setCurrentView(newMailbox);
@ -1355,6 +1356,14 @@ export default function CourrierPage() {
</AlertDialog>
);
const toggleEmailSelection = (emailId: string) => {
setSelectedEmails((prev) =>
prev.includes(emailId)
? prev.filter((id) => id !== emailId)
: [...prev, emailId]
);
};
if (error) {
return (
<div className="flex h-[calc(100vh-theme(spacing.12))] items-center justify-center bg-gray-100 mt-12">