From 46d82204667ab5761a2e389c734530c470371f52 Mon Sep 17 00:00:00 2001 From: alma Date: Sun, 27 Apr 2025 12:05:51 +0200 Subject: [PATCH] courrier refactor rebuild 2 --- app/courrier/page.tsx | 134 +++++++++++------- components/email/ComposeEmail.tsx | 222 ++++++++++-------------------- 2 files changed, 157 insertions(+), 199 deletions(-) diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index b7645cf5..71b19116 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -98,23 +98,53 @@ export default function CourrierPage() { const [sidebarOpen, setSidebarOpen] = useState(true); const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false); const [accountsDropdownOpen, setAccountsDropdownOpen] = useState(true); + const [foldersOpen, setFoldersOpen] = useState(true); const [currentView, setCurrentView] = useState('INBOX'); const [unreadCount, setUnreadCount] = useState(0); const [loading, setLoading] = useState(false); + const [userEmail, setUserEmail] = useState(''); // Mock accounts for the sidebar const [accounts, setAccounts] = useState([ - { id: 0, name: 'All', email: '', color: 'bg-gray-500' }, - { id: 1, name: 'Mail', email: 'user@example.com', color: 'bg-blue-500', folders: mailboxes } + { id: 1, name: 'Mail', email: userEmail || 'Loading...', color: 'bg-blue-500', folders: mailboxes } ]); const [selectedAccount, setSelectedAccount] = useState(null); + // Fetch user email from credentials when component mounts + useEffect(() => { + async function fetchUserEmail() { + try { + const response = await fetch('/api/courrier/credentials'); + if (response.ok) { + const data = await response.json(); + if (data.credentials?.email) { + setUserEmail(data.credentials.email); + + // Update account with the email address + setAccounts(prev => { + const updated = [...prev]; + if (updated[0]) { + updated[0].email = data.credentials.email; + updated[0].name = data.credentials.email; + } + return updated; + }); + } + } + } catch (error) { + console.error('Error fetching user email:', error); + } + } + + fetchUserEmail(); + }, []); + // Update account folders when mailboxes change useEffect(() => { setAccounts(prev => { const updated = [...prev]; - if (updated[1]) { - updated[1].folders = mailboxes; + if (updated[0]) { + updated[0].folders = mailboxes; } return updated; }); @@ -311,56 +341,68 @@ export default function CourrierPage() { - {/* Show folders for email accounts (not for "All" account) without the "Folders" header */} - {account.id !== 0 && ( + {/* Show folders for email accounts without the "Folders" header */} + {account.folders && account.folders.length > 0 && foldersOpen && (
- {account.folders && account.folders.length > 0 ? ( - account.folders.map((folder) => ( - - )) - ) : ( -
-
- {/* Create placeholder folder items with shimmer effect */} - {Array.from({ length: 5 }).map((_, index) => ( -
-
-
-
- ))} + {folder === 'INBOX' && unreadCount > 0 && ( + + {unreadCount} + + )}
-
- )} + + ))} +
+ )} + + {account.folders && account.folders.length === 0 && ( +
+
+ {/* Create placeholder folder items with shimmer effect */} + {Array.from({ length: 5 }).map((_, index) => ( +
+
+
+
+ ))} +
)} diff --git a/components/email/ComposeEmail.tsx b/components/email/ComposeEmail.tsx index 1c77745c..a7041f5f 100644 --- a/components/email/ComposeEmail.tsx +++ b/components/email/ComposeEmail.tsx @@ -10,6 +10,7 @@ import { Textarea } from '@/components/ui/textarea'; import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card'; import DOMPurify from 'isomorphic-dompurify'; import { Label } from '@/components/ui/label'; +import dynamic from 'next/dynamic'; // Import sub-components import ComposeEmailHeader from './ComposeEmailHeader'; @@ -108,26 +109,18 @@ function EmailMessageToQuotedContentAdapter({ email: EmailMessage, type: 'reply' | 'reply-all' | 'forward' }) { - // Get the email content - const content = email.content || email.html || email.text || ''; - - // Get the sender - const sender = email.from && email.from.length > 0 - ? { - name: email.from[0].name, - email: email.from[0].address - } - : { email: 'unknown@example.com' }; - - // Map the type to what QuotedEmailContent expects - const mappedType = type === 'reply-all' ? 'reply' : type; + if (!email) return null; return ( ); } @@ -146,7 +139,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) { const [cc, setCc] = useState(''); const [bcc, setBcc] = useState(''); const [subject, setSubject] = useState(''); - const [emailContent, setEmailContent] = useState(''); + const [emailContent, setEmailContent] = useState('
'); const [showCc, setShowCc] = useState(false); const [showBcc, setShowBcc] = useState(false); const [sending, setSending] = useState(false); @@ -156,132 +149,53 @@ export default function ComposeEmail(props: ComposeEmailAllProps) { type: string; }>>([]); - // Initialize the form when replying to or forwarding an email + // Initialize the form with the provided email data useEffect(() => { - if (initialEmail && type !== 'new') { - try { - // Set recipients based on type + try { + // Only process if there's an initial email and it's a reply/forward + if (initialEmail && type !== 'new') { + // For replies: set the recipient to the sender of the original email if (type === 'reply' || type === 'reply-all') { - // Reply goes to the original sender + // Set recipients and subject using the formatReplyEmail utility + const formattedEmail = formatReplyEmail(initialEmail, type as 'reply' | 'reply-all'); + + // Set recipients setTo(formatEmailAddresses(initialEmail.from || [])); - // For reply-all, include all original recipients in CC + // For reply-all: add original recipients to CC if (type === 'reply-all') { const allRecipients = [ ...(initialEmail.to || []), ...(initialEmail.cc || []) ]; - // Filter out the current user if they were a recipient - // This would need some user context to properly implement - setCc(formatEmailAddresses(allRecipients)); + + if (allRecipients.length > 0) { + setCc(formatEmailAddresses(allRecipients)); + setShowCc(true); + } } - // Set subject with Re: prefix - const subjectBase = initialEmail.subject || '(No subject)'; - const subject = subjectBase.match(/^Re:/i) ? subjectBase : `Re: ${subjectBase}`; - setSubject(subject); + // Set subject with "Re:" prefix if needed + setSubject(formattedEmail.subject); - // Format the reply content with the quoted message included directly - const content = initialEmail.content || initialEmail.html || initialEmail.text || ''; - const sender = initialEmail.from && initialEmail.from.length > 0 - ? initialEmail.from[0].name || initialEmail.from[0].address - : 'Unknown sender'; - const date = initialEmail.date ? - (typeof initialEmail.date === 'string' ? new Date(initialEmail.date) : initialEmail.date) : - new Date(); - - // Format date for display - const formattedDate = date.toLocaleString('en-US', { - weekday: 'short', - year: 'numeric', - month: 'short', - day: 'numeric', - hour: '2-digit', - minute: '2-digit' - }); - - // Create reply content with quote - const replyContent = ` -

