diff --git a/src/components/nostr/kinds/ReportRenderer.tsx b/src/components/nostr/kinds/ReportRenderer.tsx index 13a6338..7c26315 100644 --- a/src/components/nostr/kinds/ReportRenderer.tsx +++ b/src/components/nostr/kinds/ReportRenderer.tsx @@ -5,7 +5,6 @@ * Reports can target profiles, events, or blobs. */ -import { useMemo } from "react"; import { Flag, AlertTriangle, @@ -17,59 +16,35 @@ import { HelpCircle, } from "lucide-react"; import { BaseEventProps, BaseEventContainer } from "./BaseEventRenderer"; -import { KindRenderer } from "./index"; -import { useNostrEvent } from "@/hooks/useNostrEvent"; +import { QuotedEvent } from "@/components/nostr/QuotedEvent"; import { UserName } from "@/components/nostr/UserName"; -import { EventCardSkeleton } from "@/components/ui/skeleton"; import { - parseReport, + getReportInfo, type ReportType, REPORT_TYPE_LABELS, } from "@/lib/nip56-helpers"; /** - * Get icon for report type + * Get icon for report type (all muted/neutral colors) */ function getReportTypeIcon(reportType: ReportType) { + const className = "size-3.5 text-muted-foreground"; switch (reportType) { case "nudity": - return ; + return ; case "malware": - return ; + return ; case "profanity": - return ; + return ; case "illegal": - return ; + return ; case "spam": - return ; + return ; case "impersonation": - return ; + return ; case "other": default: - return ; - } -} - -/** - * Get background color class for report type badge - */ -function getReportTypeBgClass(reportType: ReportType): string { - switch (reportType) { - case "nudity": - return "bg-orange-500/10 text-orange-600 dark:text-orange-400"; - case "malware": - return "bg-red-500/10 text-red-600 dark:text-red-400"; - case "profanity": - return "bg-yellow-500/10 text-yellow-600 dark:text-yellow-400"; - case "illegal": - return "bg-red-600/10 text-red-700 dark:text-red-300"; - case "spam": - return "bg-blue-500/10 text-blue-600 dark:text-blue-400"; - case "impersonation": - return "bg-purple-500/10 text-purple-600 dark:text-purple-400"; - case "other": - default: - return "bg-muted text-muted-foreground"; + return ; } } @@ -77,17 +52,8 @@ function getReportTypeBgClass(reportType: ReportType): string { * Renderer for Kind 1984 - Reports (NIP-56) */ export function ReportRenderer({ event }: BaseEventProps) { - // Parse the report - const report = useMemo(() => parseReport(event), [event]); - - // Get event pointer if reporting an event - const eventPointer = useMemo(() => { - if (!report?.reportedEventId) return undefined; - return { id: report.reportedEventId }; - }, [report?.reportedEventId]); - - // Fetch reported event if applicable - const reportedEvent = useNostrEvent(eventPointer); + // Parse report using cached helper (no useMemo needed - applesauce caches internally) + const report = getReportInfo(event); if (!report) { return ( @@ -104,13 +70,11 @@ export function ReportRenderer({ event }: BaseEventProps) {
{/* Report header with type badge */}
- + Reported - {/* Report type badge */} - + {/* Report type badge - neutral/muted styling */} + {getReportTypeIcon(report.reportType)} {REPORT_TYPE_LABELS[report.reportType]} @@ -134,18 +98,12 @@ export function ReportRenderer({ event }: BaseEventProps) {
- {/* Embedded reported event */} - {reportedEvent && ( -
- -
- )} - - {/* Loading state */} - {report.reportedEventId && !reportedEvent && ( -
- -
+ {/* Embedded reported event using QuotedEvent */} + {report.reportedEventId && ( + )}
)} diff --git a/src/lib/nip56-helpers.ts b/src/lib/nip56-helpers.ts index 701ba77..c6017f6 100644 --- a/src/lib/nip56-helpers.ts +++ b/src/lib/nip56-helpers.ts @@ -4,9 +4,11 @@ * * A report signals that some referenced content is objectionable. * Reports can target profiles, events, or blobs. + * + * Uses applesauce caching pattern - results are cached on the event object. */ -import { getTagValue } from "applesauce-core/helpers"; +import { getTagValue, getOrComputeCachedValue } from "applesauce-core/helpers"; import type { NostrEvent } from "@/types/nostr"; /** @@ -56,6 +58,10 @@ export const REPORT_TYPE_DESCRIPTIONS: Record = { */ export type ReportTargetType = "profile" | "event" | "blob"; +// Symbols for caching computed values on events +const ParsedReportSymbol = Symbol("parsedReport"); +const ReportLabelsSymbol = Symbol("reportLabels"); + /** * Parsed report information from a kind 1984 event */ @@ -137,42 +143,45 @@ export function getReportServerUrls(event: NostrEvent): string[] { /** * Parse a report event into a structured format + * Uses applesauce caching - result is cached on the event object */ -export function parseReport(event: NostrEvent): ParsedReport | undefined { +export function getReportInfo(event: NostrEvent): ParsedReport | undefined { if (event.kind !== 1984) return undefined; - const reportedPubkey = getReportedPubkey(event); - if (!reportedPubkey) return undefined; + return getOrComputeCachedValue(event, ParsedReportSymbol, () => { + const reportedPubkey = getReportedPubkey(event); + if (!reportedPubkey) return undefined; - const reportType = getReportType(event) || "other"; - const reportedEventId = getReportedEventId(event); - const reportedBlobHash = getReportedBlobHash(event); - const serverUrls = getReportServerUrls(event); + const reportType = getReportType(event) || "other"; + const reportedEventId = getReportedEventId(event); + const reportedBlobHash = getReportedBlobHash(event); + const serverUrls = getReportServerUrls(event); - // Determine blob event ID (e tag when x tag is present) - let blobEventId: string | undefined; - if (reportedBlobHash) { - blobEventId = reportedEventId; - } + // Determine blob event ID (e tag when x tag is present) + let blobEventId: string | undefined; + if (reportedBlobHash) { + blobEventId = reportedEventId; + } - // Determine target type - let targetType: ReportTargetType = "profile"; - if (reportedBlobHash) { - targetType = "blob"; - } else if (reportedEventId) { - targetType = "event"; - } + // Determine target type + let targetType: ReportTargetType = "profile"; + if (reportedBlobHash) { + targetType = "blob"; + } else if (reportedEventId) { + targetType = "event"; + } - return { - reportedPubkey, - reportType, - reportedEventId: targetType === "event" ? reportedEventId : undefined, - reportedBlobHash, - blobEventId, - serverUrls: serverUrls.length > 0 ? serverUrls : undefined, - comment: event.content, - targetType, - }; + return { + reportedPubkey, + reportType, + reportedEventId: targetType === "event" ? reportedEventId : undefined, + reportedBlobHash, + blobEventId, + serverUrls: serverUrls.length > 0 ? serverUrls : undefined, + comment: event.content, + targetType, + }; + }); } /** @@ -184,19 +193,20 @@ export function isValidReportType(type: string): type is ReportType { /** * Get NIP-32 label tags from a report event (optional enhancement) + * Uses applesauce caching - result is cached on the event object */ export function getReportLabels( event: NostrEvent, ): { namespace: string; label: string }[] { - const namespace = getTagValue(event, "L"); - if (!namespace) return []; + return getOrComputeCachedValue(event, ReportLabelsSymbol, () => { + const namespace = getTagValue(event, "L"); + if (!namespace) return []; - const labels = event.tags - .filter((t) => t[0] === "l" && t[2] === namespace) - .map((t) => ({ - namespace, - label: t[1], - })); - - return labels; + return event.tags + .filter((t) => t[0] === "l" && t[2] === namespace) + .map((t) => ({ + namespace, + label: t[1], + })); + }); }