diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index 64779b35..36116179 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -60,6 +60,32 @@ interface Attachment { encoding: string; } +interface ParsedEmailContent { + text: string; + html: string; + attachments: { + filename: string; + contentType: string; + encoding: string; + content: string; + }[]; + headers?: string; +} + +interface ParsedEmailMetadata { + subject: string; + from: string; + to: string; + date: string; + contentType: string; + text: string | null; + html: string | null; + raw: { + headers: string; + body: string; + }; +} + // Improved MIME Decoder Implementation for Infomaniak function extractBoundary(headers: string): string | null { const boundaryMatch = headers.match(/boundary="?([^"\r\n;]+)"?/i) || @@ -414,12 +440,12 @@ function decodeMimeContent(content: string): string { // Add this helper function const renderEmailContent = (email: Email) => { try { - const parsed = parseFullEmail(email.body); - const content = parsed.text || parsed.html || email.body; - const isHtml = parsed.html || content.includes('<'); + const parsed = parseFullEmail(email.body) as ParsedEmailContent | ParsedEmailMetadata; + const content = 'text' in parsed ? parsed.text : ('html' in parsed ? parsed.html || '' : email.body); + const isHtml = 'html' in parsed ? !!parsed.html : content.includes('<'); if (isHtml) { - // Sanitize HTML content + // Enhanced HTML sanitization const sanitizedHtml = content .replace(/)<[^<]*)*<\/script>/gi, '') .replace(/)<[^<]*)*<\/style>/gi, '') @@ -428,26 +454,90 @@ const renderEmailContent = (email: Email) => { .replace(/javascript:/gi, '') .replace(/data:/gi, '') .replace(/]*>/gi, '') - .replace(/]*>/gi, ''); + .replace(/]*>/gi, '') + // Fix common email client quirks + .replace(/=3D/g, '=') + .replace(/=20/g, ' ') + .replace(/=E2=80=99/g, "'") + .replace(/=E2=80=9C/g, '"') + .replace(/=E2=80=9D/g, '"') + .replace(/=E2=80=93/g, '–') + .replace(/=E2=80=94/g, '—') + .replace(/=C2=A0/g, ' ') + .replace(/=C3=A0/g, 'à') + .replace(/=C3=A9/g, 'é') + .replace(/=C3=A8/g, 'è') + .replace(/=C3=AA/g, 'ê') + .replace(/=C3=AB/g, 'ë') + .replace(/=C3=B4/g, 'ô') + .replace(/=C3=B9/g, 'ù') + .replace(/=C3=BB/g, 'û'); return ( -
+
+ {'attachments' in parsed && parsed.attachments && parsed.attachments.length > 0 && ( +
+

Attachments:

+
+ {parsed.attachments.map((attachment, index: number) => ( +
+ + {attachment.filename} + + ({attachment.contentType}) + +
+ ))} +
+
+ )} +
+
); } else { - // Format plain text content + // Enhanced plain text formatting const formattedText = content .replace(/\n/g, '
') .replace(/\t/g, '    ') - .replace(/ /g, '  '); + .replace(/ /g, '  ') + // Fix common email client quirks + .replace(/=3D/g, '=') + .replace(/=20/g, ' ') + .replace(/=E2=80=99/g, "'") + .replace(/=E2=80=9C/g, '"') + .replace(/=E2=80=9D/g, '"') + .replace(/=E2=80=93/g, '–') + .replace(/=E2=80=94/g, '—') + .replace(/=C2=A0/g, ' ') + .replace(/=C3=A0/g, 'à') + .replace(/=C3=A9/g, 'é') + .replace(/=C3=A8/g, 'è') + .replace(/=C3=AA/g, 'ê') + .replace(/=C3=AB/g, 'ë') + .replace(/=C3=B4/g, 'ô') + .replace(/=C3=B9/g, 'ù') + .replace(/=C3=BB/g, 'û'); return ( -
+
+ {'attachments' in parsed && parsed.attachments && parsed.attachments.length > 0 && ( +
+

Attachments:

+
+ {parsed.attachments.map((attachment, index: number) => ( +
+ + {attachment.filename} + + ({attachment.contentType}) + +
+ ))} +
+
+ )} +
+
); } } catch (e) { @@ -510,7 +600,7 @@ const initialSidebarItems = [ } ]; -export default function CourrierPage() { +export default function MailPage() { const router = useRouter(); const [loading, setLoading] = useState(true); const [accounts, setAccounts] = useState([ @@ -1155,9 +1245,9 @@ export default function CourrierPage() { // Get text content from parsed email let preview = ''; - if (parsed.text) { + if ('text' in parsed && parsed.text) { preview = parsed.text; - } else if (parsed.html) { + } else if ('html' in parsed && parsed.html) { // If only HTML is available, extract text content preview = parsed.html .replace(/]*>[\s\S]*?<\/style>/gi, '') @@ -1178,13 +1268,29 @@ export default function CourrierPage() { // Clean up the preview preview = preview - .replace(/^>+/gm, '') - .replace(/Content-Type:[^\n]+/g, '') + .replace(/^>+/gm, '') // Remove quoted text markers + .replace(/Content-Type:[^\n]+/g, '') // Remove MIME headers .replace(/Content-Transfer-Encoding:[^\n]+/g, '') - .replace(/--[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?/g, '') + .replace(/--[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?/g, '') // Remove MIME boundaries .replace(/boundary=[^\n]+/g, '') .replace(/charset=[^\n]+/g, '') .replace(/[\r\n]+/g, ' ') + .replace(/=3D/g, '=') // Fix common email client quirks + .replace(/=20/g, ' ') + .replace(/=E2=80=99/g, "'") + .replace(/=E2=80=9C/g, '"') + .replace(/=E2=80=9D/g, '"') + .replace(/=E2=80=93/g, '–') + .replace(/=E2=80=94/g, '—') + .replace(/=C2=A0/g, ' ') + .replace(/=C3=A0/g, 'à') + .replace(/=C3=A9/g, 'é') + .replace(/=C3=A8/g, 'è') + .replace(/=C3=AA/g, 'ê') + .replace(/=C3=AB/g, 'ë') + .replace(/=C3=B4/g, 'ô') + .replace(/=C3=B9/g, 'ù') + .replace(/=C3=BB/g, 'û') .trim(); // Take first 100 characters @@ -1635,93 +1741,92 @@ export default function CourrierPage() { } return ( -
-
-
- {/* Sidebar */} -
- {/* Courrier Title */} -
+ <> + {/* Main layout */} +
+ {/* Sidebar */} +
+ {/* Courrier Title */} +
+
+ + COURRIER +
+
+ + {/* Compose button and refresh button */} +
+
+ + +
- {/* Compose button and refresh button */} -
- - -
- - {/* Accounts Section */} -
- - - {accountsDropdownOpen && ( -
- {accounts.map(account => ( -
- + + {accountsDropdownOpen && ( +
+ {accounts.map(account => ( +
+ -
- ))} -
- )} -
- - {/* Navigation */} - {renderSidebarNav()} + {account.email} +
+ +
+ ))} +
+ )}
- {/* Main content area */} -
- {/* Email list panel */} - {renderEmailListWrapper()} -
+ {/* Navigation */} + {renderSidebarNav()} +
+ + {/* Main content area */} +
+ {/* Email list panel */} + {renderEmailListWrapper()}
@@ -1886,6 +1991,6 @@ export default function CourrierPage() {
)} {renderDeleteConfirmDialog()} -
+ ); }