feat(nip56): hide preview and clickable header

- Add hidePreview prop to QuotedEvent for sensitive content
- Hide text preview when collapsed, show "Click to reveal content"
- Make report header clickable to open report detail
- UserName stops propagation so clicking username opens profile

https://claude.ai/code/session_012ux81GyM8iZ1GLnKHC7esJ
This commit is contained in:
Claude
2026-01-23 12:30:51 +00:00
parent f2267fec3d
commit 98bb7606cc
2 changed files with 31 additions and 11 deletions

View File

@@ -18,6 +18,8 @@ interface QuotedEventProps {
depth?: number;
/** Optional className for container */
className?: string;
/** Hide preview text when collapsed (for sensitive content) */
hidePreview?: boolean;
}
/**
@@ -31,6 +33,7 @@ export function QuotedEvent({
onOpen,
depth = 1,
className,
hidePreview = false,
}: QuotedEventProps) {
const [isExpanded, setIsExpanded] = useState(depth < 2);
@@ -99,10 +102,17 @@ export function QuotedEvent({
>
<div className="flex items-center gap-2 min-w-0">
<UserName pubkey={event.pubkey} className="text-xs font-medium" />
<span className="text-xs text-muted-foreground truncate">
{previewText}
{hasMore && "..."}
</span>
{!hidePreview && (
<span className="text-xs text-muted-foreground truncate">
{previewText}
{hasMore && "..."}
</span>
)}
{hidePreview && !isExpanded && (
<span className="text-xs text-muted-foreground italic">
Click to reveal content
</span>
)}
</div>
{isExpanded ? (
<ChevronUp className="size-3 flex-shrink-0" />

View File

@@ -18,6 +18,7 @@ import {
import { BaseEventProps, BaseEventContainer } from "./BaseEventRenderer";
import { QuotedEvent } from "@/components/nostr/QuotedEvent";
import { UserName } from "@/components/nostr/UserName";
import { useGrimoire } from "@/core/state";
import {
getReportInfo,
type ReportType,
@@ -52,6 +53,7 @@ function getReportTypeIcon(reportType: ReportType) {
* Renderer for Kind 1984 - Reports (NIP-56)
*/
export function ReportRenderer({ event }: BaseEventProps) {
const { addWindow } = useGrimoire();
// Parse report using cached helper (no useMemo needed - applesauce caches internally)
const report = getReportInfo(event);
@@ -67,26 +69,34 @@ export function ReportRenderer({ event }: BaseEventProps) {
const reasonLabel = REPORT_TYPE_LABELS[report.reportType].toLowerCase();
// Open report detail view
const openReportDetail = (e: React.MouseEvent) => {
e.stopPropagation();
addWindow("open", { pointer: { id: event.id } });
};
return (
<BaseEventContainer event={event}>
<div className="flex flex-col gap-3">
{/* Report header: "Reported <username> for <reason>" */}
<div className="flex items-center gap-1.5 flex-wrap text-sm">
{/* Report header: "Reported <username> for <reason>" - whole line clickable */}
<button
onClick={openReportDetail}
className="flex items-center gap-1.5 flex-wrap text-sm text-left hover:bg-muted/30 -mx-1 px-1 py-0.5 rounded cursor-pointer"
>
<Flag className="size-4 text-muted-foreground flex-shrink-0" />
<span className="text-muted-foreground">Reported</span>
<UserName pubkey={report.reportedPubkey} />
<span className="text-muted-foreground">for</span>
<span className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-xs font-medium bg-muted text-muted-foreground">
{getReportTypeIcon(report.reportType)}
{reasonLabel}
for {getReportTypeIcon(report.reportType)} {reasonLabel}
</span>
</div>
</button>
{/* Reported event - collapsed by default (depth=2) */}
{/* Reported event - collapsed with hidden preview (depth=2, hidePreview) */}
{report.targetType === "event" && report.reportedEventId && (
<QuotedEvent
eventPointer={{ id: report.reportedEventId }}
depth={2}
hidePreview
/>
)}