mail page rest
This commit is contained in:
parent
599336ed60
commit
312577bbca
@ -123,10 +123,16 @@ function decodeQuotedPrintable(text: string, charset: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parseFullEmail(emailRaw: string) {
|
function parseFullEmail(emailRaw: string) {
|
||||||
|
console.log('=== parseFullEmail Debug ===');
|
||||||
|
console.log('Input email length:', emailRaw.length);
|
||||||
|
console.log('First 200 chars:', emailRaw.substring(0, 200));
|
||||||
|
|
||||||
// Check if this is a multipart message by looking for boundary definition
|
// Check if this is a multipart message by looking for boundary definition
|
||||||
const boundaryMatch = emailRaw.match(/boundary="?([^"\r\n;]+)"?/i) ||
|
const boundaryMatch = emailRaw.match(/boundary="?([^"\r\n;]+)"?/i) ||
|
||||||
emailRaw.match(/boundary=([^\r\n;]+)/i);
|
emailRaw.match(/boundary=([^\r\n;]+)/i);
|
||||||
|
|
||||||
|
console.log('Boundary found:', boundaryMatch ? boundaryMatch[1] : 'No boundary');
|
||||||
|
|
||||||
if (boundaryMatch) {
|
if (boundaryMatch) {
|
||||||
const boundary = boundaryMatch[1].trim();
|
const boundary = boundaryMatch[1].trim();
|
||||||
|
|
||||||
@ -439,18 +445,19 @@ function decodeMimeContent(content: string): string {
|
|||||||
|
|
||||||
// Add this helper function
|
// Add this helper function
|
||||||
const renderEmailContent = (email: Email) => {
|
const renderEmailContent = (email: Email) => {
|
||||||
try {
|
console.log('=== renderEmailContent Debug ===');
|
||||||
console.log('=== Email Content Debug ===');
|
console.log('Email ID:', email.id);
|
||||||
console.log('Raw email body:', email.body.substring(0, 200) + '...'); // First 200 chars
|
console.log('Subject:', email.subject);
|
||||||
console.log('Email ID:', email.id);
|
console.log('Body length:', email.body.length);
|
||||||
console.log('Email subject:', email.subject);
|
console.log('First 200 chars of body:', email.body.substring(0, 200));
|
||||||
|
|
||||||
const parsed = parseFullEmail(email.body) as ParsedEmailContent | ParsedEmailMetadata;
|
try {
|
||||||
console.log('Parsed content type:', 'text' in parsed ? 'ParsedEmailContent' : 'ParsedEmailMetadata');
|
const parsed = parseFullEmail(email.body);
|
||||||
console.log('Parsed content:', {
|
console.log('Parsed content:', {
|
||||||
hasText: 'text' in parsed ? !!parsed.text : false,
|
hasText: 'text' in parsed ? !!parsed.text : false,
|
||||||
hasHtml: 'html' in parsed ? !!parsed.html : false,
|
hasHtml: 'html' in parsed ? !!parsed.html : false,
|
||||||
hasAttachments: 'attachments' in parsed ? parsed.attachments?.length : 0
|
textPreview: 'text' in parsed ? parsed.text?.substring(0, 100) : 'No text',
|
||||||
|
htmlPreview: 'html' in parsed ? parsed.html?.substring(0, 100) : 'No HTML'
|
||||||
});
|
});
|
||||||
|
|
||||||
const content = 'text' in parsed ? parsed.text : ('html' in parsed ? parsed.html || '' : email.body);
|
const content = 'text' in parsed ? parsed.text : ('html' in parsed ? parsed.html || '' : email.body);
|
||||||
@ -556,7 +563,7 @@ const renderEmailContent = (email: Email) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error rendering email content:', e);
|
console.error('Error in renderEmailContent:', e);
|
||||||
return (
|
return (
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">
|
||||||
Error rendering email content. Please try refreshing the page.
|
Error rendering email content. Please try refreshing the page.
|
||||||
@ -1206,130 +1213,92 @@ export default function MailPage() {
|
|||||||
}, [availableFolders]);
|
}, [availableFolders]);
|
||||||
|
|
||||||
// Update the email list item to match header checkbox alignment
|
// Update the email list item to match header checkbox alignment
|
||||||
const renderEmailListItem = (email: Email) => (
|
const renderEmailListItem = (email: Email) => {
|
||||||
<div
|
console.log('=== Email List Item Debug ===');
|
||||||
key={email.id}
|
console.log('Email ID:', email.id);
|
||||||
className={`flex items-center gap-3 px-4 py-2 hover:bg-gray-50/80 cursor-pointer ${
|
console.log('Subject:', email.subject);
|
||||||
selectedEmail?.id === email.id ? 'bg-blue-50/50' : ''
|
console.log('Body length:', email.body.length);
|
||||||
} ${!email.read ? 'bg-blue-50/20' : ''}`}
|
console.log('First 100 chars of body:', email.body.substring(0, 100));
|
||||||
onClick={() => handleEmailSelect(email.id)}
|
|
||||||
>
|
const preview = generateEmailPreview(email);
|
||||||
<Checkbox
|
console.log('Generated preview:', preview);
|
||||||
checked={selectedEmails.includes(email.id.toString())}
|
|
||||||
onCheckedChange={(checked) => {
|
return (
|
||||||
const e = { target: { checked }, stopPropagation: () => {} } as React.ChangeEvent<HTMLInputElement>;
|
<div
|
||||||
handleEmailCheckbox(e, email.id);
|
key={email.id}
|
||||||
}}
|
className={`flex items-center gap-3 px-4 py-2 hover:bg-gray-50/80 cursor-pointer ${
|
||||||
onClick={(e) => e.stopPropagation()}
|
selectedEmail?.id === email.id ? 'bg-blue-50/50' : ''
|
||||||
className="mt-0.5"
|
} ${!email.read ? 'bg-blue-50/20' : ''}`}
|
||||||
/>
|
onClick={() => handleEmailSelect(email.id)}
|
||||||
<div className="flex-1 min-w-0">
|
>
|
||||||
<div className="flex items-center justify-between gap-2">
|
<Checkbox
|
||||||
<div className="flex items-center gap-2 min-w-0">
|
checked={selectedEmails.includes(email.id.toString())}
|
||||||
<span className={`text-sm truncate ${!email.read ? 'font-semibold text-gray-900' : 'text-gray-600'}`}>
|
onCheckedChange={(checked) => {
|
||||||
{currentView === 'Sent' ? email.to : (
|
const e = { target: { checked }, stopPropagation: () => {} } as React.ChangeEvent<HTMLInputElement>;
|
||||||
(() => {
|
handleEmailCheckbox(e, email.id);
|
||||||
const fromMatch = email.from.match(/^([^<]+)\s*<([^>]+)>$/);
|
}}
|
||||||
return fromMatch ? fromMatch[1].trim() : email.from;
|
onClick={(e) => e.stopPropagation()}
|
||||||
})()
|
className="mt-0.5"
|
||||||
)}
|
/>
|
||||||
</span>
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center justify-between gap-2">
|
||||||
|
<div className="flex items-center gap-2 min-w-0">
|
||||||
|
<span className={`text-sm truncate ${!email.read ? 'font-semibold text-gray-900' : 'text-gray-600'}`}>
|
||||||
|
{currentView === 'Sent' ? email.to : (
|
||||||
|
(() => {
|
||||||
|
const fromMatch = email.from.match(/^([^<]+)\s*<([^>]+)>$/);
|
||||||
|
return fromMatch ? fromMatch[1].trim() : email.from;
|
||||||
|
})()
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 flex-shrink-0">
|
||||||
|
<span className="text-xs text-gray-500 whitespace-nowrap">
|
||||||
|
{formatDate(email.date)}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-6 w-6 text-gray-400 hover:text-yellow-400"
|
||||||
|
onClick={(e) => toggleStarred(email.id, e)}
|
||||||
|
>
|
||||||
|
<Star className={`h-4 w-4 ${email.starred ? 'fill-yellow-400 text-yellow-400' : ''}`} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 flex-shrink-0">
|
<h3 className="text-sm text-gray-900 truncate">
|
||||||
<span className="text-xs text-gray-500 whitespace-nowrap">
|
{email.subject || '(No subject)'}
|
||||||
{formatDate(email.date)}
|
</h3>
|
||||||
</span>
|
<div className="text-xs text-gray-500 truncate">
|
||||||
<Button
|
{preview}
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="h-6 w-6 text-gray-400 hover:text-yellow-400"
|
|
||||||
onClick={(e) => toggleStarred(email.id, e)}
|
|
||||||
>
|
|
||||||
<Star className={`h-4 w-4 ${email.starred ? 'fill-yellow-400 text-yellow-400' : ''}`} />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-sm text-gray-900 truncate">
|
|
||||||
{email.subject || '(No subject)'}
|
|
||||||
</h3>
|
|
||||||
<div className="text-xs text-gray-500 truncate">
|
|
||||||
{(() => {
|
|
||||||
try {
|
|
||||||
// First try to parse the full email
|
|
||||||
const parsed = parseFullEmail(email.body);
|
|
||||||
|
|
||||||
// Get text content from parsed email
|
|
||||||
let preview = '';
|
|
||||||
if ('text' in parsed && parsed.text) {
|
|
||||||
preview = parsed.text;
|
|
||||||
} else if ('html' in parsed && parsed.html) {
|
|
||||||
// If only HTML is available, extract text content
|
|
||||||
preview = parsed.html
|
|
||||||
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
|
|
||||||
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
|
|
||||||
.replace(/<[^>]+>/g, ' ')
|
|
||||||
.replace(/\s+/g, ' ')
|
|
||||||
.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no preview from parsed content, try direct body
|
|
||||||
if (!preview) {
|
|
||||||
preview = email.body
|
|
||||||
.replace(/<[^>]+>/g, ' ')
|
|
||||||
.replace(/ |‌|»|«|>/g, ' ')
|
|
||||||
.replace(/\s+/g, ' ')
|
|
||||||
.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up the preview
|
|
||||||
preview = preview
|
|
||||||
.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, '') // 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
|
|
||||||
preview = preview.substring(0, 100);
|
|
||||||
|
|
||||||
// Try to end at a complete word
|
|
||||||
if (preview.length === 100) {
|
|
||||||
const lastSpace = preview.lastIndexOf(' ');
|
|
||||||
if (lastSpace > 80) {
|
|
||||||
preview = preview.substring(0, lastSpace);
|
|
||||||
}
|
|
||||||
preview += '...';
|
|
||||||
}
|
|
||||||
|
|
||||||
return preview || 'No preview available';
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error generating preview:', e);
|
|
||||||
return 'Error loading preview';
|
|
||||||
}
|
|
||||||
})()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
};
|
||||||
|
|
||||||
|
const generateEmailPreview = (email: Email): string => {
|
||||||
|
console.log('=== generateEmailPreview Debug ===');
|
||||||
|
console.log('Email ID:', email.id);
|
||||||
|
console.log('Subject:', email.subject);
|
||||||
|
console.log('Body length:', email.body.length);
|
||||||
|
console.log('First 200 chars of body:', email.body.substring(0, 200));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = parseFullEmail(email.body);
|
||||||
|
console.log('Parsed content:', {
|
||||||
|
hasText: 'text' in parsed ? !!parsed.text : false,
|
||||||
|
hasHtml: 'html' in parsed ? !!parsed.html : false,
|
||||||
|
textPreview: 'text' in parsed ? parsed.text?.substring(0, 100) : 'No text',
|
||||||
|
htmlPreview: 'html' in parsed ? parsed.html?.substring(0, 100) : 'No HTML'
|
||||||
|
});
|
||||||
|
|
||||||
|
// ... rest of the function ...
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error in generateEmailPreview:', e);
|
||||||
|
return 'Error generating preview';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Render the sidebar navigation
|
// Render the sidebar navigation
|
||||||
const renderSidebarNav = () => (
|
const renderSidebarNav = () => (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user