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.
143 lines
4.9 KiB
143 lines
4.9 KiB
import { Event } from 'nostr-tools' |
|
import { Highlighter } from 'lucide-react' |
|
import { nip19 } from 'nostr-tools' |
|
import logger from '@/lib/logger' |
|
import HighlightSourcePreview from '@/components/UniversalContent/HighlightSourcePreview' |
|
|
|
export default function Highlight({ |
|
event, |
|
className |
|
}: { |
|
event: Event |
|
className?: string |
|
}) { |
|
try { |
|
|
|
// Extract the source (e-tag, a-tag, or r-tag) with improved priority handling |
|
let source = null |
|
let sourceTag: string[] | undefined |
|
|
|
// Check for 'source' marker first (highest priority) |
|
for (const tag of event.tags) { |
|
if (tag[2] === 'source' || tag[3] === 'source') { |
|
sourceTag = tag |
|
break |
|
} |
|
} |
|
|
|
// If no 'source' marker found, process tags in priority order: e > a > r |
|
if (!sourceTag) { |
|
for (const tag of event.tags) { |
|
// Give 'e' tags highest priority |
|
if (tag[0] === 'e') { |
|
sourceTag = tag |
|
continue |
|
} |
|
|
|
// Give 'a' tags second priority (but don't override 'e' tags) |
|
if (tag[0] === 'a' && (!sourceTag || sourceTag[0] !== 'e')) { |
|
sourceTag = tag |
|
continue |
|
} |
|
|
|
// Give 'r' tags lowest priority |
|
if (tag[0] === 'r' && (!sourceTag || sourceTag[0] === 'r')) { |
|
sourceTag = tag |
|
continue |
|
} |
|
} |
|
} |
|
|
|
// Process the selected source tag |
|
if (sourceTag) { |
|
if (sourceTag[0] === 'e') { |
|
source = { |
|
type: 'event' as const, |
|
value: sourceTag[1], |
|
bech32: nip19.noteEncode(sourceTag[1]) |
|
} |
|
} else if (sourceTag[0] === 'a') { |
|
const [kind, pubkey, identifier] = sourceTag[1].split(':') |
|
const relay = sourceTag[2] |
|
source = { |
|
type: 'addressable' as const, |
|
value: sourceTag[1], |
|
bech32: nip19.naddrEncode({ |
|
kind: parseInt(kind), |
|
pubkey, |
|
identifier: identifier || '', |
|
relays: relay ? [relay] : [] |
|
}) |
|
} |
|
} else if (sourceTag[0] === 'r') { |
|
source = { |
|
type: 'url' as const, |
|
value: sourceTag[1], |
|
bech32: sourceTag[1] |
|
} |
|
} |
|
} |
|
|
|
// Extract the context (the main quote/full text being highlighted from) |
|
const contextTag = event.tags.find(tag => tag[0] === 'context') |
|
const context = contextTag?.[1] || event.content // Default to content if no context |
|
|
|
// The event.content is the highlighted portion |
|
const highlightedText = event.content |
|
|
|
return ( |
|
<div className={`bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700 p-4 ${className || ''}`}> |
|
<div className="flex-1 min-w-0"> |
|
{/* Full quoted text with highlighted portion */} |
|
{context && ( |
|
<div className="text-base font-normal mb-3 whitespace-pre-wrap break-words"> |
|
{contextTag && highlightedText ? ( |
|
// If we have both context and highlighted text, show the highlight within the context |
|
<div> |
|
{context.split(highlightedText).map((part, index) => ( |
|
<span key={index}> |
|
{part} |
|
{index < context.split(highlightedText).length - 1 && ( |
|
<mark className="bg-green-200 dark:bg-green-800 px-1 rounded"> |
|
{highlightedText} |
|
</mark> |
|
)} |
|
</span> |
|
))} |
|
</div> |
|
) : ( |
|
// If no context tag, just show the content as a regular quote |
|
<blockquote className="italic"> |
|
"{context}" |
|
</blockquote> |
|
)} |
|
</div> |
|
)} |
|
|
|
{/* Source preview card */} |
|
{source && ( |
|
<div className="mt-3"> |
|
<HighlightSourcePreview source={source} className="w-full" /> |
|
</div> |
|
)} |
|
</div> |
|
</div> |
|
) |
|
} catch (error) { |
|
logger.error('Highlight component error', { error, eventId: event.id }) |
|
return ( |
|
<div className={`relative border-l-4 border-red-500 bg-red-50/50 dark:bg-red-950/20 rounded-r-lg p-4 ${className || ''}`}> |
|
<div className="flex items-start gap-3"> |
|
<Highlighter className="w-5 h-5 text-red-600 dark:text-red-500 shrink-0 mt-1" /> |
|
<div className="flex-1 min-w-0"> |
|
<div className="font-bold text-red-800 dark:text-red-200">Highlight Error:</div> |
|
<div className="text-red-700 dark:text-red-300 text-sm">{String(error)}</div> |
|
<div className="mt-2 text-sm">Content: {event.content}</div> |
|
<div className="text-sm">Context: {event.tags.find(tag => tag[0] === 'context')?.[1] || 'No context found'}</div> |
|
</div> |
|
</div> |
|
</div> |
|
) |
|
} |
|
} |
|
|
|
|