mirror of
https://github.com/dergigi/boris.git
synced 2025-12-30 21:14:48 +01:00
- Add articleTitleResolver service to fetch article titles from relays - Extract naddr identifiers from markdown content - Fetch article titles in parallel using relay pool - Replace naddr references with actual article titles - Fallback to identifier if title fetch fails - Update markdown processing to be async for title resolution - Pass relayPool through component tree to enable resolution Example: nostr:naddr1... now shows as "My Article Title" instead of "article:identifier" Improves readability by showing human-friendly article titles in cross-references
88 lines
2.7 KiB
TypeScript
88 lines
2.7 KiB
TypeScript
import React, { useState, useEffect, useRef } from 'react'
|
|
import { RelayPool } from 'applesauce-relay'
|
|
import { extractNaddrUris, replaceNostrUrisInMarkdown, replaceNostrUrisInMarkdownWithTitles } from '../utils/nostrUriResolver'
|
|
import { fetchArticleTitles } from '../services/articleTitleResolver'
|
|
|
|
/**
|
|
* Hook to convert markdown to HTML using a hidden ReactMarkdown component
|
|
* Also processes nostr: URIs in the markdown and resolves article titles
|
|
*/
|
|
export const useMarkdownToHTML = (
|
|
markdown?: string,
|
|
relayPool?: RelayPool | null
|
|
): {
|
|
renderedHtml: string
|
|
previewRef: React.RefObject<HTMLDivElement>
|
|
processedMarkdown: string
|
|
} => {
|
|
const previewRef = useRef<HTMLDivElement>(null)
|
|
const [renderedHtml, setRenderedHtml] = useState<string>('')
|
|
const [processedMarkdown, setProcessedMarkdown] = useState<string>('')
|
|
|
|
useEffect(() => {
|
|
if (!markdown) {
|
|
setRenderedHtml('')
|
|
setProcessedMarkdown('')
|
|
return
|
|
}
|
|
|
|
let isCancelled = false
|
|
|
|
const processMarkdown = async () => {
|
|
// Extract all naddr references
|
|
const naddrs = extractNaddrUris(markdown)
|
|
|
|
let processed: string
|
|
|
|
if (naddrs.length > 0 && relayPool) {
|
|
// Fetch article titles for all naddrs
|
|
try {
|
|
const articleTitles = await fetchArticleTitles(relayPool, naddrs)
|
|
|
|
if (isCancelled) return
|
|
|
|
// Replace nostr URIs with resolved titles
|
|
processed = replaceNostrUrisInMarkdownWithTitles(markdown, articleTitles)
|
|
console.log(`📚 Resolved ${articleTitles.size} article titles`)
|
|
} catch (error) {
|
|
console.warn('Failed to fetch article titles:', error)
|
|
// Fall back to basic replacement
|
|
processed = replaceNostrUrisInMarkdown(markdown)
|
|
}
|
|
} else {
|
|
// No articles to resolve, use basic replacement
|
|
processed = replaceNostrUrisInMarkdown(markdown)
|
|
}
|
|
|
|
if (isCancelled) return
|
|
|
|
setProcessedMarkdown(processed)
|
|
|
|
console.log('📝 Converting markdown to HTML...')
|
|
|
|
const rafId = requestAnimationFrame(() => {
|
|
if (previewRef.current && !isCancelled) {
|
|
const html = previewRef.current.innerHTML
|
|
console.log('✅ Markdown converted to HTML:', html.length, 'chars')
|
|
setRenderedHtml(html)
|
|
} else if (!isCancelled) {
|
|
console.warn('⚠️ markdownPreviewRef.current is null')
|
|
}
|
|
})
|
|
|
|
return () => cancelAnimationFrame(rafId)
|
|
}
|
|
|
|
processMarkdown()
|
|
|
|
return () => {
|
|
isCancelled = true
|
|
}
|
|
}, [markdown, relayPool])
|
|
|
|
return { renderedHtml, previewRef, processedMarkdown }
|
|
}
|
|
|
|
// Removed separate useMarkdownPreviewRef; use useMarkdownToHTML to obtain previewRef
|
|
|