diff --git a/.changeset/pretty-buses-remember.md b/.changeset/pretty-buses-remember.md new file mode 100644 index 000000000..de5b689b2 --- /dev/null +++ b/.changeset/pretty-buses-remember.md @@ -0,0 +1,5 @@ +--- +"nostrudel": patch +--- + +Reduce churn when loading relays on app load diff --git a/src/providers/post-modal-provider.tsx b/src/providers/post-modal-provider.tsx index a62ce36d0..0d30ba339 100644 --- a/src/providers/post-modal-provider.tsx +++ b/src/providers/post-modal-provider.tsx @@ -26,7 +26,7 @@ export default function PostModalProvider({ children }: PropsWithChildren) { return ( - {isOpen && } + {children} diff --git a/src/services/client-relays.ts b/src/services/client-relays.ts index 4922b2ca3..a342d4a1c 100644 --- a/src/services/client-relays.ts +++ b/src/services/client-relays.ts @@ -18,6 +18,7 @@ const DEFAULT_RELAYS = [ { url: "wss://relay.snort.social", mode: RelayMode.READ }, { url: "wss://eden.nostr.land", mode: RelayMode.READ }, { url: "wss://nos.lol", mode: RelayMode.READ }, + { url: "wss://purplerelay.com", mode: RelayMode.READ }, ]; const userRelaysToRelayConfig: Connection = ( @@ -26,7 +27,7 @@ const userRelaysToRelayConfig: Connection next(userRelays.relays); class ClientRelayService { - bootstrapRelays = new Set(); + // bootstrapRelays = new Set(); relays = new PersistentSubject([]); writeRelays = new PersistentSubject([]); readRelays = new PersistentSubject([]); @@ -34,50 +35,63 @@ class ClientRelayService { log = logger.extend("ClientRelays"); constructor() { - let lastSubject: Subject | undefined; - accountService.current.subscribe((account) => { - if (!account) { - this.log("No account, using default relays"); - this.relays.next(DEFAULT_RELAYS); - return; - } else this.relays.next([]); - - if (account.relays) { - this.log("Found bootstrap relays"); - this.bootstrapRelays.clear(); - for (const relay of account.relays) { - this.bootstrapRelays.add(relay); - } - } - - if (lastSubject) { - this.log("Disconnecting from previous user relays"); - this.relays.disconnect(lastSubject); - lastSubject = undefined; - } - - // load the relays from cache or bootstrap relays - this.log("Load users relays from cache or bootstrap relays"); - lastSubject = userRelaysService.requestRelays(account.pubkey, Array.from(this.bootstrapRelays), { - alwaysRequest: true, - }); - setTimeout(() => { - // double check for new relay notes - this.log("Requesting latest relays from the write relays"); - userRelaysService.requestRelays(account.pubkey, this.getWriteUrls(), { alwaysRequest: true }); - }, 1000); - - this.relays.connectWithHandler(lastSubject, userRelaysToRelayConfig); - }); + accountService.loading.subscribe(this.handleAccountChange, this); + accountService.current.subscribe(this.handleAccountChange, this); // set the read and write relays this.relays.subscribe((relays) => { - this.log("Got new relay list"); + this.log("Got new relay list", relays); this.writeRelays.next(relays.filter((r) => r.mode & RelayMode.WRITE)); this.readRelays.next(relays.filter((r) => r.mode & RelayMode.READ)); }); } + private userRequestRelaySubject: Subject | undefined; + private handleAccountChange() { + if (accountService.loading.value) return; + + // disconnect the relay list subject + if (this.userRequestRelaySubject) { + this.relays.disconnect(this.userRequestRelaySubject); + this.userRequestRelaySubject = undefined; + } + + const account = accountService.current.value; + if (!account) { + this.log("No account, using default relays"); + this.relays.next(DEFAULT_RELAYS); + return; + } + + // clear relays + this.relays.next([]); + + // connect the relay subject with the account relay subject + this.userRequestRelaySubject = userRelaysService.getRelays(account.pubkey); + this.relays.connectWithHandler(this.userRequestRelaySubject, userRelaysToRelayConfig); + + // load the relays from cache + if (!userRelaysService.getRelays(account.pubkey).value) { + this.log("Load users relay list from cache"); + userRelaysService.loadFromCache(account.pubkey).then(() => { + if (this.relays.value.length === 0) { + const bootstrapRelays = account.relays ?? ["wss://purplepag.es"]; + + this.log("Loading relay list from bootstrap relays", bootstrapRelays); + userRelaysService.requestRelays(account.pubkey, bootstrapRelays, { alwaysRequest: true }); + } + }); + } + + // double check for new relay notes + setTimeout(() => { + if (this.relays.value.length === 0) return; + + this.log("Requesting latest relay list from relays"); + userRelaysService.requestRelays(account.pubkey, this.getWriteUrls(), { alwaysRequest: true }); + }, 5000); + } + async addRelay(url: string, mode: RelayMode) { this.log(`Adding ${url} relay`); if (!this.relays.value.some((r) => r.url === url)) { diff --git a/src/services/replaceable-event-requester.ts b/src/services/replaceable-event-requester.ts index d46db0e2e..a1083fc23 100644 --- a/src/services/replaceable-event-requester.ts +++ b/src/services/replaceable-event-requester.ts @@ -192,7 +192,7 @@ class ReplaceableEventLoaderService { await transaction.done; } private loadCacheDedupe = new Map>(); - private loadFromCache(cord: string) { + loadFromCache(cord: string) { const dedupe = this.loadCacheDedupe.get(cord); if (dedupe) return dedupe; diff --git a/src/services/user-relays.ts b/src/services/user-relays.ts index 80aaaa5df..0beefed6e 100644 --- a/src/services/user-relays.ts +++ b/src/services/user-relays.ts @@ -7,7 +7,7 @@ import SuperMap from "../classes/super-map"; import Subject from "../classes/subject"; import { normalizeRelayConfigs } from "../helpers/relay"; import userContactsService from "./user-contacts"; -import replaceableEventLoaderService, { RequestOptions } from "./replaceable-event-requester"; +import replaceableEventLoaderService, { createCoordinate, RequestOptions } from "./replaceable-event-requester"; export type ParsedUserRelays = { pubkey: string; @@ -44,6 +44,16 @@ class UserRelaysService { return sub; } + async loadFromCache(pubkey: string) { + const sub = this.subjects.get(pubkey); + + // load from cache + await replaceableEventLoaderService.loadFromCache(createCoordinate(Kind.RelayList, pubkey)); + + const requestSub = replaceableEventLoaderService.getEvent(Kind.RelayList, pubkey); + sub.connectWithHandler(requestSub, (event, next) => next(parseRelaysEvent(event))); + } + receiveEvent(event: NostrEvent) { replaceableEventLoaderService.handleEvent(event); } diff --git a/src/views/login/npub.tsx b/src/views/login/npub.tsx index 4b5f60216..d1a3c8a09 100644 --- a/src/views/login/npub.tsx +++ b/src/views/login/npub.tsx @@ -24,8 +24,6 @@ export default function LoginNpubView() { accountService.addAccount({ pubkey, relays: [relayUrl], readonly: true }); } accountService.switchAccount(pubkey); - - clientRelaysService.bootstrapRelays.add(relayUrl); }; return ( diff --git a/src/views/login/nsec.tsx b/src/views/login/nsec.tsx index 88c34f7d8..84d858c43 100644 --- a/src/views/login/nsec.tsx +++ b/src/views/login/nsec.tsx @@ -73,7 +73,6 @@ export default function LoginNsecView() { const encrypted = await signingService.encryptSecKey(hexKey); accountService.addAccount({ pubkey, relays: [relayUrl], ...encrypted, readonly: false }); - clientRelaysService.bootstrapRelays.add(relayUrl); accountService.switchAccount(pubkey); };