feat: add markdown rendering support for Content component
Detect markdown-formatted posts (from bots etc.) and render them with react-markdown instead of plain text, with feed-friendly styles (flattened headings, compact lists, scrollable tables). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
fc8a160d9a
commit
cc1aa7f989
3 changed files with 196 additions and 7 deletions
|
|
@ -10,6 +10,7 @@ import {
|
|||
parseContent
|
||||
} from '@/lib/content-parser'
|
||||
import { getImetaInfosFromEvent } from '@/lib/event'
|
||||
import { containsMarkdown } from '@/lib/markdown'
|
||||
import { getEmojiInfosFromEmojiTags, getImetaInfoFromImetaTag } from '@/lib/tag'
|
||||
import { cn } from '@/lib/utils'
|
||||
import mediaUpload from '@/services/media-upload.service'
|
||||
|
|
@ -27,6 +28,7 @@ import Emoji from '../Emoji'
|
|||
import ExternalLink from '../ExternalLink'
|
||||
import HighlightButton from '../HighlightButton'
|
||||
import ImageGallery from '../ImageGallery'
|
||||
import MarkdownContent from '../MarkdownContent'
|
||||
import MediaPlayer from '../MediaPlayer'
|
||||
import PostEditor from '../PostEditor'
|
||||
import WebPreview from '../WebPreview'
|
||||
|
|
@ -50,9 +52,11 @@ export default function Content({
|
|||
const [showHighlightEditor, setShowHighlightEditor] = useState(false)
|
||||
const [selectedText, setSelectedText] = useState('')
|
||||
const translatedEvent = useTranslatedEvent(event?.id)
|
||||
const resolvedContent = translatedEvent?.content ?? event?.content ?? content
|
||||
const isMarkdown = useMemo(() => resolvedContent ? containsMarkdown(resolvedContent) : false, [resolvedContent])
|
||||
const { nodes, allImages, lastNormalUrl, emojiInfos } = useMemo(() => {
|
||||
const _content = translatedEvent?.content ?? event?.content ?? content
|
||||
if (!_content) return {}
|
||||
if (!resolvedContent || isMarkdown) return {}
|
||||
const _content = resolvedContent
|
||||
|
||||
const nodes = parseContent(_content, [
|
||||
EmbeddedEventParser,
|
||||
|
|
@ -96,17 +100,42 @@ export default function Content({
|
|||
typeof lastNormalUrlNode?.data === 'string' ? lastNormalUrlNode.data : undefined
|
||||
|
||||
return { nodes, allImages, emojiInfos, lastNormalUrl }
|
||||
}, [event, translatedEvent, content])
|
||||
|
||||
if (!nodes || nodes.length === 0) {
|
||||
return null
|
||||
}
|
||||
}, [event, resolvedContent, isMarkdown])
|
||||
|
||||
const handleHighlight = (text: string) => {
|
||||
setSelectedText(text)
|
||||
setShowHighlightEditor(true)
|
||||
}
|
||||
|
||||
if (!resolvedContent) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (isMarkdown) {
|
||||
return (
|
||||
<>
|
||||
<div ref={contentRef} className={cn('text-wrap break-words', className)}>
|
||||
<MarkdownContent content={resolvedContent} event={event} />
|
||||
</div>
|
||||
{enableHighlight && (
|
||||
<HighlightButton onHighlight={handleHighlight} containerRef={contentRef} />
|
||||
)}
|
||||
{enableHighlight && (
|
||||
<PostEditor
|
||||
highlightedText={selectedText}
|
||||
parentStuff={event}
|
||||
open={showHighlightEditor}
|
||||
setOpen={setShowHighlightEditor}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
if (!nodes || nodes.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
let imageIndex = 0
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue