You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
4.8 KiB
156 lines
4.8 KiB
/** |
|
* Universal content component that uses the content parser service |
|
* for all Nostr content fields |
|
*/ |
|
|
|
import { useEventFieldParser } from '@/hooks/useContentParser' |
|
import { Event } from 'nostr-tools' |
|
import ImageWithLightbox from '../ImageWithLightbox' |
|
import WebPreview from '../WebPreview' |
|
import HighlightSourcePreview from './HighlightSourcePreview' |
|
|
|
interface ParsedContentProps { |
|
event: Event |
|
field: 'content' | 'title' | 'summary' | 'description' |
|
className?: string |
|
enableMath?: boolean |
|
enableSyntaxHighlighting?: boolean |
|
showMedia?: boolean |
|
showLinks?: boolean |
|
showHashtags?: boolean |
|
showNostrLinks?: boolean |
|
showHighlightSources?: boolean |
|
} |
|
|
|
export default function ParsedContent({ |
|
event, |
|
field, |
|
className = '', |
|
enableMath = true, |
|
enableSyntaxHighlighting = true, |
|
showMedia = true, |
|
showLinks = false, |
|
showHashtags = false, |
|
showNostrLinks = false, |
|
showHighlightSources = false, |
|
}: ParsedContentProps) { |
|
const { parsedContent, isLoading, error } = useEventFieldParser(event, field, { |
|
enableMath, |
|
enableSyntaxHighlighting |
|
}) |
|
|
|
|
|
if (isLoading) { |
|
return ( |
|
<div className={`animate-pulse ${className}`}> |
|
<div className="h-4 bg-muted rounded w-3/4 mb-2"></div> |
|
<div className="h-4 bg-muted rounded w-1/2"></div> |
|
</div> |
|
) |
|
} |
|
|
|
if (error) { |
|
return ( |
|
<div className={`text-red-500 text-sm ${className}`}> |
|
Error loading content: {error.message} |
|
</div> |
|
) |
|
} |
|
|
|
if (!parsedContent) { |
|
return ( |
|
<div className={`text-muted-foreground text-sm ${className}`}> |
|
No content available |
|
</div> |
|
) |
|
} |
|
|
|
return ( |
|
<div className={`${parsedContent.cssClasses} ${className}`}> |
|
{/* Render AsciiDoc content (everything is now processed as AsciiDoc) */} |
|
<div dangerouslySetInnerHTML={{ __html: parsedContent.html }} /> |
|
|
|
{/* Media thumbnails */} |
|
{showMedia && parsedContent.media.length > 0 && ( |
|
<div className="mt-4 p-4 bg-muted rounded-lg"> |
|
<h4 className="text-sm font-semibold mb-3">Images in this content:</h4> |
|
<div className="grid grid-cols-8 sm:grid-cols-12 md:grid-cols-16 gap-1"> |
|
{parsedContent.media.map((media, index) => ( |
|
<div key={index} className="aspect-square"> |
|
<ImageWithLightbox |
|
image={media} |
|
className="w-full h-full object-cover rounded cursor-pointer hover:opacity-80 transition-opacity" |
|
classNames={{ |
|
wrapper: 'w-full h-full' |
|
}} |
|
/> |
|
</div> |
|
))} |
|
</div> |
|
</div> |
|
)} |
|
|
|
{/* Links summary with OpenGraph previews */} |
|
{showLinks && parsedContent.links.length > 0 && ( |
|
<div className="mt-4 p-4 bg-muted rounded-lg"> |
|
<h4 className="text-sm font-semibold mb-3">Links in this content:</h4> |
|
<div className="space-y-3"> |
|
{parsedContent.links.map((link, index) => ( |
|
<WebPreview |
|
key={index} |
|
url={link.url} |
|
className="w-full" |
|
/> |
|
))} |
|
</div> |
|
</div> |
|
)} |
|
|
|
{/* Hashtags */} |
|
{showHashtags && parsedContent.hashtags.length > 0 && ( |
|
<div className="flex gap-2 flex-wrap pb-2"> |
|
{parsedContent.hashtags.map((tag) => ( |
|
<div |
|
key={tag} |
|
title={tag} |
|
className="flex items-center rounded-full px-3 bg-muted text-muted-foreground max-w-44 cursor-pointer hover:bg-accent hover:text-accent-foreground" |
|
> |
|
#<span className="truncate">{tag}</span> |
|
</div> |
|
))} |
|
</div> |
|
)} |
|
|
|
{/* Nostr links summary */} |
|
{showNostrLinks && parsedContent.nostrLinks.length > 0 && ( |
|
<div className="mt-4 p-4 bg-muted rounded-lg"> |
|
<h4 className="text-sm font-semibold mb-2">Nostr references:</h4> |
|
<div className="space-y-1"> |
|
{parsedContent.nostrLinks.map((link, index) => ( |
|
<div key={index} className="text-sm"> |
|
<span className="font-mono text-blue-600">{link.type}:</span>{' '} |
|
<span className="font-mono">{link.id}</span> |
|
</div> |
|
))} |
|
</div> |
|
</div> |
|
)} |
|
|
|
{/* Highlight sources */} |
|
{showHighlightSources && parsedContent.highlightSources.length > 0 && ( |
|
<div className="mt-4 p-4 bg-muted rounded-lg"> |
|
<h4 className="text-sm font-semibold mb-3">Highlight sources:</h4> |
|
<div className="space-y-3"> |
|
{parsedContent.highlightSources.map((source, index) => ( |
|
<HighlightSourcePreview |
|
key={index} |
|
source={source} |
|
className="w-full" |
|
/> |
|
))} |
|
</div> |
|
</div> |
|
)} |
|
</div> |
|
) |
|
}
|
|
|