panel 2 courier api restore

This commit is contained in:
alma 2025-04-26 08:47:35 +02:00
parent 7751094519
commit 079d0a484b
3 changed files with 28 additions and 17 deletions

View File

@ -74,3 +74,17 @@
}
}
/* Email specific styles */
.email-content table { width: 100%; border-collapse: collapse; }
.email-content table.table-container { width: auto; margin-bottom: 20px; }
.email-content td, .email-content th { padding: 8px; border: 1px solid #e5e7eb; }
.email-content img { max-width: 100%; height: auto; }
.email-content div[style] { max-width: 100% !important; }
.email-content * { max-width: 100% !important; word-wrap: break-word; }
.email-content font { font-family: inherit; }
.email-content .total-row td { border-top: 1px solid #e5e7eb; }
.email-content a { color: #3b82f6; text-decoration: underline; }
.email-content p { margin-bottom: 0.75em; }
.email-content .header { margin-bottom: 1em; }
.email-content .footer { font-size: 0.875rem; color: #6b7280; margin-top: 1em; }

View File

@ -1,6 +1,6 @@
'use client';
import { useRef, useEffect, useState } from 'react';
import { useRef, useEffect, useState, useCallback } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
@ -84,6 +84,7 @@ export default function ComposeEmail({
const composeBodyRef = useRef<HTMLDivElement>(null);
const [localContent, setLocalContent] = useState('');
const [isLoading, setIsLoading] = useState(false);
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (replyTo || forwardFrom) {
@ -172,14 +173,6 @@ export default function ComposeEmail({
<p>To: ${decoded.to || ''}</p>
<br>
<div class="email-content prose prose-sm max-w-none dark:prose-invert">
<style>
/* Email-specific styles */
.email-content table { width: 100%; border-collapse: collapse; }
.email-content td, .email-content th { padding: 8px; border: 1px solid #e5e7eb; }
.email-content img { max-width: 100%; height: auto; }
.email-content div[style] { max-width: 100% !important; }
.email-content * { max-width: 100% !important; }
</style>
${decoded.html || `<pre>${decoded.text || ''}</pre>`}
</div>
</div>
@ -189,7 +182,9 @@ export default function ComposeEmail({
<div class="quoted-message">
<p>On ${formatDate(decoded.date)}, ${decoded.from || ''} wrote:</p>
<blockquote>
<div class="email-content prose prose-sm max-w-none dark:prose-invert">
${decoded.html || `<pre>${decoded.text || ''}</pre>`}
</div>
</blockquote>
</div>
`;
@ -300,16 +295,16 @@ export default function ComposeEmail({
// Ensure wheel events are properly handled
if (!(div as HTMLElement).hasAttribute('data-scroll-handler-attached')) {
div.addEventListener('wheel', (e: Event) => {
const wheelEvent = e as WheelEvent;
const target = e.currentTarget as HTMLElement;
div.addEventListener('wheel', function(this: HTMLElement, ev: Event) {
const e = ev as WheelEvent;
const target = this;
// Check if we're at the boundary of the scrollable area
const isAtBottom = target.scrollHeight - target.scrollTop <= target.clientHeight + 1;
const isAtTop = target.scrollTop <= 0;
// Only prevent default if we're not at the boundaries in the direction of scrolling
if ((wheelEvent.deltaY > 0 && !isAtBottom) || (wheelEvent.deltaY < 0 && !isAtTop)) {
if ((e.deltaY > 0 && !isAtBottom) || (e.deltaY < 0 && !isAtTop)) {
e.stopPropagation();
e.preventDefault(); // Prevent the parent container from scrolling
}

View File

@ -105,12 +105,14 @@ export function cleanHtml(html: string): string {
try {
// Enhanced configuration to preserve more HTML elements for complex emails
return DOMPurify.sanitize(html, {
ADD_TAGS: ['style', 'meta', 'link', 'table', 'thead', 'tbody', 'tr', 'td', 'th', 'hr'],
ADD_ATTR: ['*', 'colspan', 'rowspan', 'cellpadding', 'cellspacing', 'border', 'bgcolor', 'width', 'height', 'align', 'valign', 'class', 'id', 'style'],
ADD_TAGS: ['style', 'meta', 'link', 'table', 'thead', 'tbody', 'tr', 'td', 'th', 'hr', 'font', 'div', 'span', 'a', 'img', 'b', 'strong', 'i', 'em', 'u', 'br', 'p', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'code', 'center', 'section', 'header', 'footer', 'article', 'nav'],
ADD_ATTR: ['*', 'colspan', 'rowspan', 'cellpadding', 'cellspacing', 'border', 'bgcolor', 'width', 'height', 'align', 'valign', 'class', 'id', 'style', 'color', 'face', 'size', 'background', 'src', 'href', 'target', 'rel', 'alt', 'title', 'name'],
ALLOW_UNKNOWN_PROTOCOLS: true,
WHOLE_DOCUMENT: true,
KEEP_CONTENT: true,
RETURN_DOM: false
RETURN_DOM: false,
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'form', 'input', 'button', 'select', 'option', 'textarea', 'canvas', 'video', 'audio'],
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onmouseout', 'onchange', 'onsubmit']
});
} catch (error) {
console.error('Error cleaning HTML:', error);