small fixes

This commit is contained in:
hzrd149
2023-08-07 08:29:37 -05:00
parent 1c759c0d20
commit 2d6ead0db4
7 changed files with 41 additions and 14 deletions

View File

@@ -8,7 +8,7 @@ interface ConnectableApi<T> {
connect(connectable: Connectable<T>): this; connect(connectable: Connectable<T>): this;
disconnect(connectable: Connectable<T>): this; disconnect(connectable: Connectable<T>): this;
} }
type Connection<From, To = From, Prev = To> = (value: From, next: (value: To) => any, prevValue: Prev) => void; export type Connection<From, To = From, Prev = To> = (value: From, next: (value: To) => any, prevValue: Prev) => void;
export class Subject<Value> implements Connectable<Value> { export class Subject<Value> implements Connectable<Value> {
listeners: [ListenerFn<Value>, Object | undefined][] = []; listeners: [ListenerFn<Value>, Object | undefined][] = [];

View File

@@ -23,6 +23,10 @@ export function safeRelayUrl(relayUrl: string) {
return null; return null;
} }
export function safeRelayUrls(urls: string[]): string[] {
return urls.map(safeRelayUrl).filter(Boolean) as string[];
}
export function replaceDomain(url: string | URL, replacementUrl: string | URL) { export function replaceDomain(url: string | URL, replacementUrl: string | URL) {
const newUrl = new URL(url); const newUrl = new URL(url);
replacementUrl = convertToUrl(replacementUrl); replacementUrl = convertToUrl(replacementUrl);

View File

@@ -5,8 +5,9 @@ import { DraftNostrEvent, RTag } from "../types/nostr-event";
import accountService from "./account"; import accountService from "./account";
import { RelayConfig, RelayMode } from "../classes/relay"; import { RelayConfig, RelayMode } from "../classes/relay";
import userRelaysService, { ParsedUserRelays } from "./user-relays"; import userRelaysService, { ParsedUserRelays } from "./user-relays";
import { PersistentSubject, Subject } from "../classes/subject"; import { Connection, PersistentSubject, Subject } from "../classes/subject";
import signingService from "./signing"; import signingService from "./signing";
import { logger } from "../helpers/debug";
export type RelayDirectory = Record<string, { read: boolean; write: boolean }>; export type RelayDirectory = Record<string, { read: boolean; write: boolean }>;
@@ -18,21 +19,30 @@ const DEFAULT_RELAYS = [
{ url: "wss://nos.lol", mode: RelayMode.READ }, { url: "wss://nos.lol", mode: RelayMode.READ },
]; ];
const userRelaysToRelayConfig: Connection<ParsedUserRelays, RelayConfig[], RelayConfig[] | undefined> = (
userRelays,
next
) => next(userRelays.relays);
class ClientRelayService { class ClientRelayService {
bootstrapRelays = new Set<string>(); bootstrapRelays = new Set<string>();
relays = new PersistentSubject<RelayConfig[]>([]); relays = new PersistentSubject<RelayConfig[]>([]);
writeRelays = new PersistentSubject<RelayConfig[]>([]); writeRelays = new PersistentSubject<RelayConfig[]>([]);
readRelays = new PersistentSubject<RelayConfig[]>([]); readRelays = new PersistentSubject<RelayConfig[]>([]);
log = logger.extend("ClientRelays");
constructor() { constructor() {
let lastSubject: Subject<ParsedUserRelays> | undefined; let lastSubject: Subject<ParsedUserRelays> | undefined;
accountService.current.subscribe((account) => { accountService.current.subscribe((account) => {
if (!account) { if (!account) {
this.log("No account, using default relays");
this.relays.next(DEFAULT_RELAYS); this.relays.next(DEFAULT_RELAYS);
return; return;
} else this.relays.next([]); } else this.relays.next([]);
if (account.relays) { if (account.relays) {
this.log("Found bootstrap relays");
this.bootstrapRelays.clear(); this.bootstrapRelays.clear();
for (const relay of account.relays) { for (const relay of account.relays) {
this.bootstrapRelays.add(relay); this.bootstrapRelays.add(relay);
@@ -40,41 +50,47 @@ class ClientRelayService {
} }
if (lastSubject) { if (lastSubject) {
lastSubject.unsubscribe(this.handleRelayChanged, this); this.log("Disconnecting from previous user relays");
this.relays.disconnect(lastSubject);
lastSubject = undefined; lastSubject = undefined;
} }
// load the relays from cache or bootstrap relays // 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)); lastSubject = userRelaysService.requestRelays(account.pubkey, Array.from(this.bootstrapRelays));
setTimeout(() => { setTimeout(() => {
// double check for new relay notes // double check for new relay notes
this.log("Requesting latest relays from the write relays");
userRelaysService.requestRelays(account.pubkey, this.getWriteUrls(), true); userRelaysService.requestRelays(account.pubkey, this.getWriteUrls(), true);
}, 1000); }, 1000);
lastSubject.subscribe(this.handleRelayChanged, this); this.relays.connectWithHandler(lastSubject, userRelaysToRelayConfig);
}); });
this.relays.subscribe((relays) => this.writeRelays.next(relays.filter((r) => r.mode & RelayMode.WRITE))); // set the read and write relays
this.relays.subscribe((relays) => this.readRelays.next(relays.filter((r) => r.mode & RelayMode.READ))); this.relays.subscribe((relays) => {
} this.log("Got new relay list");
this.writeRelays.next(relays.filter((r) => r.mode & RelayMode.WRITE));
private handleRelayChanged(relays: ParsedUserRelays) { this.readRelays.next(relays.filter((r) => r.mode & RelayMode.READ));
this.relays.next(relays.relays); });
} }
async addRelay(url: string, mode: RelayMode) { async addRelay(url: string, mode: RelayMode) {
this.log(`Adding ${url} relay`);
if (!this.relays.value.some((r) => r.url === url)) { if (!this.relays.value.some((r) => r.url === url)) {
const newRelays = [...this.relays.value, { url, mode }]; const newRelays = [...this.relays.value, { url, mode }];
await this.postUpdatedRelays(newRelays); await this.postUpdatedRelays(newRelays);
} }
} }
async updateRelay(url: string, mode: RelayMode) { async updateRelay(url: string, mode: RelayMode) {
this.log(`Updating ${url} relay`);
if (this.relays.value.some((r) => r.url === url)) { if (this.relays.value.some((r) => r.url === url)) {
const newRelays = this.relays.value.map((r) => (r.url === url ? { url, mode } : r)); const newRelays = this.relays.value.map((r) => (r.url === url ? { url, mode } : r));
await this.postUpdatedRelays(newRelays); await this.postUpdatedRelays(newRelays);
} }
} }
async removeRelay(url: string) { async removeRelay(url: string) {
this.log(`Removing ${url} relay`);
if (this.relays.value.some((r) => r.url === url)) { if (this.relays.value.some((r) => r.url === url)) {
const newRelays = this.relays.value.filter((r) => r.url !== url); const newRelays = this.relays.value.filter((r) => r.url !== url);
await this.postUpdatedRelays(newRelays); await this.postUpdatedRelays(newRelays);

View File

@@ -1,5 +1,6 @@
import createDefer, { Deferred } from "../classes/deferred"; import createDefer, { Deferred } from "../classes/deferred";
import { NostrRequest } from "../classes/nostr-request"; import { NostrRequest } from "../classes/nostr-request";
import { safeRelayUrl, safeRelayUrls } from "../helpers/url";
import { NostrEvent } from "../types/nostr-event"; import { NostrEvent } from "../types/nostr-event";
class SingleEventService { class SingleEventService {
@@ -11,7 +12,7 @@ class SingleEventService {
const event = this.eventCache.get(id); const event = this.eventCache.get(id);
if (event) return event; if (event) return event;
this.pending.set(id, this.pending.get(id)?.concat(relays) ?? relays); this.pending.set(id, this.pending.get(id)?.concat(relays) ?? safeRelayUrls(relays));
const deferred = createDefer<NostrEvent>(); const deferred = createDefer<NostrEvent>();
this.pendingPromises.set(id, deferred); this.pendingPromises.set(id, deferred);
return deferred; return deferred;

View File

@@ -29,7 +29,13 @@ class UserRelaysService {
} }
requestRelays(pubkey: string, relays: string[], alwaysRequest = false) { requestRelays(pubkey: string, relays: string[], alwaysRequest = false) {
const sub = this.subjects.get(pubkey); const sub = this.subjects.get(pubkey);
const requestSub = replaceableEventLoaderService.requestEvent(relays, Kind.RelayList, pubkey); const requestSub = replaceableEventLoaderService.requestEvent(
relays,
Kind.RelayList,
pubkey,
undefined,
alwaysRequest
);
sub.connectWithHandler(requestSub, (event, next) => next(parseRelaysEvent(event))); sub.connectWithHandler(requestSub, (event, next) => next(parseRelaysEvent(event)));
// also fetch the relays from the users contacts // also fetch the relays from the users contacts

View File

@@ -38,7 +38,7 @@ export default function RelaysView() {
return ( return (
<Flex direction="column" gap="2" p="2"> <Flex direction="column" gap="2" p="2">
<Flex alignItems="center" gap="2"> <Flex alignItems="center" gap="2" wrap="wrap">
<Input type="search" placeholder="search" value={search} onChange={(e) => setSearch(e.target.value)} w="auto" /> <Input type="search" placeholder="search" value={search} onChange={(e) => setSearch(e.target.value)} w="auto" />
<Switch isChecked={showAll.isOpen} onChange={showAll.onToggle}> <Switch isChecked={showAll.isOpen} onChange={showAll.onToggle}>
Show All Show All

View File

@@ -105,7 +105,7 @@ function RelayPage({ relay }: { relay: string }) {
<RelayJoinAction url={relay} /> <RelayJoinAction url={relay} />
</Flex> </Flex>
<RelayMetadata url={relay} /> <RelayMetadata url={relay} />
<Flex gap="2"> <Flex gap="2" wrap="wrap">
{info?.supported_nips?.map((nip) => ( {info?.supported_nips?.map((nip) => (
<NipTag key={nip} nip={nip} /> <NipTag key={nip} nip={nip} />
))} ))}