mirror of
https://github.com/hoornet/vega.git
synced 2026-04-29 09:10:00 -07:00
Split client.ts (1036 lines) into 11 domain modules under lib/nostr/ — core, notes, social, articles, engagement, dms, bookmarks, muting, search, relays, trending. Barrel index.ts re-exports all; zero consumer import changes. Extract ProfileView sub-components (ImageField, Nip05Field, EditProfileForm, ProfileMediaGallery), NoteContent renderers (TextSegments, MediaCards), and NoteCard actions (NoteActions, InlineReplyBox). All component files now ≤270 lines, all lib files ≤300.
117 lines
3.6 KiB
TypeScript
117 lines
3.6 KiB
TypeScript
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(<span key={i}>{seg.value}</span>);
|
|
break;
|
|
case "link":
|
|
elements.push(
|
|
<a
|
|
key={i}
|
|
href={seg.value}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-accent hover:text-accent-hover underline underline-offset-2 decoration-accent/40"
|
|
onClick={(e) => {
|
|
if (tryHandleUrlInternally(seg.value)) e.preventDefault();
|
|
}}
|
|
>
|
|
{seg.display}
|
|
</a>
|
|
);
|
|
break;
|
|
case "mention":
|
|
elements.push(
|
|
<span
|
|
key={i}
|
|
className="text-accent cursor-pointer hover:text-accent-hover"
|
|
onClick={(e) => { e.stopPropagation(); tryOpenNostrEntity(seg.value); }}
|
|
>
|
|
@{resolveMentions
|
|
? <MentionName pubkey={seg.mentionPubkey} fallback={seg.display ?? seg.value.slice(0, 12) + "…"} />
|
|
: seg.display}
|
|
</span>
|
|
);
|
|
break;
|
|
case "hashtag":
|
|
elements.push(
|
|
<span
|
|
key={i}
|
|
className="text-accent/80 cursor-pointer hover:text-accent"
|
|
onClick={(e) => { e.stopPropagation(); openHashtag(seg.value); }}
|
|
>
|
|
{seg.display}
|
|
</span>
|
|
);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
|
|
return elements;
|
|
}
|