feat: integrate Markdown into NotificationDetail (#8335)

This commit is contained in:
naman-bruno
2026-06-23 15:24:04 +05:30
committed by GitHub
parent bf0e9bcf23
commit 3f69977176
3 changed files with 51 additions and 51 deletions

View File

@@ -6,9 +6,9 @@ import { isValidUrl } from 'utils/url/index';
import DOMPurify from 'dompurify';
import { useMemo } from 'react';
const Markdown = ({ collectionPath, onDoubleClick, content }) => {
const Markdown = ({ collectionPath, onDoubleClick, content, allowHtml = true }) => {
const markdownItOptions = {
html: true,
html: allowHtml,
breaks: true,
linkify: true,
replaceLink: function (link, env) {
@@ -35,7 +35,7 @@ const Markdown = ({ collectionPath, onDoubleClick, content }) => {
};
const md = new MarkdownIt(markdownItOptions).use(MarkdownItReplaceLink);
const htmlFromMarkdown = useMemo(() => md.render(content || ''), [content, collectionPath]);
const htmlFromMarkdown = useMemo(() => md.render(content || ''), [content, collectionPath, allowHtml]);
const cleanHTML = useMemo(() => DOMPurify.sanitize(htmlFromMarkdown), [htmlFromMarkdown]);
return (

View File

@@ -1,4 +1,4 @@
import DOMPurify from 'dompurify';
import Markdown from 'components/MarkDown';
import { parseToRgb, rgba } from 'polished';
import { useTheme } from 'providers/Theme';
import { humanizeDate } from 'utils/common';
@@ -19,47 +19,9 @@ export const getBadgeStyle = (color, theme) => {
};
};
const getSanitizedDescription = (description) => {
return DOMPurify.sanitize(description || '', {
ALLOWED_TAGS: ['a', 'ul', 'img', 'li', 'div', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'br', 'strong', 'em'],
ALLOWED_ATTR: ['href', 'style', 'target', 'src', 'alt']
});
};
const NotificationDetail = ({ notification }) => {
const { theme } = useTheme();
// Rendered in a sandboxed iframe (no allow-scripts); theme CSS is inlined
// since the iframe doesn't inherit app styles.
const buildDescriptionDocument = (description) => {
const body = getSanitizedDescription(description);
return `<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<base target="_blank" />
<style>
html, body { margin: 0; padding: 0; background: ${theme.notifications.bg}; }
body {
padding: 8px 12px;
font-family: Inter, sans-serif;
font-size: 12px;
line-height: 20px;
font-weight: 500;
color: ${theme.colors.text.muted};
word-break: break-word;
}
p { margin: 0 0 0.75rem 0; }
a { color: ${theme.textLink}; text-decoration: underline; }
h1, h2, h3, h4, h5, h6 { font-size: 13px; font-weight: 600; margin: 0 0 0.5rem 0; color: ${theme.text}; }
ul { padding-left: 1.25rem; margin: 0 0 0.75rem 0; }
img { max-width: 100%; }
</style>
</head>
<body>${body}</body>
</html>`;
};
if (!notification) {
return (
<div className="notif-detail">
@@ -81,13 +43,9 @@ const NotificationDetail = ({ notification }) => {
</div>
<div className="notif-detail-title">{notification.title}</div>
</div>
<iframe
key={notification.id}
className="notif-detail-body"
title="Notification details"
sandbox="allow-popups"
srcDoc={buildDescriptionDocument(notification.description)}
/>
<div key={notification.id} className="notif-detail-body">
<Markdown content={notification.description} allowHtml={false} onDoubleClick={() => {}} />
</div>
</div>
);
};

View File

@@ -244,8 +244,50 @@ const StyledWrapper = styled.div`
flex: 1;
min-height: 0;
width: 100%;
border: none;
background: transparent;
overflow-y: auto;
padding: 8px 12px;
.markdown-body {
background: transparent;
font-size: 12px;
line-height: 20px;
font-weight: 500;
color: ${(props) => props.theme.colors.text.muted};
word-break: break-word;
p {
margin: 0 0 0.75rem 0;
white-space: normal;
}
a {
color: ${(props) => props.theme.textLink};
text-decoration: underline;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: 13px;
font-weight: 600;
margin: 0 0 0.5rem 0;
padding: 0;
border: none;
color: ${(props) => props.theme.text};
}
ul {
padding-left: 1.25rem;
margin: 0 0 0.75rem 0;
}
img {
max-width: 100%;
}
}
}
.notif-empty {