mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-10 07:27:23 +02:00
81 lines
2.5 KiB
TypeScript
81 lines
2.5 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import { use$ } from "applesauce-react/hooks";
|
|
import { map } from "rxjs/operators";
|
|
import { getSeenRelays } from "applesauce-core/helpers/relays";
|
|
import eventStore from "@/services/event-store";
|
|
import pool from "@/services/relay-pool";
|
|
import {
|
|
resolveGroupMetadata,
|
|
type ResolvedGroupMetadata,
|
|
} from "@/lib/chat/group-metadata-helpers";
|
|
|
|
/**
|
|
* Hook that fetches and resolves NIP-29 group metadata for a single group.
|
|
*
|
|
* Subscribes to kind 39000 on the group's relay, then resolves metadata
|
|
* with profile fallback. Filters by seenRelays to avoid cross-relay contamination
|
|
* when multiple relays host groups with the same ID.
|
|
*/
|
|
export function useGroupMetadata(
|
|
groupId: string,
|
|
relayUrl: string,
|
|
): ResolvedGroupMetadata | undefined {
|
|
const isUnmanaged = groupId === "_";
|
|
|
|
// Subscribe to kind 39000 metadata on the group's relay
|
|
useEffect(() => {
|
|
if (isUnmanaged) return;
|
|
|
|
const sub = pool
|
|
.subscription([relayUrl], [{ kinds: [39000], "#d": [groupId] }], {
|
|
eventStore,
|
|
})
|
|
.subscribe();
|
|
|
|
return () => sub.unsubscribe();
|
|
}, [groupId, relayUrl, isUnmanaged]);
|
|
|
|
// Observe the metadata event from the store via timeline query.
|
|
// kind 39000 author is the relay's pubkey (unknown in advance), so we
|
|
// query by d-tag and filter by seenRelays to get the correct relay's metadata.
|
|
const normalizedRelay = relayUrl.replace(/\/$/, "");
|
|
const metadataEvent = use$(
|
|
() =>
|
|
!isUnmanaged
|
|
? eventStore.timeline([{ kinds: [39000], "#d": [groupId] }]).pipe(
|
|
map((events) => {
|
|
// Prefer the event actually seen on this relay
|
|
const fromRelay = events.find((evt) => {
|
|
const seen = getSeenRelays(evt);
|
|
if (!seen || seen.size === 0) return false;
|
|
return Array.from(seen).some(
|
|
(r) => r.replace(/\/$/, "") === normalizedRelay,
|
|
);
|
|
});
|
|
return fromRelay ?? events[0];
|
|
}),
|
|
)
|
|
: undefined,
|
|
[groupId, isUnmanaged, normalizedRelay],
|
|
);
|
|
|
|
// Resolve metadata with profile fallback
|
|
const [resolved, setResolved] = useState<ResolvedGroupMetadata | undefined>();
|
|
|
|
useEffect(() => {
|
|
if (isUnmanaged) return;
|
|
|
|
let cancelled = false;
|
|
|
|
resolveGroupMetadata(groupId, relayUrl, metadataEvent).then((result) => {
|
|
if (!cancelled) setResolved(result);
|
|
});
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [groupId, relayUrl, metadataEvent, isUnmanaged]);
|
|
|
|
return resolved;
|
|
}
|