- RelayMetadata.dmRelays; Relays UI to add/remove, publishing kind 10050 - NostrSync fetches the user's 10050 on login (alongside 10002) - DMs deliver gift wraps to recipients' + own DM relays (fallback: write relays) and read from own DM relays + NIP-65 read relays - useDmRelays() to look up others' DM relay lists Also fixes a latent data-loss bug: NostrSync's 10002 fetch replaced relayMetadata wholesale, which would wipe the new local/blocked/dm lists on login — it now merges. 'Trusted' category intentionally skipped (no distinct behaviour -> no empty UI); documented in AGENTS.md.
46 lines
1.6 KiB
TypeScript
46 lines
1.6 KiB
TypeScript
import { useQuery } from '@tanstack/react-query';
|
|
import { useNostr } from '@nostrify/react';
|
|
import { useAppContext } from '@/hooks/useAppContext';
|
|
|
|
/** Parse the `relay` tags of a kind-10050 event (NIP-17) into URLs. */
|
|
export function dmRelaysFromEvent(tags: string[][]): string[] {
|
|
return tags
|
|
.filter(([name]) => name === 'relay')
|
|
.map(([, url]) => url)
|
|
.filter((url): url is string => Boolean(url));
|
|
}
|
|
|
|
/** The current user's own published DM relays (kind 10050), from config. */
|
|
export function useMyDmRelays(): string[] {
|
|
const { config } = useAppContext();
|
|
return config.relayMetadata.dmRelays ?? [];
|
|
}
|
|
|
|
/**
|
|
* Fetch the NIP-17 DM relay lists (kind 10050) for the given pubkeys.
|
|
* Returns a `pubkey → relayUrls` map (newest event per author wins).
|
|
*/
|
|
export function useDmRelays(pubkeys: string[]) {
|
|
const { nostr } = useNostr();
|
|
const unique = [...new Set(pubkeys.filter(Boolean))].sort();
|
|
|
|
return useQuery<Record<string, string[]>>({
|
|
queryKey: ['dm-relays', unique.join(',')],
|
|
enabled: unique.length > 0,
|
|
staleTime: 10 * 60 * 1000,
|
|
queryFn: async (c) => {
|
|
const signal = AbortSignal.any([c.signal, AbortSignal.timeout(8000)]);
|
|
const events = await nostr.query(
|
|
[{ kinds: [10050], authors: unique, limit: unique.length * 2 }],
|
|
{ signal },
|
|
);
|
|
const map: Record<string, string[]> = {};
|
|
for (const ev of [...events].sort((a, b) => b.created_at - a.created_at)) {
|
|
if (map[ev.pubkey]) continue;
|
|
const urls = dmRelaysFromEvent(ev.tags);
|
|
if (urls.length > 0) map[ev.pubkey] = urls;
|
|
}
|
|
return map;
|
|
},
|
|
});
|
|
}
|