이메일 내용 iframe으로 변경

This commit is contained in:
caadiq 2025-12-22 09:36:41 +09:00
parent 730ea717bf
commit 54f6dce97d

View file

@ -2,7 +2,7 @@
* 메일 상세 보기 컴포넌트
* 선택된 메일의 내용, 첨부파일, 액션 버튼 표시
*/
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { useMail } from '../context/MailContext';
import { Trash2, Printer, Star, Reply, Forward, Mail as MailIcon, MailOpen, Archive, Paperclip, Download, FileText, Image, File, Edit, AlertOctagon, Languages, Sparkles } from 'lucide-react';
@ -15,6 +15,103 @@ import { encodeEmailId } from '../utils/emailIdEncoder';
import { HighlightText } from '../utils/highlightText';
import ConfirmDialog from './ConfirmDialog';
/**
* 이메일 HTML을 iframe 안에서 렌더링하는 컴포넌트 (CSS 격리)
*/
const EmailIframe = ({ htmlContent }) => {
const iframeRef = useRef(null);
useEffect(() => {
if (iframeRef.current && htmlContent) {
const doc = iframeRef.current.contentDocument;
if (doc) {
doc.open();
doc.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<base target="_blank">
<style>
html { height: auto !important; overflow: visible !important; }
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #333;
height: auto !important;
overflow: visible !important;
}
img { max-width: 100%; height: auto; }
a { color: #2563eb; }
* { max-width: 100%; box-sizing: border-box; }
table { height: auto !important; }
div, td, tr { height: auto !important; }
</style>
</head>
<body>${htmlContent}</body>
</html>
`);
doc.close();
// iframe -
const resizeIframe = () => {
if (iframeRef.current && doc.body) {
//
const children = doc.body.children;
let maxBottom = 0;
for (let child of children) {
const rect = child.getBoundingClientRect();
const bottom = rect.top + rect.height;
if (bottom > maxBottom) {
maxBottom = bottom;
}
}
// 100px, + 20px
const height = Math.max(maxBottom + 20, 100);
iframeRef.current.style.height = height + 'px';
}
};
//
const images = doc.images;
let loadedCount = 0;
const totalImages = images.length;
if (totalImages === 0) {
resizeIframe();
} else {
for (let img of images) {
img.onload = img.onerror = () => {
loadedCount++;
resizeIframe();
};
}
}
// +
setTimeout(resizeIframe, 50);
setTimeout(resizeIframe, 200);
setTimeout(resizeIframe, 500);
}
}
}, [htmlContent]);
return (
<iframe
ref={iframeRef}
title="email-content"
className="w-full border-0 mb-8"
style={{ minHeight: '100px', overflow: 'hidden' }}
sandbox="allow-same-origin allow-popups allow-popups-to-escape-sandbox"
scrolling="no"
/>
);
};
const MailDetail = ({ onContinueDraft, onReply, onForward }) => {
const navigate = useNavigate();
const { selectedEmail, setSelectedEmail, toggleStar, moveToTrash, markAsUnread, markAsRead, restoreEmail, deleteEmail, selectedBox, moveToSpam, moveEmail, userNames, isSearchMode, searchQuery } = useMail();
@ -590,19 +687,9 @@ const MailDetail = ({ onContinueDraft, onReply, onForward }) => {
const textHasHtml = textContent && /<[a-z][\s\S]*>/i.test(textContent);
if (htmlContent) {
return (
<div
className="email-html-content mb-8 overflow-x-auto"
dangerouslySetInnerHTML={{ __html: htmlContent }}
/>
);
return <EmailIframe htmlContent={htmlContent} />;
} else if (textHasHtml) {
return (
<div
className="email-html-content mb-8 overflow-x-auto"
dangerouslySetInnerHTML={{ __html: textContent }}
/>
);
return <EmailIframe htmlContent={textContent} />;
} else {
return (
<div className="mb-8 p-6 bg-gray-50 rounded-xl border border-gray-100">
@ -646,26 +733,24 @@ const MailDetail = ({ onContinueDraft, onReply, onForward }) => {
const isHtml = /<[a-z][\s\S]*>/i.test(translatedContent);
return (
<div className="relative">
<div className="absolute -top-2 left-4 px-3 py-1 rounded-full text-xs font-semibold text-white shadow-md flex items-center gap-1.5" style={{ background: 'linear-gradient(90deg, #4285f4, #ea4335, #fbbc05, #34a853)' }}>
<div className="absolute -top-2 left-4 px-3 py-1 rounded-full text-xs font-semibold text-white shadow-md flex items-center gap-1.5 z-10" style={{ background: 'linear-gradient(90deg, #4285f4, #ea4335, #fbbc05, #34a853)' }}>
<Sparkles className="w-3 h-3" />
번역됨
</div>
{isHtml ? (
<div
className="email-html-content mb-8 overflow-x-auto rounded-xl p-4 mt-2"
style={{ border: '2px solid transparent', background: 'linear-gradient(white, white) padding-box, linear-gradient(90deg, #4285f4, #ea4335, #fbbc05, #34a853) border-box' }}
dangerouslySetInnerHTML={{ __html: translatedContent }}
/>
) : (
<div
className="mb-8 p-6 rounded-xl mt-2"
style={{ border: '2px solid transparent', background: 'linear-gradient(#f0f9ff, #f0f9ff) padding-box, linear-gradient(90deg, #4285f4, #ea4335, #fbbc05, #34a853) border-box' }}
>
<pre className="whitespace-pre-wrap font-sans text-sm text-gray-700 leading-relaxed">
{translatedContent}
</pre>
</div>
)}
<div
className="rounded-xl mt-2 overflow-hidden"
style={{ border: '2px solid transparent', background: 'linear-gradient(white, white) padding-box, linear-gradient(90deg, #4285f4, #ea4335, #fbbc05, #34a853) border-box' }}
>
{isHtml ? (
<EmailIframe htmlContent={translatedContent} />
) : (
<div className="p-6">
<pre className="whitespace-pre-wrap font-sans text-sm text-gray-700 leading-relaxed">
{translatedContent}
</pre>
</div>
)}
</div>
</div>
);
}