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],
+ }));
+ });
}