+ {/* Header */}
+
+
+
+
Encrypted DMs
+
+ ({conversations.length})
+
+
+
+
+
+ {/* Conversation list */}
+
+ {conversations.map((summary) => (
+ handleSelectConversation(summary.partnerPubkey)}
+ />
+ ))}
+
+
+ {/* Footer hint */}
+
+
+ Click a conversation to open it, or use{" "}
+ chat npub... for
+ new chats
+
+
+
+ );
+}
diff --git a/src/lib/chat-parser.test.ts b/src/lib/chat-parser.test.ts
index d0f430c..7940042 100644
--- a/src/lib/chat-parser.test.ts
+++ b/src/lib/chat-parser.test.ts
@@ -55,24 +55,33 @@ describe("parseChatCommand", () => {
const result = parseChatCommand(["relay.example.com'bitcoin-dev"]);
expect(result.protocol).toBe("nip-29");
- expect(result.identifier.value).toBe("bitcoin-dev");
- expect(result.identifier.relays).toEqual(["wss://relay.example.com"]);
+ expect(result.identifier.type).toBe("group");
+ if (result.identifier.type === "group") {
+ expect(result.identifier.value).toBe("bitcoin-dev");
+ expect(result.identifier.relays).toEqual(["wss://relay.example.com"]);
+ }
});
it("should parse NIP-29 group with different relay when split", () => {
const result = parseChatCommand(["relay.example.com", "bitcoin-dev"]);
expect(result.protocol).toBe("nip-29");
- expect(result.identifier.value).toBe("bitcoin-dev");
- expect(result.identifier.relays).toEqual(["wss://relay.example.com"]);
+ expect(result.identifier.type).toBe("group");
+ if (result.identifier.type === "group") {
+ expect(result.identifier.value).toBe("bitcoin-dev");
+ expect(result.identifier.relays).toEqual(["wss://relay.example.com"]);
+ }
});
it("should parse NIP-29 group from nos.lol", () => {
const result = parseChatCommand(["nos.lol'welcome"]);
expect(result.protocol).toBe("nip-29");
- expect(result.identifier.value).toBe("welcome");
- expect(result.identifier.relays).toEqual(["wss://nos.lol"]);
+ expect(result.identifier.type).toBe("group");
+ if (result.identifier.type === "group") {
+ expect(result.identifier.value).toBe("welcome");
+ expect(result.identifier.relays).toEqual(["wss://nos.lol"]);
+ }
});
});
@@ -146,16 +155,19 @@ describe("parseChatCommand", () => {
const result = parseChatCommand([naddr]);
expect(result.protocol).toBe("nip-53");
- expect(result.identifier.value).toEqual({
- kind: 30311,
- pubkey:
- "0000000000000000000000000000000000000000000000000000000000000001",
- identifier: "podcast-episode-42",
- });
- expect(result.identifier.relays).toEqual([
- "wss://relay1.example.com",
- "wss://relay2.example.com",
- ]);
+ expect(result.identifier.type).toBe("live-activity");
+ if (result.identifier.type === "live-activity") {
+ expect(result.identifier.value).toEqual({
+ kind: 30311,
+ pubkey:
+ "0000000000000000000000000000000000000000000000000000000000000001",
+ identifier: "podcast-episode-42",
+ });
+ expect(result.identifier.relays).toEqual([
+ "wss://relay1.example.com",
+ "wss://relay2.example.com",
+ ]);
+ }
});
it("should not parse NIP-29 group naddr as NIP-53", () => {
diff --git a/src/lib/chat-parser.ts b/src/lib/chat-parser.ts
index dca046d..f3a9428 100644
--- a/src/lib/chat-parser.ts
+++ b/src/lib/chat-parser.ts
@@ -1,4 +1,4 @@
-import type { ChatCommandResult } from "@/types/chat";
+import type { ChatCommandResult, ChatProtocol } from "@/types/chat";
// import { NipC7Adapter } from "./chat/adapters/nip-c7-adapter";
import { Nip17Adapter } from "./chat/adapters/nip-17-adapter";
import { Nip29Adapter } from "./chat/adapters/nip-29-adapter";
@@ -6,6 +6,11 @@ import { Nip53Adapter } from "./chat/adapters/nip-53-adapter";
// Import other adapters as they're implemented
// import { Nip28Adapter } from "./chat/adapters/nip-28-adapter";
+/**
+ * Protocols that support conversation list view
+ */
+const LISTABLE_PROTOCOLS: ChatProtocol[] = ["nip-17"];
+
/**
* Parse a chat command identifier and auto-detect the protocol
*
@@ -16,6 +21,8 @@ import { Nip53Adapter } from "./chat/adapters/nip-53-adapter";
* 4. NIP-53 (live chat) - specific addressable format (kind 30311)
* 5. NIP-C7 (simple chat) - fallback for generic pubkeys
*
+ * Special case: `chat nip-17` shows conversation list
+ *
* @param args - Command arguments (first arg is the identifier)
* @returns Parsed result with protocol and identifier
* @throws Error if no adapter can parse the identifier
@@ -25,6 +32,19 @@ export function parseChatCommand(args: string[]): ChatCommandResult {
throw new Error("Chat identifier required. Usage: chat