import { useSecondaryPage } from '@/PageManager' import ImageWithLightbox from '@/components/ImageWithLightbox' import { getLongFormArticleMetadataFromEvent } from '@/lib/event-metadata' import { toNoteList } from '@/lib/link' import { ChevronDown, ChevronRight } from 'lucide-react' import { Event, kinds } from 'nostr-tools' import { useMemo, useState, useEffect, useRef } from 'react' import { useEventFieldParser } from '@/hooks/useContentParser' import WebPreview from '../../WebPreview' import HighlightSourcePreview from '../../UniversalContent/HighlightSourcePreview' import { Button } from '@/components/ui/button' import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible' import { ExtendedKind } from '@/constants' export default function Article({ event, className }: { event: Event className?: string }) { const { push } = useSecondaryPage() const metadata = useMemo(() => getLongFormArticleMetadataFromEvent(event), [event]) const [isInfoOpen, setIsInfoOpen] = useState(false) // Determine if this is an article-type event that should show ToC and Article Info const isArticleType = useMemo(() => { return event.kind === kinds.LongFormArticle || event.kind === ExtendedKind.WIKI_ARTICLE || event.kind === ExtendedKind.PUBLICATION || event.kind === ExtendedKind.PUBLICATION_CONTENT }, [event.kind]) // Use the comprehensive content parser const { parsedContent, isLoading, error } = useEventFieldParser(event, 'content', { enableMath: true, enableSyntaxHighlighting: true }) const contentRef = useRef(null) // Handle wikilink clicks useEffect(() => { if (!contentRef.current) return const handleWikilinkClick = (event: MouseEvent) => { const target = event.target as HTMLElement if (target.classList.contains('wikilink')) { event.preventDefault() const dTag = target.getAttribute('data-dtag') const displayText = target.getAttribute('data-display') if (dTag && displayText) { // Create a simple dropdown menu const existingDropdown = document.querySelector('.wikilink-dropdown') if (existingDropdown) { existingDropdown.remove() } const dropdown = document.createElement('div') dropdown.className = 'wikilink-dropdown fixed bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-md shadow-lg z-50 p-2' dropdown.style.left = `${event.pageX}px` dropdown.style.top = `${event.pageY + 10}px` const wikistrButton = document.createElement('button') wikistrButton.className = 'w-full text-left px-3 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-700 rounded flex items-center gap-2' wikistrButton.innerHTML = 'View on Wikistr' wikistrButton.onclick = () => { window.open(`https://wikistr.imwald.eu/${dTag}`, '_blank', 'noopener,noreferrer') dropdown.remove() } const alexandriaButton = document.createElement('button') alexandriaButton.className = 'w-full text-left px-3 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-700 rounded flex items-center gap-2' alexandriaButton.innerHTML = 'View on Alexandria' alexandriaButton.onclick = () => { window.open(`https://next-alexandria.gitcitadel.eu/events?d=${dTag}`, '_blank', 'noopener,noreferrer') dropdown.remove() } dropdown.appendChild(wikistrButton) dropdown.appendChild(alexandriaButton) document.body.appendChild(dropdown) // Close dropdown when clicking outside const closeDropdown = (e: MouseEvent) => { if (!dropdown.contains(e.target as Node)) { dropdown.remove() document.removeEventListener('click', closeDropdown) } } setTimeout(() => document.addEventListener('click', closeDropdown), 0) } } } contentRef.current.addEventListener('click', handleWikilinkClick) return () => { contentRef.current?.removeEventListener('click', handleWikilinkClick) } }, [parsedContent]) // Add ToC return buttons to section headers useEffect(() => { if (!contentRef.current || !isArticleType || !parsedContent) return const addTocReturnButtons = () => { const headers = contentRef.current?.querySelectorAll('h1, h2, h3, h4, h5, h6') if (!headers) return headers.forEach((header) => { // Skip if button already exists if (header.querySelector('.toc-return-btn')) return // Create the return button const returnBtn = document.createElement('span') returnBtn.className = 'toc-return-btn' returnBtn.innerHTML = '↑ ToC' returnBtn.title = 'Return to Table of Contents' // Add click handler returnBtn.addEventListener('click', (e) => { e.preventDefault() e.stopPropagation() // Scroll to the ToC const tocElement = document.getElementById('toc') if (tocElement) { tocElement.scrollIntoView({ behavior: 'smooth', block: 'start' }) } }) // Add the button to the header header.appendChild(returnBtn) }) } // Add buttons after a short delay to ensure content is rendered const timeoutId = setTimeout(addTocReturnButtons, 100) return () => clearTimeout(timeoutId) }, [parsedContent?.html, isArticleType]) if (isLoading) { return (
Loading content...
) } if (error) { return (
Error loading content: {error.message}
) } if (!parsedContent) { return (
No content available
) } return (
{/* Article metadata */}

{metadata.title}

{metadata.summary && (

{metadata.summary}

)} {metadata.image && ( )} {/* Render AsciiDoc content (everything is now processed as AsciiDoc) */}
{/* Collapsible Article Info - only for article-type events */} {isArticleType && (parsedContent.media.length > 0 || parsedContent.links.length > 0 || parsedContent.nostrLinks.length > 0 || parsedContent.highlightSources.length > 0 || parsedContent.hashtags.length > 0) && ( {/* Media thumbnails */} {parsedContent.media.length > 0 && (

Images in this article:

{parsedContent.media.map((media, index) => (
))}
)} {/* Links summary with OpenGraph previews */} {parsedContent.links.length > 0 && (

Links in this article:

{parsedContent.links.map((link, index) => ( ))}
)} {/* Nostr links summary */} {parsedContent.nostrLinks.length > 0 && (

Nostr references:

{parsedContent.nostrLinks.map((link, index) => (
{link.type}:{' '} {link.id}
))}
)} {/* Highlight sources */} {parsedContent.highlightSources.length > 0 && (

Highlight sources:

{parsedContent.highlightSources.map((source, index) => ( ))}
)} {/* Hashtags */} {parsedContent.hashtags.length > 0 && (

Tags:

{parsedContent.hashtags.map((tag) => (
{ e.stopPropagation() push(toNoteList({ hashtag: tag, kinds: [kinds.LongFormArticle] })) }} > #{tag}
))}
)}
)}
) }