mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-12 16:37:06 +02:00
Fix reaction loading to use protocol-specific relay hints
Previously MessageReactions was only querying EventStore without actually fetching reactions from relays. Now it properly: - Starts a relay subscription per message to fetch kind 7 reactions - Uses protocol-specific relay hints via getConversationRelays() helper: * NIP-29 groups: Single relay from conversation.metadata.relayUrl * NIP-53 live chats: Multiple relays from conversation.metadata.liveActivity.relays - Memoizes relay array in MessageItem to prevent unnecessary re-subscriptions - Cleans up subscriptions when message unmounts or changes This ensures reactions are actually fetched and displayed correctly across different chat protocols.
This commit is contained in:
@@ -135,6 +135,24 @@ function isLiveActivityMetadata(value: unknown): value is LiveActivityMetadata {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get relay URLs for a conversation based on protocol
|
||||
* Used for fetching protocol-specific data like reactions
|
||||
*/
|
||||
function getConversationRelays(conversation: Conversation): string[] {
|
||||
// NIP-53 live chats: Use full relay list from liveActivity metadata
|
||||
if (conversation.protocol === "nip-53") {
|
||||
const liveActivity = conversation.metadata?.liveActivity;
|
||||
if (isLiveActivityMetadata(liveActivity) && liveActivity.relays) {
|
||||
return liveActivity.relays;
|
||||
}
|
||||
}
|
||||
|
||||
// NIP-29 groups and fallback: Use single relay URL
|
||||
const relayUrl = conversation.metadata?.relayUrl;
|
||||
return relayUrl ? [relayUrl] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the chat command identifier for a conversation
|
||||
* Returns a string that can be passed to the `chat` command to open this conversation
|
||||
@@ -249,6 +267,12 @@ const MessageItem = memo(function MessageItem({
|
||||
canReply: boolean;
|
||||
onScrollToMessage?: (messageId: string) => void;
|
||||
}) {
|
||||
// Get relays for this conversation (memoized to prevent unnecessary re-subscriptions)
|
||||
const relays = useMemo(
|
||||
() => getConversationRelays(conversation),
|
||||
[conversation],
|
||||
);
|
||||
|
||||
// System messages (join/leave) have special styling
|
||||
if (message.type === "system") {
|
||||
return (
|
||||
@@ -334,10 +358,7 @@ const MessageItem = memo(function MessageItem({
|
||||
</div>
|
||||
</div>
|
||||
{/* Reactions display - lazy loaded per message */}
|
||||
<MessageReactions
|
||||
messageId={message.id}
|
||||
relayUrl={conversation.metadata?.relayUrl}
|
||||
/>
|
||||
<MessageReactions messageId={message.id} relays={relays} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -380,10 +401,7 @@ const MessageItem = memo(function MessageItem({
|
||||
)}
|
||||
</div>
|
||||
{/* Reactions display - lazy loaded per message */}
|
||||
<MessageReactions
|
||||
messageId={message.id}
|
||||
relayUrl={conversation.metadata?.relayUrl}
|
||||
/>
|
||||
<MessageReactions messageId={message.id} relays={relays} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useMemo } from "react";
|
||||
import { useMemo, useEffect } from "react";
|
||||
import { use$ } from "applesauce-react/hooks";
|
||||
import eventStore from "@/services/event-store";
|
||||
import pool from "@/services/relay-pool";
|
||||
import { EMOJI_SHORTCODE_REGEX } from "@/lib/emoji-helpers";
|
||||
import type { NostrEvent } from "@/types/nostr";
|
||||
|
||||
interface MessageReactionsProps {
|
||||
messageId: string;
|
||||
/** Relay URL for fetching reactions (NIP-29 group relay) */
|
||||
relayUrl?: string;
|
||||
/** Relay URLs for fetching reactions - protocol-specific */
|
||||
relays: string[];
|
||||
}
|
||||
|
||||
interface ReactionSummary {
|
||||
@@ -26,14 +27,51 @@ interface ReactionSummary {
|
||||
* Loads kind 7 (reaction) events that reference the messageId via e-tag.
|
||||
* Aggregates by emoji and displays as tiny inline badges in bottom-right corner.
|
||||
*
|
||||
* Uses EventStore timeline for reactive updates - new reactions appear automatically.
|
||||
* Fetches reactions from protocol-specific relays and uses EventStore timeline
|
||||
* for reactive updates - new reactions appear automatically.
|
||||
*/
|
||||
export function MessageReactions({
|
||||
messageId,
|
||||
relayUrl,
|
||||
}: MessageReactionsProps) {
|
||||
export function MessageReactions({ messageId, relays }: MessageReactionsProps) {
|
||||
// Start relay subscription to fetch reactions for this message
|
||||
useEffect(() => {
|
||||
if (relays.length === 0) return;
|
||||
|
||||
const filter = {
|
||||
kinds: [7],
|
||||
"#e": [messageId],
|
||||
limit: 100, // Reasonable limit for reactions
|
||||
};
|
||||
|
||||
// Subscribe to relays to fetch reactions
|
||||
const subscription = pool
|
||||
.subscription(relays, [filter], {
|
||||
eventStore, // Automatically add reactions to EventStore
|
||||
})
|
||||
.subscribe({
|
||||
next: (response) => {
|
||||
if (typeof response !== "string") {
|
||||
// Event received - it's automatically added to EventStore
|
||||
console.log(
|
||||
`[MessageReactions] Reaction received for ${messageId.slice(0, 8)}...`,
|
||||
);
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error(
|
||||
`[MessageReactions] Subscription error for ${messageId.slice(0, 8)}...`,
|
||||
err,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// Cleanup subscription when component unmounts or messageId changes
|
||||
return () => {
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, [messageId, relays]);
|
||||
|
||||
// Load reactions for this message from EventStore
|
||||
// Filter: kind 7, e-tag pointing to messageId
|
||||
// This observable will update automatically as reactions arrive from the subscription above
|
||||
const reactions = use$(
|
||||
() =>
|
||||
eventStore.timeline({
|
||||
|
||||
Reference in New Issue
Block a user