ui: only show direct replied to

This commit is contained in:
Alejandro Gómez
2025-12-17 09:48:11 +01:00
parent ce18567900
commit 8fe0ffd5c3
2 changed files with 41 additions and 162 deletions

View File

@@ -2,14 +2,13 @@ import { RichText } from "../RichText";
import { BaseEventContainer, type BaseEventProps } from "./BaseEventRenderer";
import {
getCommentReplyPointer,
getCommentRootPointer,
isCommentAddressPointer,
isCommentEventPointer,
type CommentPointer,
} from "applesauce-core/helpers/comment";
import { useNostrEvent } from "@/hooks/useNostrEvent";
import { UserName } from "../UserName";
import { Reply, MessageSquare } from "lucide-react";
import { Reply } from "lucide-react";
import { useGrimoire } from "@/core/state";
import { InlineReplySkeleton } from "@/components/ui/skeleton";
import { KindBadge } from "@/components/KindBadge";
@@ -45,36 +44,6 @@ function convertCommentPointer(
return undefined;
}
/**
* Check if two pointers reference the same event
*/
function isSamePointer(
pointer1:
| { id: string }
| { kind: number; pubkey: string; identifier: string }
| undefined,
pointer2:
| { id: string }
| { kind: number; pubkey: string; identifier: string }
| undefined,
): boolean {
if (!pointer1 || !pointer2) return false;
if ("id" in pointer1 && "id" in pointer2) {
return pointer1.id === pointer2.id;
}
if ("kind" in pointer1 && "kind" in pointer2) {
return (
pointer1.kind === pointer2.kind &&
pointer1.pubkey === pointer2.pubkey &&
pointer1.identifier === pointer2.identifier
);
}
return false;
}
/**
* Parent event card component - compact single line
*/
@@ -89,6 +58,9 @@ function ParentEventCard({
tooltipText: string;
onClickHandler: () => void;
}) {
// Don't show kind badge for kind 1 (most common, adds clutter)
const showKindBadge = parentEvent.kind !== 1;
return (
<div
onClick={onClickHandler}
@@ -102,7 +74,7 @@ function ParentEventCard({
<p>{tooltipText}</p>
</TooltipContent>
</Tooltip>
<KindBadge kind={parentEvent.kind} variant="compact" />
{showKindBadge && <KindBadge kind={parentEvent.kind} variant="compact" />}
<UserName
pubkey={parentEvent.pubkey}
className="text-accent font-semibold flex-shrink-0"
@@ -121,73 +93,41 @@ function ParentEventCard({
/**
* Renderer for Kind 1111 - Post (NIP-22)
* Shows parent event with kind icon, author, and title in reply-style format
* If both root and reply exist and are different, shows both (root first, then reply)
* Shows immediate parent (reply) only for cleaner display
*/
export function Kind1111Renderer({ event, depth = 0 }: BaseEventProps) {
const { addWindow } = useGrimoire();
// Use NIP-22 specific helpers to get reply/root pointers
// Use NIP-22 specific helpers to get reply pointer
const replyPointerRaw = getCommentReplyPointer(event);
const rootPointerRaw = getCommentRootPointer(event);
// Convert to useNostrEvent format
const rootPointer = convertCommentPointer(rootPointerRaw);
const replyPointer = convertCommentPointer(replyPointerRaw);
// Fetch both events
const rootEvent = useNostrEvent(rootPointer, event);
// Fetch reply event
const replyEvent = useNostrEvent(replyPointer, event);
// Check if root and reply are different events
const hasDistinctReply =
rootPointer &&
replyPointer &&
!isSamePointer(rootPointer, replyPointer) &&
rootEvent &&
replyEvent;
const handleRootClick = () => {
if (!rootEvent || !rootPointer) return;
addWindow("open", { pointer: rootPointer });
};
const handleReplyClick = () => {
if (!replyEvent || !replyPointer) return;
addWindow(
"open",
{ pointer: replyPointer },
);
addWindow("open", { pointer: replyPointer });
};
return (
<BaseEventContainer event={event}>
<TooltipProvider>
<div className="flex flex-col gap-0.5">
{/* Show root event (thread origin) */}
{rootPointer && !rootEvent && (
<InlineReplySkeleton icon={<MessageSquare className="size-3" />} />
)}
{/* Show reply event (immediate parent) */}
{replyPointer && !replyEvent && (
<InlineReplySkeleton icon={<Reply className="size-3" />} />
)}
{rootPointer && rootEvent && (
<ParentEventCard
parentEvent={rootEvent}
icon={MessageSquare}
tooltipText="Thread root"
onClickHandler={handleRootClick}
/>
)}
{/* Show reply event (immediate parent) if different from root */}
{hasDistinctReply && replyPointer && (
<ParentEventCard
parentEvent={replyEvent}
icon={Reply}
tooltipText="Replying to"
onClickHandler={handleReplyClick}
/>
)}
</div>
{replyPointer && replyEvent && (
<ParentEventCard
parentEvent={replyEvent}
icon={Reply}
tooltipText="Replying to"
onClickHandler={handleReplyClick}
/>
)}
</TooltipProvider>
<RichText event={event} className="text-sm" depth={depth} />

View File

@@ -3,7 +3,7 @@ import { BaseEventContainer, type BaseEventProps } from "./BaseEventRenderer";
import { getNip10References } from "applesauce-core/helpers/threading";
import { useNostrEvent } from "@/hooks/useNostrEvent";
import { UserName } from "../UserName";
import { Reply, MessageSquare } from "lucide-react";
import { Reply } from "lucide-react";
import { useGrimoire } from "@/core/state";
import { InlineReplySkeleton } from "@/components/ui/skeleton";
import { KindBadge } from "@/components/KindBadge";
@@ -15,42 +15,6 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import type { EventPointer } from "nostr-tools/nip19";
import type { AddressPointer } from "nostr-tools/nip19";
/**
* Check if two pointers reference the same event
*/
function isSamePointer(
pointer1:
| { e: EventPointer; a: undefined }
| { e: undefined; a: AddressPointer }
| { e: EventPointer; a: AddressPointer }
| undefined,
pointer2:
| { e: EventPointer; a: undefined }
| { e: undefined; a: AddressPointer }
| { e: EventPointer; a: AddressPointer }
| undefined,
): boolean {
if (!pointer1 || !pointer2) return false;
// Compare event pointers
if (pointer1.e && pointer2.e) {
return pointer1.e.id === pointer2.e.id;
}
// Compare address pointers
if (pointer1.a && pointer2.a) {
return (
pointer1.a.kind === pointer2.a.kind &&
pointer1.a.pubkey === pointer2.a.pubkey &&
pointer1.a.identifier === pointer2.a.identifier
);
}
return false;
}
/**
* Parent event card component - compact single line
@@ -66,6 +30,9 @@ function ParentEventCard({
tooltipText: string;
onClickHandler: () => void;
}) {
// Don't show kind badge for kind 1 (most common, adds clutter)
const showKindBadge = parentEvent.kind !== 1;
return (
<div
onClick={onClickHandler}
@@ -79,7 +46,7 @@ function ParentEventCard({
<p>{tooltipText}</p>
</TooltipContent>
</Tooltip>
<KindBadge kind={parentEvent.kind} variant="compact" />
{showKindBadge && <KindBadge kind={parentEvent.kind} variant="compact" />}
<UserName
pubkey={parentEvent.pubkey}
className="text-accent font-semibold flex-shrink-0"
@@ -98,8 +65,7 @@ function ParentEventCard({
/**
* Renderer for Kind 1 - Short Text Note (NIP-10 threading)
* Shows parent event with kind icon, author, and title in reply-style format
* If both root and reply exist and are different, shows both (root first, then reply)
* Shows immediate parent (reply) only for cleaner display
*/
export function Kind1Renderer({ event, depth = 0 }: BaseEventProps) {
const { addWindow } = useGrimoire();
@@ -107,27 +73,12 @@ export function Kind1Renderer({ event, depth = 0 }: BaseEventProps) {
// Use NIP-10 threading helpers
const refs = getNip10References(event);
// Get pointers for root and reply
const rootPointer = refs.root?.e || refs.root?.a;
// Get reply pointer (immediate parent)
const replyPointer = refs.reply?.e || refs.reply?.a;
// Fetch both events
const rootEvent = useNostrEvent(rootPointer, event);
// Fetch reply event
const replyEvent = useNostrEvent(replyPointer, event);
// Check if root and reply are different events
const hasDistinctReply =
refs.root &&
refs.reply &&
!isSamePointer(refs.root, refs.reply) &&
rootEvent &&
replyEvent;
const handleRootClick = () => {
if (!rootEvent || !rootPointer) return;
addWindow("open", { pointer: rootPointer }, `Thread root`);
};
const handleReplyClick = () => {
if (!replyEvent || !replyPointer) return;
addWindow(
@@ -140,31 +91,19 @@ export function Kind1Renderer({ event, depth = 0 }: BaseEventProps) {
return (
<BaseEventContainer event={event}>
<TooltipProvider>
<div className="flex flex-col gap-0.5">
{/* Show root event (thread origin) */}
{rootPointer && !rootEvent && (
<InlineReplySkeleton icon={<MessageSquare className="size-3" />} />
)}
{/* Show reply event (immediate parent) */}
{replyPointer && !replyEvent && (
<InlineReplySkeleton icon={<Reply className="size-3" />} />
)}
{rootPointer && rootEvent && (
<ParentEventCard
parentEvent={rootEvent}
icon={MessageSquare}
tooltipText="Thread root"
onClickHandler={handleRootClick}
/>
)}
{/* Show reply event (immediate parent) if different from root */}
{hasDistinctReply && replyPointer && (
<ParentEventCard
parentEvent={replyEvent}
icon={Reply}
tooltipText="Replying to"
onClickHandler={handleReplyClick}
/>
)}
</div>
{replyPointer && replyEvent && (
<ParentEventCard
parentEvent={replyEvent}
icon={Reply}
tooltipText="Replying to"
onClickHandler={handleReplyClick}
/>
)}
</TooltipProvider>
<RichText event={event} className="text-sm" depth={depth} />