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);
};