courrier refactor rebuild 2
This commit is contained in:
parent
819955d24b
commit
2ba6a2717b
@ -1,38 +0,0 @@
|
|||||||
import { NextResponse } from 'next/server';
|
|
||||||
import { getServerSession } from 'next-auth';
|
|
||||||
import { authOptions } from '@/app/api/auth/[...nextauth]/route';
|
|
||||||
import { getUserEmailCredentials } from '@/lib/services/email-service';
|
|
||||||
|
|
||||||
export async function GET() {
|
|
||||||
try {
|
|
||||||
// Authenticate user
|
|
||||||
const session = await getServerSession(authOptions);
|
|
||||||
if (!session || !session.user?.id) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Not authenticated" },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user's email credentials
|
|
||||||
const credentials = await getUserEmailCredentials(session.user.id);
|
|
||||||
|
|
||||||
if (!credentials) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "No email credentials found" },
|
|
||||||
{ status: 404 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the email address
|
|
||||||
return NextResponse.json({
|
|
||||||
email: credentials.email
|
|
||||||
});
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error("Error fetching user email:", error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Failed to fetch user email", message: error.message },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -101,49 +101,24 @@ export default function CourrierPage() {
|
|||||||
const [currentView, setCurrentView] = useState('INBOX');
|
const [currentView, setCurrentView] = useState('INBOX');
|
||||||
const [unreadCount, setUnreadCount] = useState(0);
|
const [unreadCount, setUnreadCount] = useState(0);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [userEmail, setUserEmail] = useState<string>('');
|
|
||||||
|
|
||||||
// Get user's email from session data
|
// Mock accounts for the sidebar
|
||||||
useEffect(() => {
|
|
||||||
const getUserEmail = async () => {
|
|
||||||
try {
|
|
||||||
// Try to get user email from API
|
|
||||||
const response = await fetch('/api/user/email');
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
if (data.email) {
|
|
||||||
setUserEmail(data.email);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching user email:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
getUserEmail();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Accounts for the sidebar (using the actual user email)
|
|
||||||
const [accounts, setAccounts] = useState<Account[]>([
|
const [accounts, setAccounts] = useState<Account[]>([
|
||||||
{ id: 0, name: 'All', email: '', color: 'bg-gray-500' },
|
{ id: 0, name: 'All', email: '', color: 'bg-gray-500' },
|
||||||
{ id: 1, name: userEmail || 'Loading...', email: userEmail || '', color: 'bg-blue-500', folders: mailboxes }
|
{ id: 1, name: 'Mail', email: 'user@example.com', color: 'bg-blue-500', folders: mailboxes }
|
||||||
]);
|
]);
|
||||||
const [selectedAccount, setSelectedAccount] = useState<Account | null>(null);
|
const [selectedAccount, setSelectedAccount] = useState<Account | null>(null);
|
||||||
|
|
||||||
// Update account folders and email when mailboxes or user email changes
|
// Update account folders when mailboxes change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAccounts(prev => {
|
setAccounts(prev => {
|
||||||
const updated = [...prev];
|
const updated = [...prev];
|
||||||
if (updated[1]) {
|
if (updated[1]) {
|
||||||
updated[1].folders = mailboxes;
|
updated[1].folders = mailboxes;
|
||||||
if (userEmail) {
|
|
||||||
updated[1].name = userEmail;
|
|
||||||
updated[1].email = userEmail;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return updated;
|
return updated;
|
||||||
});
|
});
|
||||||
}, [mailboxes, userEmail]);
|
}, [mailboxes]);
|
||||||
|
|
||||||
// Calculate unread count (this would be replaced with actual data in production)
|
// Calculate unread count (this would be replaced with actual data in production)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -146,7 +146,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
|
|||||||
const [cc, setCc] = useState<string>('');
|
const [cc, setCc] = useState<string>('');
|
||||||
const [bcc, setBcc] = useState<string>('');
|
const [bcc, setBcc] = useState<string>('');
|
||||||
const [subject, setSubject] = useState<string>('');
|
const [subject, setSubject] = useState<string>('');
|
||||||
const [emailContent, setEmailContent] = useState<string>('<div></div>');
|
const [emailContent, setEmailContent] = useState<string>('');
|
||||||
const [showCc, setShowCc] = useState<boolean>(false);
|
const [showCc, setShowCc] = useState<boolean>(false);
|
||||||
const [showBcc, setShowBcc] = useState<boolean>(false);
|
const [showBcc, setShowBcc] = useState<boolean>(false);
|
||||||
const [sending, setSending] = useState<boolean>(false);
|
const [sending, setSending] = useState<boolean>(false);
|
||||||
@ -158,36 +158,129 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
|
|||||||
|
|
||||||
// Initialize the form when replying to or forwarding an email
|
// Initialize the form when replying to or forwarding an email
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialEmail && (type === 'reply' || type === 'reply-all' || type === 'forward')) {
|
if (initialEmail && type !== 'new') {
|
||||||
try {
|
try {
|
||||||
// Handle reply and reply all
|
// Set recipients based on type
|
||||||
if (type === 'reply' || type === 'reply-all') {
|
if (type === 'reply' || type === 'reply-all') {
|
||||||
const result = formatReplyEmail(initialEmail, type === 'reply-all' ? 'reply-all' : 'reply');
|
// Reply goes to the original sender
|
||||||
|
setTo(formatEmailAddresses(initialEmail.from || []));
|
||||||
|
|
||||||
setTo(result.to || '');
|
// For reply-all, include all original recipients in CC
|
||||||
if (type === 'reply-all' && result.cc) {
|
if (type === 'reply-all') {
|
||||||
setCc(result.cc);
|
const allRecipients = [
|
||||||
setShowCc(Boolean(result.cc));
|
...(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));
|
||||||
}
|
}
|
||||||
setSubject(result.subject || `Re: ${initialEmail.subject || ''}`);
|
|
||||||
setEmailContent('<div></div>');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle forward
|
|
||||||
if (type === 'forward') {
|
|
||||||
const result = formatForwardedEmail(initialEmail);
|
|
||||||
|
|
||||||
setSubject(result.subject || `Fwd: ${initialEmail.subject || ''}`);
|
// Set subject with Re: prefix
|
||||||
setEmailContent('<div></div>');
|
const subjectBase = initialEmail.subject || '(No subject)';
|
||||||
|
const subject = subjectBase.match(/^Re:/i) ? subjectBase : `Re: ${subjectBase}`;
|
||||||
|
setSubject(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 = `
|
||||||
|
<div><br></div>
|
||||||
|
<div><br></div>
|
||||||
|
<div><br></div>
|
||||||
|
<div><br></div>
|
||||||
|
<div style="font-weight: 400; color: #555; margin: 20px 0 8px 0; font-size: 13px;">On ${formattedDate}, ${sender} wrote:</div>
|
||||||
|
<blockquote style="margin: 0; padding: 10px 0 10px 15px; border-left: 2px solid #ddd; color: #505050; background-color: #f9f9f9; border-radius: 4px;">
|
||||||
|
<div style="font-size: 13px;">
|
||||||
|
${content}
|
||||||
|
</div>
|
||||||
|
</blockquote>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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 = `
|
||||||
|
<div><br></div>
|
||||||
|
<div><br></div>
|
||||||
|
<div><br></div>
|
||||||
|
<div><br></div>
|
||||||
|
<div style="border-top: 1px solid #ccc; margin-top: 10px; padding-top: 10px;">
|
||||||
|
<div style="font-family: Arial, sans-serif; color: #333;">
|
||||||
|
<div style="margin-bottom: 15px;">
|
||||||
|
<div>---------- Forwarded message ---------</div>
|
||||||
|
<div><b>From:</b> ${fromString}</div>
|
||||||
|
<div><b>Date:</b> ${formattedDate}</div>
|
||||||
|
<div><b>Subject:</b> ${initialEmail.subject || ''}</div>
|
||||||
|
<div><b>To:</b> ${toString}</div>
|
||||||
|
</div>
|
||||||
|
<div class="email-original-content">
|
||||||
|
${content}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error initializing compose form:', error);
|
console.error('Error initializing compose form:', error);
|
||||||
// Set default subject if we couldn't format it
|
|
||||||
if (initialEmail.subject) {
|
|
||||||
setSubject(type === 'forward'
|
|
||||||
? `Fwd: ${initialEmail.subject}`
|
|
||||||
: `Re: ${initialEmail.subject}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [initialEmail, type]);
|
}, [initialEmail, type]);
|
||||||
@ -360,34 +453,15 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
|
|||||||
{/* Message Body */}
|
{/* Message Body */}
|
||||||
<div className="flex-1 min-h-[200px] flex flex-col overflow-hidden">
|
<div className="flex-1 min-h-[200px] flex flex-col overflow-hidden">
|
||||||
<Label htmlFor="message" className="flex-none block text-sm font-medium text-gray-700 mb-2">Message</Label>
|
<Label htmlFor="message" className="flex-none block text-sm font-medium text-gray-700 mb-2">Message</Label>
|
||||||
<div className="flex-1 border border-gray-300 rounded-md overflow-hidden bg-white">
|
<div className="flex-1 border border-gray-300 rounded-md overflow-hidden">
|
||||||
<RichEmailEditor
|
<RichEmailEditor
|
||||||
initialContent={emailContent}
|
initialContent={emailContent}
|
||||||
onChange={setEmailContent}
|
onChange={setEmailContent}
|
||||||
minHeight="200px"
|
minHeight="200px"
|
||||||
maxHeight="calc(100vh - 460px)"
|
maxHeight="none"
|
||||||
preserveFormatting={true}
|
preserveFormatting={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Use QuotedEmailContent component */}
|
|
||||||
{initialEmail && (type === 'reply' || type === 'reply-all' || type === 'forward') && (
|
|
||||||
<QuotedEmailContent
|
|
||||||
content={initialEmail.content || initialEmail.html || initialEmail.text || ''}
|
|
||||||
sender={{
|
|
||||||
name: initialEmail.from && initialEmail.from.length > 0 ? initialEmail.from[0].name : undefined,
|
|
||||||
email: initialEmail.from && initialEmail.from.length > 0 ? initialEmail.from[0].address : 'unknown@example.com'
|
|
||||||
}}
|
|
||||||
date={initialEmail.date || new Date()}
|
|
||||||
type={type === 'forward' ? 'forward' : 'reply'}
|
|
||||||
subject={initialEmail.subject}
|
|
||||||
recipients={initialEmail.to?.map(to => ({
|
|
||||||
name: to.name,
|
|
||||||
email: to.address
|
|
||||||
}))}
|
|
||||||
className="mt-4"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Attachments */}
|
{/* Attachments */}
|
||||||
@ -742,12 +816,12 @@ function LegacyAdapter({
|
|||||||
{/* Message Body */}
|
{/* Message Body */}
|
||||||
<div className="flex-1 min-h-[200px] flex flex-col overflow-hidden">
|
<div className="flex-1 min-h-[200px] flex flex-col overflow-hidden">
|
||||||
<Label htmlFor="message" className="flex-none block text-sm font-medium text-gray-700 mb-2">Message</Label>
|
<Label htmlFor="message" className="flex-none block text-sm font-medium text-gray-700 mb-2">Message</Label>
|
||||||
<div className="flex-1 border border-gray-300 rounded-md overflow-hidden bg-white">
|
<div className="flex-1 border border-gray-300 rounded-md overflow-hidden">
|
||||||
<RichEmailEditor
|
<RichEmailEditor
|
||||||
initialContent={composeBody}
|
initialContent={composeBody}
|
||||||
onChange={setComposeBody}
|
onChange={setComposeBody}
|
||||||
minHeight="200px"
|
minHeight="200px"
|
||||||
maxHeight="calc(100vh - 460px)"
|
maxHeight="none"
|
||||||
preserveFormatting={true}
|
preserveFormatting={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,8 +12,6 @@ interface QuotedEmailContentProps {
|
|||||||
date: Date | string;
|
date: Date | string;
|
||||||
type: 'reply' | 'forward';
|
type: 'reply' | 'forward';
|
||||||
className?: string;
|
className?: string;
|
||||||
subject?: string;
|
|
||||||
recipients?: Array<{name?: string; email: string}>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,9 +22,7 @@ const QuotedEmailContent: React.FC<QuotedEmailContentProps> = ({
|
|||||||
sender,
|
sender,
|
||||||
date,
|
date,
|
||||||
type,
|
type,
|
||||||
className = '',
|
className = ''
|
||||||
subject,
|
|
||||||
recipients
|
|
||||||
}) => {
|
}) => {
|
||||||
// Format the date
|
// Format the date
|
||||||
const formatDate = (date: Date | string) => {
|
const formatDate = (date: Date | string) => {
|
||||||
@ -52,16 +48,6 @@ const QuotedEmailContent: React.FC<QuotedEmailContentProps> = ({
|
|||||||
const senderName = sender.name || sender.email;
|
const senderName = sender.name || sender.email;
|
||||||
const formattedDate = formatDate(date);
|
const formattedDate = formatDate(date);
|
||||||
|
|
||||||
// Format recipients for display
|
|
||||||
const formatRecipientList = (recipients?: Array<{name?: string; email: string}>) => {
|
|
||||||
if (!recipients || recipients.length === 0) return '';
|
|
||||||
|
|
||||||
return recipients.map(r => {
|
|
||||||
const displayName = r.name || r.email;
|
|
||||||
return `${displayName} <${r.email}>`;
|
|
||||||
}).join(', ');
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create header based on type
|
// Create header based on type
|
||||||
const renderQuoteHeader = () => {
|
const renderQuoteHeader = () => {
|
||||||
if (type === 'reply') {
|
if (type === 'reply') {
|
||||||
@ -76,8 +62,8 @@ const QuotedEmailContent: React.FC<QuotedEmailContentProps> = ({
|
|||||||
<div>---------- Forwarded message ---------</div>
|
<div>---------- Forwarded message ---------</div>
|
||||||
<div><b>From:</b> {senderName} <{sender.email}></div>
|
<div><b>From:</b> {senderName} <{sender.email}></div>
|
||||||
<div><b>Date:</b> {formattedDate}</div>
|
<div><b>Date:</b> {formattedDate}</div>
|
||||||
<div><b>Subject:</b> {subject || 'No Subject'}</div>
|
<div><b>Subject:</b> {/* Subject would be passed as a prop if needed */}</div>
|
||||||
<div><b>To:</b> {formatRecipientList(recipients) || 'No Recipients'}</div>
|
<div><b>To:</b> {/* Recipients would be passed as a prop if needed */}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,21 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
|
|
||||||
const Quill = (await import('quill')).default;
|
const Quill = (await import('quill')).default;
|
||||||
|
|
||||||
|
// Import quill-better-table
|
||||||
|
try {
|
||||||
|
const QuillBetterTable = await import('quill-better-table');
|
||||||
|
|
||||||
|
// Register the table module if available
|
||||||
|
if (QuillBetterTable && QuillBetterTable.default) {
|
||||||
|
Quill.register({
|
||||||
|
'modules/better-table': QuillBetterTable.default
|
||||||
|
}, true);
|
||||||
|
console.log('Better Table module registered successfully');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('Table module not available:', err);
|
||||||
|
}
|
||||||
|
|
||||||
// Define custom formats/modules with table support
|
// Define custom formats/modules with table support
|
||||||
const emailToolbarOptions = [
|
const emailToolbarOptions = [
|
||||||
['bold', 'italic', 'underline', 'strike'],
|
['bold', 'italic', 'underline', 'strike'],
|
||||||
@ -55,6 +70,15 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
// Add any custom toolbar handlers here
|
// Add any custom toolbar handlers here
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'better-table': preserveFormatting ? {
|
||||||
|
operationMenu: {
|
||||||
|
items: {
|
||||||
|
unmergeCells: {
|
||||||
|
text: 'Unmerge cells'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} : false,
|
||||||
},
|
},
|
||||||
placeholder: placeholder,
|
placeholder: placeholder,
|
||||||
theme: 'snow',
|
theme: 'snow',
|
||||||
@ -71,34 +95,41 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
|
|
||||||
// If we're specifically trying to preserve complex HTML like tables
|
// If we're specifically trying to preserve complex HTML like tables
|
||||||
if (preserveFormatting) {
|
if (preserveFormatting) {
|
||||||
// For tables and complex formatting, use direct HTML setting
|
// For tables and complex formatting, we may need to manually preserve some elements
|
||||||
|
// Get all table elements from the original content
|
||||||
const tempDiv = document.createElement('div');
|
const tempDiv = document.createElement('div');
|
||||||
tempDiv.innerHTML = preservedContent;
|
tempDiv.innerHTML = preservedContent;
|
||||||
|
|
||||||
if (tempDiv.querySelectorAll('table').length > 0) {
|
// Force better table rendering in Quill
|
||||||
// This handles tables better than the Quill Delta format
|
|
||||||
quillRef.current.root.innerHTML = preservedContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force refresh after a delay
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
// This ensures tables are properly rendered by forcing a refresh
|
||||||
quillRef.current.update();
|
quillRef.current.update();
|
||||||
|
|
||||||
|
// Additional step: directly set HTML if tables aren't rendering properly
|
||||||
|
if (tempDiv.querySelectorAll('table').length > 0 &&
|
||||||
|
!quillRef.current.root.querySelectorAll('table').length) {
|
||||||
|
console.log('Using HTML fallback for tables');
|
||||||
|
quillRef.current.root.innerHTML = preservedContent;
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the cursor and scroll position is at the top of the editor
|
// Ensure the cursor and scroll position is at the top of the editor
|
||||||
quillRef.current.setSelection(0, 0);
|
quillRef.current.setSelection(0, 0);
|
||||||
|
|
||||||
// Also scroll the container to the top
|
// Also scroll the container to the top
|
||||||
if (editorRef.current) {
|
if (editorRef.current) {
|
||||||
editorRef.current.scrollTop = 0;
|
editorRef.current.scrollTop = 0;
|
||||||
const containers = [
|
|
||||||
editorRef.current.closest('.ql-container'),
|
// Also find and scroll parent containers that might have scroll
|
||||||
editorRef.current.closest('.rich-email-editor-container')
|
const scrollContainer = editorRef.current.closest('.ql-container');
|
||||||
];
|
if (scrollContainer) {
|
||||||
containers.forEach(container => {
|
scrollContainer.scrollTop = 0;
|
||||||
if (container) {
|
}
|
||||||
(container as HTMLElement).scrollTop = 0;
|
|
||||||
}
|
// One more check for nested scroll containers (like overflow divs)
|
||||||
});
|
const parentScrollContainer = editorRef.current.closest('.rich-email-editor-container');
|
||||||
|
if (parentScrollContainer) {
|
||||||
|
parentScrollContainer.scrollTop = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 50);
|
}, 50);
|
||||||
} else {
|
} else {
|
||||||
@ -114,16 +145,10 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add change listener (with debounce to prevent infinite updates)
|
// Add change listener
|
||||||
let debounceTimeout: NodeJS.Timeout;
|
|
||||||
quillRef.current.on('text-change', () => {
|
quillRef.current.on('text-change', () => {
|
||||||
clearTimeout(debounceTimeout);
|
const html = quillRef.current.root.innerHTML;
|
||||||
debounceTimeout = setTimeout(() => {
|
onChange(html);
|
||||||
const html = quillRef.current?.root.innerHTML;
|
|
||||||
if (html !== initialContent) {
|
|
||||||
onChange(html);
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Improve editor layout
|
// Improve editor layout
|
||||||
@ -178,7 +203,7 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [initialContent, isReady, preserveFormatting]);
|
}, [initialContent, isReady]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rich-email-editor-wrapper">
|
<div className="rich-email-editor-wrapper">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user