mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-09 15:07:10 +02:00
feat: event dialog
This commit is contained in:
@@ -3,7 +3,7 @@ import type { EventPointer, AddressPointer } from "nostr-tools/nip19";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { DetailKindRenderer } from "./nostr/kinds";
|
||||
import { EventErrorBoundary } from "./EventErrorBoundary";
|
||||
import { JsonViewer } from "./JsonViewer";
|
||||
import { EventJsonDialog } from "./EventJsonDialog";
|
||||
import { RelayLink } from "./nostr/RelayLink";
|
||||
import { EventDetailSkeleton } from "@/components/ui/skeleton";
|
||||
import { Copy, CopyCheck, FileJson, Wifi } from "lucide-react";
|
||||
@@ -178,11 +178,10 @@ export function EventDetailViewer({ pointer }: EventDetailViewerProps) {
|
||||
</div>
|
||||
|
||||
{/* JSON Viewer Dialog */}
|
||||
<JsonViewer
|
||||
data={event}
|
||||
<EventJsonDialog
|
||||
event={event}
|
||||
open={showJson}
|
||||
onOpenChange={setShowJson}
|
||||
title="Event JSON"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
114
src/components/EventJsonDialog.tsx
Normal file
114
src/components/EventJsonDialog.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { useCopy } from "@/hooks/useCopy";
|
||||
import { useProfile } from "@/hooks/useProfile";
|
||||
import { getDisplayName } from "@/lib/nostr-utils";
|
||||
import { getKindName } from "@/constants/kinds";
|
||||
import { Copy, CopyCheck } from "lucide-react";
|
||||
import { CodeCopyButton } from "@/components/CodeCopyButton";
|
||||
import { SyntaxHighlight } from "@/components/SyntaxHighlight";
|
||||
import { isAddressableKind } from "@/lib/nostr-kinds";
|
||||
import { getSeenRelays } from "applesauce-core/helpers/relays";
|
||||
import { getTagValue } from "applesauce-core/helpers";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import type { NostrEvent } from "@/types/nostr";
|
||||
|
||||
interface EventJsonDialogProps {
|
||||
event: NostrEvent;
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export function EventJsonDialog({
|
||||
event,
|
||||
open,
|
||||
onOpenChange,
|
||||
}: EventJsonDialogProps) {
|
||||
const profile = useProfile(event.pubkey);
|
||||
const { copy: copyBech32, copied: copiedBech32 } = useCopy();
|
||||
const { copy: copyJson, copied: copiedJson } = useCopy();
|
||||
|
||||
const displayName = getDisplayName(event.pubkey, profile);
|
||||
const kindName = getKindName(event.kind);
|
||||
|
||||
const bech32Id = useMemo(() => {
|
||||
const seenRelaysSet = getSeenRelays(event);
|
||||
const relays = seenRelaysSet ? Array.from(seenRelaysSet) : [];
|
||||
|
||||
return isAddressableKind(event.kind)
|
||||
? nip19.naddrEncode({
|
||||
kind: event.kind,
|
||||
pubkey: event.pubkey,
|
||||
identifier: getTagValue(event, "d") || "",
|
||||
relays,
|
||||
})
|
||||
: nip19.neventEncode({
|
||||
id: event.id,
|
||||
author: event.pubkey,
|
||||
relays,
|
||||
});
|
||||
}, [event]);
|
||||
|
||||
const jsonString = useMemo(() => JSON.stringify(event, null, 2), [event]);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-3xl max-h-[80vh] flex flex-col rounded-none">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{displayName} - {kindName} (kind {event.kind})
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex flex-col gap-3 flex-1 overflow-hidden">
|
||||
{/* Nostr ID */}
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-xs font-medium text-muted-foreground">
|
||||
Nostr ID
|
||||
</span>
|
||||
<div className="flex items-center gap-2 bg-muted p-2 rounded-sm">
|
||||
<code className="font-mono text-xs truncate flex-1 min-w-0">
|
||||
{bech32Id}
|
||||
</code>
|
||||
<button
|
||||
onClick={() => copyBech32(bech32Id)}
|
||||
className="flex-shrink-0 p-1 hover:bg-background/50 rounded transition-colors"
|
||||
aria-label="Copy Nostr ID"
|
||||
>
|
||||
{copiedBech32 ? (
|
||||
<CopyCheck className="size-4 text-muted-foreground" />
|
||||
) : (
|
||||
<Copy className="size-4 text-muted-foreground" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* JSON */}
|
||||
<div className="flex flex-col gap-1 flex-1 overflow-hidden">
|
||||
<span className="text-xs font-medium text-muted-foreground">
|
||||
JSON
|
||||
</span>
|
||||
<div className="flex-1 overflow-auto relative">
|
||||
<SyntaxHighlight
|
||||
code={jsonString}
|
||||
language="json"
|
||||
className="bg-muted p-4 pr-10 overflow-scroll"
|
||||
/>
|
||||
<CodeCopyButton
|
||||
onCopy={() => copyJson(jsonString)}
|
||||
copied={copiedJson}
|
||||
label="Copy JSON"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { useAddWindow } from "@/core/state";
|
||||
import { useCopy } from "@/hooks/useCopy";
|
||||
import { JsonViewer } from "@/components/JsonViewer";
|
||||
import { EventJsonDialog } from "@/components/EventJsonDialog";
|
||||
import { KindBadge } from "@/components/KindBadge";
|
||||
import { EmojiPickerDialog } from "./EmojiPickerDialog";
|
||||
import { nip19 } from "nostr-tools";
|
||||
@@ -224,11 +224,10 @@ export function ChatMessageContextMenu({
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
<JsonViewer
|
||||
data={event}
|
||||
<EventJsonDialog
|
||||
event={event}
|
||||
open={jsonDialogOpen}
|
||||
onOpenChange={setJsonDialogOpen}
|
||||
title={`Event ${event.id.slice(0, 8)}... - Raw JSON`}
|
||||
/>
|
||||
{conversation && adapter && (
|
||||
<EmojiPickerDialog
|
||||
|
||||
@@ -30,7 +30,7 @@ import { useAddWindow, useGrimoire } from "@/core/state";
|
||||
import { useCopy } from "@/hooks/useCopy";
|
||||
import { useAccount } from "@/hooks/useAccount";
|
||||
import { useSettings } from "@/hooks/useSettings";
|
||||
import { JsonViewer } from "@/components/JsonViewer";
|
||||
import { EventJsonDialog } from "@/components/EventJsonDialog";
|
||||
import { EmojiPickerDialog } from "@/components/chat/EmojiPickerDialog";
|
||||
import { formatTimestamp } from "@/hooks/useLocale";
|
||||
import { nip19 } from "nostr-tools";
|
||||
@@ -295,11 +295,10 @@ export function EventMenu({
|
||||
View JSON
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
<JsonViewer
|
||||
data={event}
|
||||
<EventJsonDialog
|
||||
event={event}
|
||||
open={jsonDialogOpen}
|
||||
onOpenChange={setJsonDialogOpen}
|
||||
title={`Event ${event.id.slice(0, 8)}... - Raw JSON`}
|
||||
/>
|
||||
</DropdownMenu>
|
||||
);
|
||||
@@ -475,11 +474,10 @@ export function EventContextMenu({
|
||||
View JSON
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
<JsonViewer
|
||||
data={event}
|
||||
<EventJsonDialog
|
||||
event={event}
|
||||
open={jsonDialogOpen}
|
||||
onOpenChange={setJsonDialogOpen}
|
||||
title={`Event ${event.id.slice(0, 8)}... - Raw JSON`}
|
||||
/>
|
||||
</ContextMenu>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user