From ae1087f4010d49c79432c4b12e7ada5e1a48869a Mon Sep 17 00:00:00 2001 From: alma Date: Sun, 27 Apr 2025 00:02:35 +0200 Subject: [PATCH] courrier refactor rebuild preview --- app/globals.css | 99 +++++++++++++++++++++++++++++ components/email/EmailContent.tsx | 100 +++++++++++------------------- components/email/EmailPreview.tsx | 67 +++++--------------- 3 files changed, 151 insertions(+), 115 deletions(-) diff --git a/app/globals.css b/app/globals.css index 28557475..c36495d6 100644 --- a/app/globals.css +++ b/app/globals.css @@ -212,3 +212,102 @@ div[style*="---------- Forwarded message ---------"] { margin: 0; } +/* Email display styles */ +.email-content-display { + max-width: 100%; + word-wrap: break-word; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + line-height: 1.5; +} + +/* Preserve email structure */ +.email-content-display * { + max-width: 100% !important; +} + +/* Images */ +.email-content-display img { + max-width: 100%; + height: auto; + display: inline-block; + margin: 8px 0; +} + +/* Tables */ +.email-content-display table { + width: 100%; + border-collapse: collapse; + margin: 16px 0; +} + +.email-content-display td, +.email-content-display th { + padding: 8px; + border: 1px solid #e5e7eb; +} + +/* Buttons */ +.email-content-display button, +.email-content-display a[role="button"], +.email-content-display a.button, +.email-content-display div.button, +.email-content-display [class*="btn"], +.email-content-display [class*="button"] { + display: inline-block; + padding: 8px 16px; + background-color: #f97316; + color: white; + border-radius: 4px; + text-decoration: none; + font-weight: 500; + margin: 8px 0; + text-align: center; + cursor: pointer; + border: none; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} + +/* Links */ +.email-content-display a { + color: #3b82f6; + text-decoration: underline; +} + +/* Headers and text */ +.email-content-display h1, +.email-content-display h2, +.email-content-display h3 { + margin-top: 24px; + margin-bottom: 16px; + font-weight: 600; + line-height: 1.25; +} + +.email-content-display p { + margin-bottom: 16px; +} + +/* Quote blocks for email replies */ +.email-content-display blockquote { + margin: 16px 0; + padding: 8px 16px; + border-left: 3px solid #e5e7eb; + color: #4b5563; + background-color: #f9fafb; +} + +/* Support for RTL content */ +.email-content-display[dir="rtl"], +.email-content-display [dir="rtl"] { + text-align: right; +} + +/* Remove any padding/margins from the first and last elements */ +.email-content-display > *:first-child { + margin-top: 0; +} + +.email-content-display > *:last-child { + margin-bottom: 0; +} + diff --git a/components/email/EmailContent.tsx b/components/email/EmailContent.tsx index 3171a282..81a4be00 100644 --- a/components/email/EmailContent.tsx +++ b/components/email/EmailContent.tsx @@ -1,80 +1,45 @@ 'use client'; import React, { useState, useEffect } from 'react'; -import { Loader2, Paperclip, FileDown, Download } from 'lucide-react'; +import { Loader2, Paperclip, Download } from 'lucide-react'; import { sanitizeHtml } from '@/lib/utils/email-formatter'; -import { Button } from '@/components/ui/button'; -import { Email } from '@/hooks/use-courrier'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; -import { ScrollArea } from '@/components/ui/scroll-area'; import { Avatar, AvatarFallback } from '@/components/ui/avatar'; +interface EmailAddress { + name: string; + address: string; +} + +interface Email { + id: string; + subject: string; + from: EmailAddress[]; + to: EmailAddress[]; + cc?: EmailAddress[]; + bcc?: EmailAddress[]; + date: Date | string; + content?: string; + html?: string; + text?: string; + hasAttachments?: boolean; + attachments?: Array<{ + filename: string; + contentType: string; + size: number; + path?: string; + content?: string; + }>; +} + interface EmailContentProps { email: Email; } export default function EmailContent({ email }: EmailContentProps) { - const [content, setContent] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); - useEffect(() => { - if (!email) return; - - const renderContent = async () => { - setIsLoading(true); - setError(null); - - try { - if (!email.content || email.content.length === 0) { - setContent(
Email content is empty
); - return; - } - - // Use the sanitizer from the centralized formatter - const sanitizedHtml = sanitizeHtml(email.content); - - // Look for specific markers that indicate this is a forwarded or replied email - const isForwarded = sanitizedHtml.includes('---------- Forwarded message ---------'); - const isReply = sanitizedHtml.includes('class="reply-body"') || - sanitizedHtml.includes('blockquote style="margin: 0; padding: 10px 0 10px 15px; border-left:'); - - // For forwarded or replied emails, ensure we keep the exact structure - if (isForwarded || isReply) { - setContent( -
- ); - } else { - // For regular emails, wrap in the same structure used in the compose editor - setContent( -
-
-
-
-
- ); - } - } catch (err) { - console.error('Error rendering email content:', err); - setError('Error rendering email content. Please try again.'); - setContent(null); - } finally { - setIsLoading(false); - } - }; - - renderContent(); - }, [email]); - // Render attachments if they exist const renderAttachments = () => { if (!email?.attachments || email.attachments.length === 0) { @@ -102,7 +67,7 @@ export default function EmailContent({ email }: EmailContentProps) { if (isLoading) { return (
-
+
); } @@ -156,7 +121,14 @@ export default function EmailContent({ email }: EmailContentProps) {
- {content} + {email.content ? ( +
+
+
+ ) : ( +

No content available

+ )} + {renderAttachments()}
); diff --git a/components/email/EmailPreview.tsx b/components/email/EmailPreview.tsx index 16dfa1bb..d07fb418 100644 --- a/components/email/EmailPreview.tsx +++ b/components/email/EmailPreview.tsx @@ -1,8 +1,7 @@ 'use client'; -import { useState, useEffect } from 'react'; -import DOMPurify from 'isomorphic-dompurify'; -import { Loader2, Paperclip, Download } from 'lucide-react'; +import { useState } from 'react'; +import { Loader2, Paperclip } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; @@ -55,49 +54,6 @@ interface EmailPreviewProps { export default function EmailPreview({ email, loading = false, onReply }: EmailPreviewProps) { const [contentLoading, setContentLoading] = useState(false); - // Handle sanitizing and rendering HTML content - const renderContent = () => { - if (!email?.content) return

No content available

; - - try { - // Use the centralized sanitizeHtml function which preserves direction - const sanitizedContent = sanitizeHtml(email.content); - - // Look for specific markers that indicate this is a forwarded or replied email - const isForwarded = sanitizedContent.includes('---------- Forwarded message ---------'); - const isReply = sanitizedContent.includes('class="reply-body"') || - sanitizedContent.includes('blockquote style="margin: 0; padding: 10px 0 10px 15px; border-left:'); - - // For forwarded or replied emails, ensure we keep the exact structure - if (isForwarded || isReply) { - return ( -
- ); - } - - // For regular emails, preserve all HTML elements with minimal wrapping - return ( -
-
-
-
-
- ); - } catch (error) { - console.error('Error rendering email content:', error); - return

Error displaying email content

; - } - }; - // Format the date const formatDate = (date: Date | string) => { if (!date) return ''; @@ -124,17 +80,19 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP ).join(', '); }; + // Display loading state if (loading || contentLoading) { return (
-

Loading email content...

+

Loading email...

); } + // No email selected if (!email) { return (
@@ -221,10 +179,17 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP
{/* Email content */} - -
- {renderContent()} -
+ + {email.content ? ( +
+ ) : ( +

No content available

+ )}
);