diff --git a/packages/app/src/commands.ts b/packages/app/src/commands.ts index 08b1c6a..aa4eb5a 100644 --- a/packages/app/src/commands.ts +++ b/packages/app/src/commands.ts @@ -3,46 +3,52 @@ import {addToListPublicly, removeFromList, makeList, FOLLOWS, MUTES, PINS} from import {userFollows, userMutes, userPins} from "./user.js" import {nip44EncryptToSelf} from "./session.js" import {publishThunk} from "./thunk.js" -import {Router} from "./router.js" +import {Router, addMaximalFallbacks} from "./router.js" export const unfollow = async (value: string) => { const list = get(userFollows) || makeList({kind: FOLLOWS}) const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf) + const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls() - return publishThunk({event, relays: Router.get().FromUser().getUrls()}) + return publishThunk({event, relays}) } export const follow = async (tag: string[]) => { const list = get(userFollows) || makeList({kind: FOLLOWS}) const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf) + const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls() - return publishThunk({event, relays: Router.get().FromUser().getUrls()}) + return publishThunk({event, relays}) } export const unmute = async (value: string) => { const list = get(userMutes) || makeList({kind: MUTES}) const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf) + const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls() - return publishThunk({event, relays: Router.get().FromUser().getUrls()}) + return publishThunk({event, relays}) } export const mute = async (tag: string[]) => { const list = get(userMutes) || makeList({kind: MUTES}) const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf) + const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls() - return publishThunk({event, relays: Router.get().FromUser().getUrls()}) + return publishThunk({event, relays}) } export const unpin = async (value: string) => { const list = get(userPins) || makeList({kind: PINS}) const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf) + const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls() - return publishThunk({event, relays: Router.get().FromUser().getUrls()}) + return publishThunk({event, relays}) } export const pin = async (tag: string[]) => { const list = get(userPins) || makeList({kind: PINS}) const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf) + const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls() - return publishThunk({event, relays: Router.get().FromUser().getUrls()}) + return publishThunk({event, relays}) } diff --git a/packages/app/src/feeds.ts b/packages/app/src/feeds.ts index 703c2fc..a0433ba 100644 --- a/packages/app/src/feeds.ts +++ b/packages/app/src/feeds.ts @@ -5,7 +5,7 @@ import {Scope, FeedController, RequestOpts, FeedOptions, DVMOpts, Feed} from "@w import {makeDvmRequest, DVMEvent} from "@welshman/dvm" import {makeSecret, Nip01Signer} from "@welshman/signer" import {pubkey, signer} from "./session.js" -import {Router, getFilterSelections} from "./router.js" +import {Router, addMinimalFallbacks, getFilterSelections} from "./router.js" import {loadRelaySelections} from "./relaySelections.js" import {wotGraph, maxWot, getFollows, getNetwork, getFollowers} from "./wot.js" @@ -33,9 +33,7 @@ export const requestDVM = async ({kind, onEvent, ...request}: DVMOpts) => { const tags = request.tags || [] const $signer = signer.get() || new Nip01Signer(makeSecret()) const pubkey = await $signer.getPubkey() - const relays = request.relays - ? Router.get().FromRelays(request.relays).getUrls() - : Router.get().FromPubkeys(getPubkeyTagValues(tags)).getUrls() + const relays = request.relays || Router.get().FromPubkeys(getPubkeyTagValues(tags)).policy(addMinimalFallbacks).getUrls() if (!tags.some(nthEq(0, "expiration"))) { tags.push(["expiration", String(now() + 60)]) diff --git a/packages/app/src/relaySelections.ts b/packages/app/src/relaySelections.ts index 2820777..e72c135 100644 --- a/packages/app/src/relaySelections.ts +++ b/packages/app/src/relaySelections.ts @@ -60,10 +60,22 @@ export const { export const loadWithAsapMetaRelayUrls = (pubkey: string, relays: string[], filters: Filter[]) => { const router = Router.get() - return Promise.race([ - load({filters, relays: router.merge([router.FromRelays(relays), router.Index()]).getUrls()}), - loadRelaySelections(pubkey, relays).then(() => load({filters, relays: router.FromPubkey(pubkey).getUrls()})), - ]) + return new Promise(resolve => { + let resolved = 0 + + const onLoad = (events: TrustedEvent[]) => { + if (++resolved === 2 || events.length > 0) { + resolve(events) + } + } + + load({filters, relays: router.merge([router.Index(), router.FromRelays(relays)]).getUrls()}) + .then(onLoad) + + loadRelaySelections(pubkey, relays) + .then(() => load({filters, relays: router.FromPubkey(pubkey).getUrls()})) + .then(onLoad) + }) } export const inboxRelaySelections = deriveEventsMapped(repository, { diff --git a/packages/app/src/router.ts b/packages/app/src/router.ts index 3378e08..6005887 100644 --- a/packages/app/src/router.ts +++ b/packages/app/src/router.ts @@ -76,10 +76,10 @@ export type RouterOptions = { getPubkeyRelays?: (pubkey: string, mode?: RelayMode) => string[] /** - * Retrieves fallback relays, for use when no other relays can be selected. + * Retrieves default relays, for use as fallbacks when no other relays can be selected. * @returns An array of relay URLs as strings. */ - getFallbackRelays?: () => string[] + getDefaultRelays?: () => string[] /** * Retrieves relays that index profiles and relay selections. @@ -177,7 +177,7 @@ export const getPubkeyRelays = (pubkey: string, mode?: string) => { export const routerContext: RouterOptions = { getRelayQuality, getPubkeyRelays, - getFallbackRelays: () => ["wss://relay.damus.io/", "wss://nos.lol/"], + getDefaultRelays: () => ["wss://relay.damus.io/", "wss://nos.lol/"], getIndexerRelays: () => ["wss://purplepag.es/", "wss://relay.nostr.band/"], getSearchRelays: () => ["wss://relay.nostr.band/", "wss://nostr.wine/"], getUserPubkey: () => pubkey.get(), @@ -230,6 +230,8 @@ export class Router { Index = () => this.FromRelays(this.options.getIndexerRelays?.() || []) + Default = () => this.FromRelays(this.options.getDefaultRelays?.() || []) + ForUser = () => this.FromRelays(this.getRelaysForUser(RelayMode.Read)) FromUser = () => this.FromRelays(this.getRelaysForUser(RelayMode.Write)) @@ -396,7 +398,7 @@ export class RouterScenario { ) const fallbacksNeeded = fallbackPolicy(relays.length, limit) - const allFallbackRelays: string[] = this.router.options.getFallbackRelays?.() || [] + const allFallbackRelays: string[] = this.router.options.getDefaultRelays?.() || [] const fallbackRelays = shuffle(allFallbackRelays).slice(0, fallbacksNeeded) for (const fallbackRelay of fallbackRelays) { diff --git a/packages/app/src/tags.ts b/packages/app/src/tags.ts index 3b28791..af89ed3 100644 --- a/packages/app/src/tags.ts +++ b/packages/app/src/tags.ts @@ -14,19 +14,19 @@ import {Router} from "./router.js" export const tagZapSplit = (pubkey: string, split = 1) => [ "zap", pubkey, - Router.get().FromPubkey(pubkey).getUrl(), + Router.get().FromPubkey(pubkey).getUrl() || "", String(split), ] export const tagPubkey = (pubkey: string, ...args: unknown[]) => [ "p", pubkey, - Router.get().FromPubkey(pubkey).getUrl(), + Router.get().FromPubkey(pubkey).getUrl() || "", displayProfileByPubkey(pubkey), ] export const tagEvent = (event: TrustedEvent, mark = "") => { - const url = Router.get().Event(event).getUrl() + const url = Router.get().Event(event).getUrl() || "" const tags = [["e", event.id, url, mark, event.pubkey]] if (isReplaceable(event)) { @@ -42,7 +42,7 @@ export const tagEventPubkeys = (event: TrustedEvent) => export const tagEventForQuote = (event: TrustedEvent) => [ "q", event.id, - Router.get().Event(event).getUrl(), + Router.get().Event(event).getUrl() || "", event.pubkey, ] @@ -55,11 +55,11 @@ export const tagEventForReply = (event: TrustedEvent) => { // Root comes first if (roots.length > 0) { for (const t of roots) { - tags.push([...t.slice(0, 2), Router.get().EventRoots(event).getUrl(), "root"]) + tags.push([...t.slice(0, 2), Router.get().EventRoots(event).getUrl() || "", "root"]) } } else { for (const t of replies) { - tags.push([...t.slice(0, 2), Router.get().EventParents(event).getUrl(), "root"]) + tags.push([...t.slice(0, 2), Router.get().EventParents(event).getUrl() || "", "root"]) } } @@ -81,7 +81,7 @@ export const tagEventForReply = (event: TrustedEvent) => { // Finally, tag the event itself const mark = replies.length > 0 ? "reply" : "root" - const hint = Router.get().Event(event).getUrl() + const hint = Router.get().Event(event).getUrl() || "" // e-tag the event tags.push(["e", event.id, hint, mark, event.pubkey]) @@ -95,8 +95,8 @@ export const tagEventForReply = (event: TrustedEvent) => { } export const tagEventForComment = (event: TrustedEvent) => { - const pubkeyHint = Router.get().FromPubkey(event.pubkey).getUrl() - const eventHint = Router.get().Event(event).getUrl() + const pubkeyHint = Router.get().FromPubkey(event.pubkey).getUrl() || "" + const eventHint = Router.get().Event(event).getUrl() || "" const address = getAddress(event) const seenRoots = new Set() const tags: string[][] = [] @@ -130,7 +130,7 @@ export const tagEventForComment = (event: TrustedEvent) => { } export const tagEventForReaction = (event: TrustedEvent) => { - const hint = Router.get().Event(event).getUrl() + const hint = Router.get().Event(event).getUrl() || "" const tags: string[][] = [] // Mention the event's author