refactor: move NIP-29 member list fallback to adapter level

Move the participant fallback logic from ChatViewer to the NIP-29 adapter's
resolveConversation() method. This keeps protocol-specific behavior in the
adapter rather than leaking it into the view.

When no member list events (kind 39001/39002) are found, the adapter now
fetches recent messages (kind 9) and derives participants from message
authors.

https://claude.ai/code/session_01Qikw63P4kxwR6ceCQuFMAM
This commit is contained in:
Claude
2026-01-27 10:23:10 +00:00
parent 7378b0f50a
commit 88589915ed
2 changed files with 37 additions and 25 deletions

View File

@@ -31,7 +31,7 @@ import { Nip10Adapter } from "@/lib/chat/adapters/nip-10-adapter";
import { Nip29Adapter } from "@/lib/chat/adapters/nip-29-adapter";
import { Nip53Adapter } from "@/lib/chat/adapters/nip-53-adapter";
import type { ChatProtocolAdapter } from "@/lib/chat/adapters/base-adapter";
import type { Message, ParticipantRole } from "@/types/chat";
import type { Message } from "@/types/chat";
import type { ChatAction } from "@/types/chat-actions";
import { parseSlashCommand } from "@/lib/chat/slash-command-parser";
import {
@@ -895,29 +895,6 @@ export function ChatViewer({
return participants;
}
// NIP-29 groups: use static participants but fallback to message authors if empty
// Some relays may not provide kind 39001/39002 member list events
if (protocol === "nip-29" && messages) {
const staticParticipants = conversation?.participants || [];
// If we have static participants from member list events, use them as base
// and augment with message authors
const participants: { pubkey: string; role?: ParticipantRole }[] = [
...staticParticipants,
];
const seen = new Set(staticParticipants.map((p) => p.pubkey));
// Add message authors who aren't already in the list as members
for (const msg of messages) {
if (msg.type !== "system" && !seen.has(msg.author)) {
seen.add(msg.author);
participants.push({ pubkey: msg.author, role: "member" });
}
}
return participants;
}
// Other protocols: use static participants from conversation
return conversation?.participants || [];
}, [

View File

@@ -269,11 +269,46 @@ export class Nip29Adapter extends ChatProtocolAdapter {
}
}
const participants = Array.from(participantsMap.values());
let participants = Array.from(participantsMap.values());
console.log(
`[NIP-29] Found ${participants.length} participants (${adminEvents.length} admin events, ${memberEvents.length} member events)`,
);
// Fallback: if no member list found, derive participants from recent messages
if (participants.length === 0) {
console.log(
`[NIP-29] No member list found, fetching recent messages for participant fallback`,
);
const messagesFilter: Filter = {
kinds: [9],
"#h": [groupId],
limit: 50,
};
const recentMessages = await firstValueFrom(
pool
.request([relayUrl], [messagesFilter], { eventStore })
.pipe(toArray()),
);
// Extract unique authors from messages
const messageAuthors = new Set<string>();
for (const event of recentMessages) {
messageAuthors.add(event.pubkey);
}
participants = Array.from(messageAuthors).map((pubkey) => ({
pubkey,
role: "member" as const,
}));
console.log(
`[NIP-29] Derived ${participants.length} participants from ${recentMessages.length} messages`,
);
}
console.log(
`[NIP-29] Metadata - title: ${title}, icon: ${icon}, description: ${description}`,
);