diff --git a/src/components/ChatViewer.tsx b/src/components/ChatViewer.tsx index 01d991e..188f4fa 100644 --- a/src/components/ChatViewer.tsx +++ b/src/components/ChatViewer.tsx @@ -9,7 +9,10 @@ import { AlertTriangle, RefreshCw, Paperclip, + Copy, + CopyCheck, } from "lucide-react"; +import { nip19 } from "nostr-tools"; import { getZapRequest } from "applesauce-common/helpers/zap"; import { toast } from "sonner"; import accountManager from "@/services/accounts"; @@ -45,6 +48,7 @@ import { } from "./editor/MentionEditor"; import { useProfileSearch } from "@/hooks/useProfileSearch"; import { useEmojiSearch } from "@/hooks/useEmojiSearch"; +import { useCopy } from "@/hooks/useCopy"; import { Label } from "./ui/label"; import { Tooltip, @@ -129,6 +133,43 @@ function isLiveActivityMetadata(value: unknown): value is LiveActivityMetadata { ); } +/** + * Get the chat command identifier for a conversation + * Returns a string that can be passed to the `chat` command to open this conversation + * + * For NIP-29 groups: relay'group-id (without wss:// prefix) + * For NIP-53 live activities: naddr1... encoding + */ +function getChatIdentifier(conversation: Conversation): string | null { + if (conversation.protocol === "nip-29") { + const groupId = conversation.metadata?.groupId; + const relayUrl = conversation.metadata?.relayUrl; + if (!groupId || !relayUrl) return null; + + // Strip wss:// or ws:// prefix for cleaner identifier + const cleanRelay = relayUrl.replace(/^wss?:\/\//, ""); + return `${cleanRelay}'${groupId}`; + } + + if (conversation.protocol === "nip-53") { + const activityAddress = conversation.metadata?.activityAddress; + if (!activityAddress) return null; + + // Get relay hints from live activity metadata + const liveActivity = conversation.metadata?.liveActivity; + const relays = liveActivity?.relays || []; + + return nip19.naddrEncode({ + kind: activityAddress.kind, + pubkey: activityAddress.pubkey, + identifier: activityAddress.identifier, + relays: relays.slice(0, 3), // Limit relay hints to keep naddr short + }); + } + + return null; +} + /** * Conversation resolution result - either success with conversation or error */ @@ -360,6 +401,9 @@ export function ChatViewer({ // Emoji search for custom emoji autocomplete const { searchEmojis } = useEmojiSearch(); + // Copy chat identifier to clipboard + const { copy: copyChatId, copied: chatIdCopied } = useCopy(); + // Ref to MentionEditor for programmatic submission const editorRef = useRef(null); @@ -811,6 +855,23 @@ export function ChatViewer({ + {/* Copy Chat ID button */} + {getChatIdentifier(conversation) && ( + + )}