import { ReactNode } from "react"; import { nip19 } from "@nostr-dev-kit/ndk"; import { useUIStore } from "../../stores/ui"; import { useProfile } from "../../hooks/useProfile"; import { ContentSegment } from "../../lib/parsing"; // Returns true if we handled the URL internally (njump.me interception). export function tryHandleUrlInternally(url: string): boolean { try { const u = new URL(url); if (u.hostname === "njump.me") { const entity = u.pathname.replace(/^\//, ""); if (entity) return tryOpenNostrEntity(entity); } } catch { /* not a valid URL */ } return false; } // Decodes a NIP-19 bech32 string and navigates internally where possible. // Returns true if handled, false if the caller should fall back to a browser open. export function tryOpenNostrEntity(raw: string): boolean { try { const decoded = nip19.decode(raw); const { openProfile, openArticle } = useUIStore.getState(); if (decoded.type === "npub") { openProfile(decoded.data as string); return true; } if (decoded.type === "nprofile") { openProfile((decoded.data as { pubkey: string }).pubkey); return true; } if (decoded.type === "naddr") { const { kind } = decoded.data as { kind: number; pubkey: string; identifier: string }; if (kind === 30023) { openArticle(raw); return true; } } // note / nevent / other naddr kinds — fall through to njump.me } catch { /* invalid entity */ } return false; } export function MentionName({ pubkey, fallback }: { pubkey?: string; fallback: string }) { const profile = useProfile(pubkey ?? ""); if (!pubkey) return <>{fallback}; const name = profile?.displayName || profile?.name; return <>{name || fallback}; } interface RenderTextSegmentsOptions { /** If true, use MentionName component for mentions (inline mode). If false, use seg.display directly. */ resolveMentions?: boolean; } export function renderTextSegments( segments: ContentSegment[], openHashtag: (tag: string) => void, options: RenderTextSegmentsOptions = {} ): ReactNode[] { const { resolveMentions = false } = options; const elements: ReactNode[] = []; segments.forEach((seg, i) => { switch (seg.type) { case "text": elements.push({seg.value}); break; case "link": elements.push( { if (tryHandleUrlInternally(seg.value)) e.preventDefault(); }} > {seg.display} ); break; case "mention": elements.push( { e.stopPropagation(); tryOpenNostrEntity(seg.value); }} > @{resolveMentions ? : seg.display} ); break; case "hashtag": elements.push( { e.stopPropagation(); openHashtag(seg.value); }} > {seg.display} ); break; default: break; } }); return elements; }