mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-12 00:17:02 +02:00
refactor: improve NIP-29 adapter metadata fetching
- Check eventStore first before making network request for kind 39000 - Use extractMetadataFromEvent for sync extraction when event is cached - Only fetch from relay if not already in eventStore - Fall back to async resolveGroupMetadata for profile-based groups This improves performance when opening a chat that was previously loaded, as the metadata is already in the eventStore cache. https://claude.ai/code/session_01CCxAcUsRBkWSL6as1wtFoA
This commit is contained in:
@@ -28,7 +28,10 @@ import {
|
||||
GroupMessageBlueprint,
|
||||
ReactionBlueprint,
|
||||
} from "applesauce-common/blueprints";
|
||||
import { resolveGroupMetadata } from "@/lib/chat/group-metadata-helpers";
|
||||
import {
|
||||
resolveGroupMetadata,
|
||||
extractMetadataFromEvent,
|
||||
} from "@/lib/chat/group-metadata-helpers";
|
||||
|
||||
/**
|
||||
* NIP-29 Adapter - Relay-Based Groups
|
||||
@@ -130,72 +133,90 @@ export class Nip29Adapter extends ChatProtocolAdapter {
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[NIP-29] Fetching group metadata for ${groupId} from ${relayUrl}`,
|
||||
`[NIP-29] Resolving group metadata for ${groupId} from ${relayUrl}`,
|
||||
);
|
||||
|
||||
// Fetch group metadata from the specific relay (kind 39000)
|
||||
const metadataFilter: Filter = {
|
||||
kinds: [39000],
|
||||
"#d": [groupId],
|
||||
limit: 1,
|
||||
};
|
||||
// Check eventStore first for cached kind 39000 event
|
||||
// Use timeline query since we don't know the relay's pubkey
|
||||
const cachedMetadata = await firstValueFrom(
|
||||
eventStore
|
||||
.timeline([{ kinds: [39000], "#d": [groupId], limit: 1 }])
|
||||
.pipe(first()),
|
||||
{ defaultValue: [] },
|
||||
);
|
||||
let metadataEvent: NostrEvent | undefined = cachedMetadata[0];
|
||||
|
||||
// Use pool.subscription to fetch from the relay
|
||||
const metadataEvents: NostrEvent[] = [];
|
||||
const metadataObs = pool.subscription([relayUrl], [metadataFilter], {
|
||||
eventStore, // Automatically add to store
|
||||
});
|
||||
// If not in store, fetch from relay
|
||||
if (!metadataEvent) {
|
||||
console.log(`[NIP-29] No cached metadata, fetching from relay`);
|
||||
|
||||
// Subscribe and wait for EOSE
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
console.log("[NIP-29] Metadata fetch timeout");
|
||||
resolve();
|
||||
}, 5000);
|
||||
const metadataFilter: Filter = {
|
||||
kinds: [39000],
|
||||
"#d": [groupId],
|
||||
limit: 1,
|
||||
};
|
||||
|
||||
const sub = metadataObs.subscribe({
|
||||
next: (response) => {
|
||||
if (typeof response === "string") {
|
||||
// EOSE received
|
||||
clearTimeout(timeout);
|
||||
console.log(
|
||||
`[NIP-29] Got ${metadataEvents.length} metadata events`,
|
||||
);
|
||||
sub.unsubscribe();
|
||||
resolve();
|
||||
} else {
|
||||
// Event received
|
||||
metadataEvents.push(response);
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
clearTimeout(timeout);
|
||||
console.error("[NIP-29] Metadata fetch error:", err);
|
||||
sub.unsubscribe();
|
||||
reject(err);
|
||||
},
|
||||
const metadataEvents: NostrEvent[] = [];
|
||||
const metadataObs = pool.subscription([relayUrl], [metadataFilter], {
|
||||
eventStore,
|
||||
});
|
||||
});
|
||||
|
||||
const metadataEvent = metadataEvents[0];
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
console.log("[NIP-29] Metadata fetch timeout");
|
||||
resolve();
|
||||
}, 5000);
|
||||
|
||||
// Debug: Log metadata event tags
|
||||
if (metadataEvent) {
|
||||
console.log(`[NIP-29] Metadata event tags:`, metadataEvent.tags);
|
||||
const sub = metadataObs.subscribe({
|
||||
next: (response) => {
|
||||
if (typeof response === "string") {
|
||||
clearTimeout(timeout);
|
||||
console.log(
|
||||
`[NIP-29] Got ${metadataEvents.length} metadata events`,
|
||||
);
|
||||
sub.unsubscribe();
|
||||
resolve();
|
||||
} else {
|
||||
metadataEvents.push(response);
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
clearTimeout(timeout);
|
||||
console.error("[NIP-29] Metadata fetch error:", err);
|
||||
sub.unsubscribe();
|
||||
reject(err);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
metadataEvent = metadataEvents[0];
|
||||
} else {
|
||||
console.log(`[NIP-29] Using cached metadata event`);
|
||||
}
|
||||
|
||||
// Resolve group metadata with profile fallback
|
||||
const resolved = await resolveGroupMetadata(
|
||||
groupId,
|
||||
relayUrl,
|
||||
metadataEvent,
|
||||
);
|
||||
// Extract metadata - use sync extraction if we have the event
|
||||
let title: string;
|
||||
let description: string | undefined;
|
||||
let icon: string | undefined;
|
||||
let source: string;
|
||||
|
||||
const title = resolved.name || groupId;
|
||||
const description = resolved.description;
|
||||
const icon = resolved.icon;
|
||||
if (metadataEvent && metadataEvent.kind === 39000) {
|
||||
// Fast path: sync extraction from kind 39000 event
|
||||
const resolved = extractMetadataFromEvent(groupId, metadataEvent);
|
||||
title = resolved.name || groupId;
|
||||
description = resolved.description;
|
||||
icon = resolved.icon;
|
||||
source = resolved.source;
|
||||
} else {
|
||||
// Slow path: async resolution with profile fallback (for pubkey-based groupIds)
|
||||
const resolved = await resolveGroupMetadata(groupId, relayUrl, undefined);
|
||||
title = resolved.name || groupId;
|
||||
description = resolved.description;
|
||||
icon = resolved.icon;
|
||||
source = resolved.source;
|
||||
}
|
||||
|
||||
console.log(`[NIP-29] Group title: ${title} (source: ${resolved.source})`);
|
||||
console.log(`[NIP-29] Group title: ${title} (source: ${source})`);
|
||||
|
||||
// Fetch admins (kind 39001) and members (kind 39002) in parallel
|
||||
// Both use d tag (addressable events signed by relay)
|
||||
|
||||
Reference in New Issue
Block a user