-

-

-

-
On ${formattedDate}, ${sender} wrote:
-
-
- ${content} -
-
- `; - - setEmailContent(replyContent); - - // Show CC field if there are CC recipients - if (initialEmail.cc && initialEmail.cc.length > 0) { - setShowCc(true); - } - } - else if (type === 'forward') { - // Set subject with Fwd: prefix - const subjectBase = initialEmail.subject || '(No subject)'; - const subject = subjectBase.match(/^(Fwd|FW|Forward):/i) ? subjectBase : `Fwd: ${subjectBase}`; - setSubject(subject); - - // Format the forward content with the original email included directly - const content = initialEmail.content || initialEmail.html || initialEmail.text || ''; - const fromString = formatEmailAddresses(initialEmail.from || []); - const toString = formatEmailAddresses(initialEmail.to || []); - const date = initialEmail.date ? - (typeof initialEmail.date === 'string' ? new Date(initialEmail.date) : initialEmail.date) : - new Date(); - - // Format date for display - const formattedDate = date.toLocaleString('en-US', { - weekday: 'short', - year: 'numeric', - month: 'short', - day: 'numeric', - hour: '2-digit', - minute: '2-digit' - }); - - // Create forwarded content - const forwardContent = ` -

-

-

-

-
-
-
-
---------- Forwarded message ---------
-
From: ${fromString}
-
Date: ${formattedDate}
-
Subject: ${initialEmail.subject || ''}
-
To: ${toString}
-
- -
-
- `; - - setEmailContent(forwardContent); - - // If the original email has attachments, we should include them - if (initialEmail.attachments && initialEmail.attachments.length > 0) { - const formattedAttachments = initialEmail.attachments.map(att => ({ - name: att.filename || 'attachment', - type: att.contentType || 'application/octet-stream', - content: att.content || '' - })); - setAttachments(formattedAttachments); - } + // Initialize with empty content - we'll use QuotedEmailContent in the render + setEmailContent('
'); + } + + // For forwards: set forwarded subject + else if (type === 'forward') { + // Format the email for forwarding + const formattedEmail = formatForwardedEmail(initialEmail); + + // Set subject with "Fwd:" prefix + setSubject(formattedEmail.subject); + + // Initialize with empty content - we'll use QuotedEmailContent in the render + setEmailContent('
'); } - } catch (error) { - console.error('Error initializing compose form:', error); } + } catch (error) { + console.error('Error initializing compose form:', error); } }, [initialEmail, type]); @@ -450,18 +364,26 @@ export default function ComposeEmail(props: ComposeEmailAllProps) { /> - {/* Message Body */} -
- -
- -
+ {/* Email Content Section */} +
+ + {/* Add QuotedEmailContent for replies and forwards */} + {initialEmail && (type === 'reply' || type === 'reply-all' || type === 'forward') && ( +
+ +
+ )}
{/* Attachments */} @@ -813,18 +735,12 @@ function LegacyAdapter({ />
- {/* Message Body */} -
- -
- -
+ {/* Email Content Section */} +
+
{/* Attachments */}