From 5dfcabce6789496796c20abbcbe23ce7aa3e335d Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Wed, 11 Mar 2026 11:36:05 -0700 Subject: [PATCH] Add search relays --- packages/app/src/commands.ts | 30 +++++++++++++++++++++++ packages/app/src/index.ts | 4 ++++ packages/app/src/searchRelayLists.ts | 36 ++++++++++++++++++++++++++++ packages/app/src/user.ts | 9 +++++++ packages/net/src/repository.ts | 3 ++- 5 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 packages/app/src/searchRelayLists.ts diff --git a/packages/app/src/commands.ts b/packages/app/src/commands.ts index 6a1cf00..41c490e 100644 --- a/packages/app/src/commands.ts +++ b/packages/app/src/commands.ts @@ -28,6 +28,7 @@ import { makeEvent, MESSAGING_RELAYS, BLOCKED_RELAYS, + SEARCH_RELAYS, FOLLOWS, RELAYS, MUTES, @@ -43,6 +44,8 @@ import { forceLoadUserMessagingRelayList, userBlockedRelayList, forceLoadUserBlockedRelayList, + userSearchRelayList, + forceLoadUserSearchRelayList, userFollowList, forceLoadUserFollowList, userMuteList, @@ -159,6 +162,33 @@ export const setBlockedRelays = async (urls: string[]) => { return publishThunk({event, relays}) } +export const removeSearchRelay = async (url: string) => { + await forceLoadUserSearchRelayList([]) + + const list = get(userSearchRelayList) || makeList({kind: SEARCH_RELAYS}) + const event = await removeFromList(list, url).reconcile(nip44EncryptToSelf) + const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls() + + return publishThunk({event, relays}) +} + +export const addSearchRelay = async (url: string) => { + await forceLoadUserSearchRelayList([]) + + const list = get(userSearchRelayList) || makeList({kind: SEARCH_RELAYS}) + const event = await addToListPublicly(list, ["relay", url]).reconcile(nip44EncryptToSelf) + const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls() + + return publishThunk({event, relays}) +} + +export const setSearchRelays = async (urls: string[]) => { + const event = makeEvent(SEARCH_RELAYS, {tags: urls.map(url => ["relay", url])}) + const relays = Router.get().FromUser().getUrls() + + return publishThunk({event, relays}) +} + // NIP 01 export const setProfile = (profile: Profile) => { diff --git a/packages/app/src/index.ts b/packages/app/src/index.ts index a212689..fa70719 100644 --- a/packages/app/src/index.ts +++ b/packages/app/src/index.ts @@ -41,6 +41,7 @@ import {repository, tracker} from "./core.js" import {getRelays, loadRelay} from "./relays.js" import {trackRelayStats, getRelayQuality} from "./relayStats.js" import {deriveRelayList, getRelayList} from "./relayLists.js" +import {deriveSearchRelayList, getSearchRelayList} from "./searchRelayLists.js" import {deriveBlockedRelayList, getBlockedRelayList} from "./blockedRelayLists.js" import {deriveMessagingRelayList, getMessagingRelayList} from "./messagingRelayLists.js" @@ -87,12 +88,15 @@ const _relayGetter = (fn?: (relay: RelayProfile) => any) => }) export const getPubkeyRelays = (pubkey: string, mode?: RelayMode) => { + if (mode === RelayMode.Search) return getRelaysFromList(getSearchRelayList(pubkey)) if (mode === RelayMode.Blocked) return getRelaysFromList(getBlockedRelayList(pubkey)) if (mode === RelayMode.Messaging) return getRelaysFromList(getMessagingRelayList(pubkey)) return getRelaysFromList(getRelayList(pubkey), mode) } export const derivePubkeyRelays = (pubkey: string, mode?: RelayMode) => { + if (mode === RelayMode.Search) + return derived(deriveSearchRelayList(pubkey), list => getRelaysFromList(list)) if (mode === RelayMode.Blocked) return derived(deriveBlockedRelayList(pubkey), list => getRelaysFromList(list)) if (mode === RelayMode.Messaging) diff --git a/packages/app/src/searchRelayLists.ts b/packages/app/src/searchRelayLists.ts new file mode 100644 index 0000000..72a61ab --- /dev/null +++ b/packages/app/src/searchRelayLists.ts @@ -0,0 +1,36 @@ +import {SEARCH_RELAYS, asDecryptedEvent, readList} from "@welshman/util" +import {TrustedEvent} from "@welshman/util" +import { + deriveItemsByKey, + deriveItems, + makeForceLoadItem, + makeLoadItem, + makeDeriveItem, + getter, +} from "@welshman/store" +import {repository} from "./core.js" +import {makeOutboxLoader} from "./relayLists.js" + +export const searchRelayListsByPubkey = deriveItemsByKey({ + repository, + eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)), + filters: [{kinds: [SEARCH_RELAYS]}], + getKey: searchRelayLists => searchRelayLists.event.pubkey, +}) + +export const searchRelayLists = deriveItems(searchRelayListsByPubkey) + +export const getSearchRelayListsByPubkey = getter(searchRelayListsByPubkey) + +export const getSearchRelayLists = getter(searchRelayLists) + +export const getSearchRelayList = (pubkey: string) => getSearchRelayListsByPubkey().get(pubkey) + +export const forceLoadSearchRelayList = makeForceLoadItem( + makeOutboxLoader(SEARCH_RELAYS), + getSearchRelayList, +) + +export const loadSearchRelayList = makeLoadItem(makeOutboxLoader(SEARCH_RELAYS), getSearchRelayList) + +export const deriveSearchRelayList = makeDeriveItem(searchRelayListsByPubkey, loadSearchRelayList) diff --git a/packages/app/src/user.ts b/packages/app/src/user.ts index d7e58cf..678f5ef 100644 --- a/packages/app/src/user.ts +++ b/packages/app/src/user.ts @@ -21,6 +21,11 @@ import { forceLoadBlockedRelayList, loadBlockedRelayList, } from "./blockedRelayLists.js" +import { + searchRelayListsByPubkey, + forceLoadSearchRelayList, + loadSearchRelayList, +} from "./searchRelayLists.js" import {wotGraph, getWotGraph} from "./wot.js" export const makeUserData = ( @@ -72,6 +77,10 @@ export const userMessagingRelayList = makeUserData( export const forceLoadUserMessagingRelayList = makeUserLoader(forceLoadMessagingRelayList) export const loadUserMessagingRelayList = makeUserLoader(loadMessagingRelayList) +export const userSearchRelayList = makeUserData(searchRelayListsByPubkey, loadSearchRelayList) +export const forceLoadUserSearchRelayList = makeUserLoader(forceLoadSearchRelayList) +export const loadUserSearchRelayList = makeUserLoader(loadSearchRelayList) + export const userBlockedRelayList = makeUserData(blockedRelayListsByPubkey, loadBlockedRelayList) export const forceLoadUserBlockedRelayList = makeUserLoader(forceLoadBlockedRelayList) export const loadUserBlockedRelayList = makeUserLoader(loadBlockedRelayList) diff --git a/packages/net/src/repository.ts b/packages/net/src/repository.ts index 86fb0e1..8888b48 100644 --- a/packages/net/src/repository.ts +++ b/packages/net/src/repository.ts @@ -258,7 +258,8 @@ export class Repository extends Emitter { } } - if (shouldNotify) { + // Notify, but only if the event hasn't been deleted + if (shouldNotify && !this.isDeleted(event)) { this.emit("update", {added: [event], removed}) }