mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-27 12:07:43 +02:00
cleanup
This commit is contained in:
15
README.md
15
README.md
@@ -52,23 +52,20 @@
|
|||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- add `client` tag to published events
|
- Rebuild relays view to show relay info and settings NIP-11
|
||||||
- add button for creating lightning invoice via WebLN
|
- add button for creating lightning invoice via WebLN
|
||||||
- make app a valid web share target https://developer.chrome.com/articles/web-share-target/
|
|
||||||
- make app handle image files
|
|
||||||
- block notes based on content
|
|
||||||
- implement NIP-56 and blocking
|
|
||||||
- allow user to select relay or following list when fetching replies (default to my relays + following?)
|
- allow user to select relay or following list when fetching replies (default to my relays + following?)
|
||||||
- massive thread note1dapvuu8fl09yjtg2gyr2h6nypaffl2sq0aj5raz86463qk5kpyzqlxvtc3
|
- massive thread note1dapvuu8fl09yjtg2gyr2h6nypaffl2sq0aj5raz86463qk5kpyzqlxvtc3
|
||||||
- sort replies by date
|
|
||||||
- filter list of followers by users the user has blocked/reported (stops bots/spammers from showing up at followers)
|
- filter list of followers by users the user has blocked/reported (stops bots/spammers from showing up at followers)
|
||||||
- Add client side relay groups
|
|
||||||
- Add mentions in posts (https://css-tricks.com/so-you-want-to-build-an-mention-autocomplete-feature/)
|
|
||||||
- Add note embeds
|
- Add note embeds
|
||||||
- Add "repost" button that mentions the note
|
- Add "repost" button that mentions the note
|
||||||
- Add preview tab to note modal
|
- Add preview tab to note modal
|
||||||
|
- Add mentions in posts (https://css-tricks.com/so-you-want-to-build-an-mention-autocomplete-feature/)
|
||||||
|
- add `client` tag to published events
|
||||||
- Save note drafts and let users manage them
|
- Save note drafts and let users manage them
|
||||||
- Add support for relay favicons
|
- make app a valid web share target https://developer.chrome.com/articles/web-share-target/
|
||||||
|
- implement NIP-56 and blocking
|
||||||
|
- block notes based on content
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Subject, Subscription } from "rxjs";
|
import { Subject, Subscription } from "rxjs";
|
||||||
import { relayPool } from "../services/relays";
|
import relayPoolService from "../services/relay-pool";
|
||||||
import { NostrEvent } from "../types/nostr-event";
|
import { NostrEvent } from "../types/nostr-event";
|
||||||
|
|
||||||
export type PostResult = { url: string; message?: string; status: boolean };
|
export type PostResult = { url: string; message?: string; status: boolean };
|
||||||
@@ -9,7 +9,7 @@ export function nostrPostAction(relays: string[], event: NostrEvent, timeout: nu
|
|||||||
let remaining = new Set<Subscription>();
|
let remaining = new Set<Subscription>();
|
||||||
|
|
||||||
for (const url of relays) {
|
for (const url of relays) {
|
||||||
const relay = relayPool.requestRelay(url);
|
const relay = relayPoolService.requestRelay(url);
|
||||||
|
|
||||||
const sub = relay.onCommandResult.subscribe((result) => {
|
const sub = relay.onCommandResult.subscribe((result) => {
|
||||||
if (result.eventId === event.id) {
|
if (result.eventId === event.id) {
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { Subject, Subscription as RxSubscription } from "rxjs";
|
import { Subject, Subscription as RxSubscription } from "rxjs";
|
||||||
import { NostrEvent } from "../types/nostr-event";
|
import { NostrEvent } from "../types/nostr-event";
|
||||||
import { NostrQuery } from "../types/nostr-query";
|
import { NostrQuery } from "../types/nostr-query";
|
||||||
import { Relay } from "../services/relays";
|
import relayPoolService from "../services/relay-pool";
|
||||||
import relayPool from "../services/relays/relay-pool";
|
import { Relay } from "./relay";
|
||||||
|
|
||||||
let lastId = 0;
|
let lastId = 0;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ export class NostrRequest {
|
|||||||
|
|
||||||
constructor(relayUrls: string[], timeout?: number) {
|
constructor(relayUrls: string[], timeout?: number) {
|
||||||
this.id = `request-${lastId++}`;
|
this.id = `request-${lastId++}`;
|
||||||
this.relays = new Set(relayUrls.map((url) => relayPool.requestRelay(url)));
|
this.relays = new Set(relayUrls.map((url) => relayPoolService.requestRelay(url)));
|
||||||
|
|
||||||
for (const relay of this.relays) {
|
for (const relay of this.relays) {
|
||||||
const cleanup: RxSubscription[] = [];
|
const cleanup: RxSubscription[] = [];
|
||||||
|
@@ -1,9 +1,8 @@
|
|||||||
import { Subject, SubscriptionLike } from "rxjs";
|
import { Subject, SubscriptionLike } from "rxjs";
|
||||||
import { NostrEvent } from "../types/nostr-event";
|
import { NostrEvent } from "../types/nostr-event";
|
||||||
import { NostrOutgoingMessage, NostrQuery } from "../types/nostr-query";
|
import { NostrOutgoingMessage, NostrQuery } from "../types/nostr-query";
|
||||||
import { Relay } from "../services/relays";
|
import { IncomingEvent, Relay } from "./relay";
|
||||||
import { IncomingEvent } from "../services/relays/relay";
|
import relayPoolService from "../services/relay-pool";
|
||||||
import relayPool from "../services/relays/relay-pool";
|
|
||||||
|
|
||||||
let lastId = 0;
|
let lastId = 0;
|
||||||
|
|
||||||
@@ -27,7 +26,7 @@ export class NostrSubscription {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
this.relayUrls = relayUrls;
|
this.relayUrls = relayUrls;
|
||||||
|
|
||||||
this.relays = relayUrls.map((url) => relayPool.requestRelay(url));
|
this.relays = relayUrls.map((url) => relayPoolService.requestRelay(url));
|
||||||
}
|
}
|
||||||
private handleEvent(event: IncomingEvent) {
|
private handleEvent(event: IncomingEvent) {
|
||||||
if (this.state === NostrSubscription.OPEN && event.subId === this.id && !this.seenEvents.has(event.body.id)) {
|
if (this.state === NostrSubscription.OPEN && event.subId === this.id && !this.seenEvents.has(event.body.id)) {
|
||||||
@@ -51,7 +50,7 @@ export class NostrSubscription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const url of this.relayUrls) {
|
for (const url of this.relayUrls) {
|
||||||
relayPool.addClaim(url, this);
|
relayPoolService.addClaim(url, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** listen for event and open events from relays */
|
/** listen for event and open events from relays */
|
||||||
@@ -60,7 +59,7 @@ export class NostrSubscription {
|
|||||||
this.cleanup.clear();
|
this.cleanup.clear();
|
||||||
|
|
||||||
for (const url of this.relayUrls) {
|
for (const url of this.relayUrls) {
|
||||||
relayPool.removeClaim(url, this);
|
relayPoolService.removeClaim(url, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +87,7 @@ export class NostrSubscription {
|
|||||||
}
|
}
|
||||||
setRelays(relays: string[]) {
|
setRelays(relays: string[]) {
|
||||||
this.unsubscribeFromRelays();
|
this.unsubscribeFromRelays();
|
||||||
const newRelays = relays.map((url) => relayPool.requestRelay(url));
|
const newRelays = relays.map((url) => relayPoolService.requestRelay(url));
|
||||||
|
|
||||||
for (const relay of this.relays) {
|
for (const relay of this.relays) {
|
||||||
if (!newRelays.includes(relay)) {
|
if (!newRelays.includes(relay)) {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Subject } from "rxjs";
|
import { Subject } from "rxjs";
|
||||||
import { RawIncomingNostrEvent, NostrEvent } from "../../types/nostr-event";
|
import { RawIncomingNostrEvent, NostrEvent } from "../types/nostr-event";
|
||||||
import { NostrOutgoingMessage } from "../../types/nostr-query";
|
import { NostrOutgoingMessage } from "../types/nostr-query";
|
||||||
|
|
||||||
export type IncomingEvent = {
|
export type IncomingEvent = {
|
||||||
type: "EVENT";
|
type: "EVENT";
|
@@ -1,93 +0,0 @@
|
|||||||
import { Subject } from "rxjs";
|
|
||||||
import { NostrEvent } from "../types/nostr-event";
|
|
||||||
import { NostrQuery } from "../types/nostr-query";
|
|
||||||
import { NostrRequest } from "./nostr-request";
|
|
||||||
|
|
||||||
function mergeSets<T extends unknown>(to: Set<T>, from: Iterable<T>) {
|
|
||||||
for (const el of from) {
|
|
||||||
to.add(el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type getQueryKeyFn<QueryT> = (query: QueryT) => string;
|
|
||||||
export type mergeQueriesFn<QueryT> = (a: QueryT, b: QueryT) => QueryT | undefined | null;
|
|
||||||
export type getEventQueryKeyFn = (event: NostrEvent) => string;
|
|
||||||
|
|
||||||
type PendingRequest<QueryT = NostrQuery> = {
|
|
||||||
query: QueryT;
|
|
||||||
subject: Subject<NostrEvent>;
|
|
||||||
relays: Set<string>;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @deprecated incomplete */
|
|
||||||
export class RequestManager<QueryT extends NostrQuery> {
|
|
||||||
private getQueryKey: getQueryKeyFn<QueryT>;
|
|
||||||
private mergeQueries: mergeQueriesFn<QueryT>;
|
|
||||||
private getEventQueryKey: getEventQueryKeyFn;
|
|
||||||
|
|
||||||
private runningRequests = new Map<string, NostrRequest>();
|
|
||||||
private requestQueue = new Map<string, PendingRequest<QueryT>>();
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
getQueryKey: getQueryKeyFn<QueryT>,
|
|
||||||
mergeQueries: mergeQueriesFn<QueryT>,
|
|
||||||
getEventQueryKey: getEventQueryKeyFn
|
|
||||||
) {
|
|
||||||
this.getQueryKey = getQueryKey;
|
|
||||||
this.mergeQueries = mergeQueries;
|
|
||||||
this.getEventQueryKey = getEventQueryKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
request(query: QueryT, relays: string[]) {
|
|
||||||
const key = this.getQueryKey(query);
|
|
||||||
if (this.runningRequests.has(key)) throw new Error("requesting a currently running query");
|
|
||||||
|
|
||||||
const pending = this.requestQueue.get(key);
|
|
||||||
if (pending) {
|
|
||||||
mergeSets(pending.relays, relays);
|
|
||||||
return pending.subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
const subject = new Subject<NostrEvent>();
|
|
||||||
this.requestQueue.set(key, {
|
|
||||||
query,
|
|
||||||
relays: new Set(relays),
|
|
||||||
subject,
|
|
||||||
});
|
|
||||||
|
|
||||||
return subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
batch() {
|
|
||||||
const requests: PendingRequest<QueryT>[] = [];
|
|
||||||
|
|
||||||
for (const [key, pending] of this.requestQueue) {
|
|
||||||
let wasMerged = false;
|
|
||||||
if (requests.length > 0) {
|
|
||||||
for (const request of requests) {
|
|
||||||
const merged = this.mergeQueries(request.query, pending.query);
|
|
||||||
if (merged) {
|
|
||||||
request.query = merged;
|
|
||||||
request.subject.subscribe(pending.subject);
|
|
||||||
mergeSets(request.relays, pending.relays);
|
|
||||||
wasMerged = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there are no requests. or pending failed to merge create new request
|
|
||||||
if (!wasMerged) {
|
|
||||||
const subject = new Subject<NostrEvent>();
|
|
||||||
subject.subscribe(pending.subject);
|
|
||||||
requests.push({ query: pending.query, subject, relays: pending.relays });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const request of requests) {
|
|
||||||
const r = new NostrRequest(Array.from(request.relays));
|
|
||||||
r.onEvent.subscribe(request.subject);
|
|
||||||
r.start(request.query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -10,20 +10,20 @@ import {
|
|||||||
ModalCloseButton,
|
ModalCloseButton,
|
||||||
Button,
|
Button,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { Relay } from "../services/relays";
|
import relayPoolService from "../services/relay-pool";
|
||||||
import relayPool from "../services/relays/relay-pool";
|
|
||||||
import { useInterval } from "react-use";
|
import { useInterval } from "react-use";
|
||||||
import { RelayStatus } from "./relay-status";
|
import { RelayStatus } from "./relay-status";
|
||||||
import { useIsMobile } from "../hooks/use-is-mobile";
|
import { useIsMobile } from "../hooks/use-is-mobile";
|
||||||
import { RelayIcon } from "./icons";
|
import { RelayIcon } from "./icons";
|
||||||
|
import { Relay } from "../classes/relay";
|
||||||
|
|
||||||
export const ConnectedRelays = () => {
|
export const ConnectedRelays = () => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const [relays, setRelays] = useState<Relay[]>(relayPool.getRelays());
|
const [relays, setRelays] = useState<Relay[]>(relayPoolService.getRelays());
|
||||||
|
|
||||||
useInterval(() => {
|
useInterval(() => {
|
||||||
setRelays(relayPool.getRelays());
|
setRelays(relayPoolService.getRelays());
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
const connected = relays.filter((relay) => relay.okay);
|
const connected = relays.filter((relay) => relay.okay);
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Box, Button, ButtonGroup, Heading, IconButton, Text } from "@chakra-ui/react";
|
import { Box, Button, ButtonGroup, IconButton, Text } from "@chakra-ui/react";
|
||||||
import { requestProvider } from "webln";
|
import { requestProvider } from "webln";
|
||||||
import { getReadableAmount, parsePaymentRequest } from "../helpers/bolt11";
|
import { getReadableAmount, parsePaymentRequest } from "../helpers/bolt11";
|
||||||
import { useAsync } from "react-use";
|
import { useAsync } from "react-use";
|
||||||
|
@@ -16,11 +16,11 @@ import { nostrPostAction } from "../../classes/nostr-post-action";
|
|||||||
import { NostrRequest } from "../../classes/nostr-request";
|
import { NostrRequest } from "../../classes/nostr-request";
|
||||||
import useSubject from "../../hooks/use-subject";
|
import useSubject from "../../hooks/use-subject";
|
||||||
import { getEventRelays, handleEventFromRelay } from "../../services/event-relays";
|
import { getEventRelays, handleEventFromRelay } from "../../services/event-relays";
|
||||||
import { relayPool } from "../../services/relays";
|
|
||||||
import { NostrEvent } from "../../types/nostr-event";
|
import { NostrEvent } from "../../types/nostr-event";
|
||||||
import { RelayIcon, SearchIcon } from "../icons";
|
import { RelayIcon, SearchIcon } from "../icons";
|
||||||
import { RelayFavicon } from "../relay-favicon";
|
import { RelayFavicon } from "../relay-favicon";
|
||||||
import { useReadRelayUrls, useWriteRelayUrls } from "../../hooks/use-client-relays";
|
import { useReadRelayUrls, useWriteRelayUrls } from "../../hooks/use-client-relays";
|
||||||
|
import relayPoolService from "../../services/relay-pool";
|
||||||
|
|
||||||
export type NoteRelaysProps = Omit<IconButtonProps, "icon" | "aria-label"> & {
|
export type NoteRelaysProps = Omit<IconButtonProps, "icon" | "aria-label"> & {
|
||||||
event: NostrEvent;
|
event: NostrEvent;
|
||||||
@@ -56,7 +56,7 @@ export const NoteRelays = memo(({ event, ...props }: NoteRelaysProps) => {
|
|||||||
action.subscribe({
|
action.subscribe({
|
||||||
next: (result) => {
|
next: (result) => {
|
||||||
if (result.status) {
|
if (result.status) {
|
||||||
handleEventFromRelay(relayPool.requestRelay(result.url, false), event);
|
handleEventFromRelay(relayPoolService.requestRelay(result.url, false), event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
complete: () => {
|
complete: () => {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Badge, useForceUpdate } from "@chakra-ui/react";
|
import { Badge, useForceUpdate } from "@chakra-ui/react";
|
||||||
import { useInterval } from "react-use";
|
import { useInterval } from "react-use";
|
||||||
import { Relay, relayPool } from "../services/relays";
|
import { Relay } from "../classes/relay";
|
||||||
|
import relayPoolService from "../services/relay-pool";
|
||||||
|
|
||||||
const getStatusText = (relay: Relay) => {
|
const getStatusText = (relay: Relay) => {
|
||||||
if (relay.connecting) return "Connecting...";
|
if (relay.connecting) return "Connecting...";
|
||||||
@@ -20,7 +21,7 @@ const getStatusColor = (relay: Relay) => {
|
|||||||
export const RelayStatus = ({ url }: { url: string }) => {
|
export const RelayStatus = ({ url }: { url: string }) => {
|
||||||
const update = useForceUpdate();
|
const update = useForceUpdate();
|
||||||
|
|
||||||
const relay = relayPool.requestRelay(url, false);
|
const relay = relayPoolService.requestRelay(url, false);
|
||||||
|
|
||||||
useInterval(() => update(), 500);
|
useInterval(() => update(), 500);
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { getEventRelays } from "../services/event-relays";
|
import { getEventRelays } from "../services/event-relays";
|
||||||
import { DraftNostrEvent, isETag, isPTag, NostrEvent, RTag } from "../types/nostr-event";
|
import { DraftNostrEvent, isETag, isPTag, NostrEvent, RTag } from "../types/nostr-event";
|
||||||
import { RelayConfig, RelayMode } from "../services/relays/relay";
|
import { RelayConfig, RelayMode } from "../classes/relay";
|
||||||
|
|
||||||
export function isReply(event: NostrEvent | DraftNostrEvent) {
|
export function isReply(event: NostrEvent | DraftNostrEvent) {
|
||||||
return !!event.tags.find((tag) => isETag(tag) && tag[3] !== "mention");
|
return !!event.tags.find((tag) => isETag(tag) && tag[3] !== "mention");
|
||||||
|
8
src/hooks/use-client-relays copy.ts
Normal file
8
src/hooks/use-client-relays copy.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { useAsync } from "react-use";
|
||||||
|
import relayInfoService from "../services/relay-info";
|
||||||
|
|
||||||
|
export function useRelayInfo(relay: string) {
|
||||||
|
const { value: info, loading, error } = useAsync(() => relayInfoService.getInfo(relay));
|
||||||
|
|
||||||
|
return { info, loading, error };
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
import { unique } from "../helpers/array";
|
import { unique } from "../helpers/array";
|
||||||
import clientRelaysService from "../services/client-relays";
|
import clientRelaysService from "../services/client-relays";
|
||||||
import { RelayMode } from "../services/relays/relay";
|
import { RelayMode } from "../classes/relay";
|
||||||
import useSubject from "./use-subject";
|
import useSubject from "./use-subject";
|
||||||
|
|
||||||
export function useClientRelays(mode: RelayMode = RelayMode.READ) {
|
export function useClientRelays(mode: RelayMode = RelayMode.READ) {
|
||||||
|
@@ -4,7 +4,7 @@ import { nostrPostAction } from "../classes/nostr-post-action";
|
|||||||
import { unique } from "../helpers/array";
|
import { unique } from "../helpers/array";
|
||||||
import { DraftNostrEvent, RTag } from "../types/nostr-event";
|
import { DraftNostrEvent, RTag } from "../types/nostr-event";
|
||||||
import identity from "./identity";
|
import identity from "./identity";
|
||||||
import { RelayConfig, RelayMode } from "./relays/relay";
|
import { RelayConfig, RelayMode } from "../classes/relay";
|
||||||
import userRelaysService from "./user-relays";
|
import userRelaysService from "./user-relays";
|
||||||
|
|
||||||
export type RelayDirectory = Record<string, { read: boolean; write: boolean }>;
|
export type RelayDirectory = Record<string, { read: boolean; write: boolean }>;
|
||||||
|
@@ -34,10 +34,10 @@ const MIGRATIONS: MigrationFunction[] = [
|
|||||||
dnsIdentifiers.createIndex("domain", "domain", { unique: false });
|
dnsIdentifiers.createIndex("domain", "domain", { unique: false });
|
||||||
dnsIdentifiers.createIndex("updated", "updated", { unique: false });
|
dnsIdentifiers.createIndex("updated", "updated", { unique: false });
|
||||||
|
|
||||||
const pubkeyRelayWeights = db.createObjectStore("pubkeyRelayWeights", { keyPath: "pubkey" });
|
db.createObjectStore("pubkeyRelayWeights", { keyPath: "pubkey" });
|
||||||
|
|
||||||
// setup data
|
db.createObjectStore("settings");
|
||||||
const settings = db.createObjectStore("settings");
|
db.createObjectStore("relayInfo");
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -65,6 +65,7 @@ export async function clearCacheData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteDatabase() {
|
export async function deleteDatabase() {
|
||||||
|
db.close();
|
||||||
await deleteDB(dbName);
|
await deleteDB(dbName);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { DBSchema } from "idb";
|
import { DBSchema } from "idb";
|
||||||
import { NostrEvent } from "../../types/nostr-event";
|
import { NostrEvent } from "../../types/nostr-event";
|
||||||
import { RelayConfig } from "../relays/relay";
|
import { RelayInformationDocument } from "../relay-info";
|
||||||
|
|
||||||
export interface CustomSchema extends DBSchema {
|
export interface CustomSchema extends DBSchema {
|
||||||
userMetadata: {
|
userMetadata: {
|
||||||
@@ -21,7 +21,7 @@ export interface CustomSchema extends DBSchema {
|
|||||||
};
|
};
|
||||||
userRelays: {
|
userRelays: {
|
||||||
key: string;
|
key: string;
|
||||||
value: { pubkey: string; relays: {url: string, mode: number}[]; created_at: number };
|
value: { pubkey: string; relays: { url: string; mode: number }[]; created_at: number };
|
||||||
indexes: { created_at: number };
|
indexes: { created_at: number };
|
||||||
};
|
};
|
||||||
dnsIdentifiers: {
|
dnsIdentifiers: {
|
||||||
@@ -29,6 +29,7 @@ export interface CustomSchema extends DBSchema {
|
|||||||
value: { name: string; domain: string; pubkey: string; relays: string[]; updated: number };
|
value: { name: string; domain: string; pubkey: string; relays: string[]; updated: number };
|
||||||
indexes: { name: string; domain: string; pubkey: string; updated: number };
|
indexes: { name: string; domain: string; pubkey: string; updated: number };
|
||||||
};
|
};
|
||||||
|
relayInfo: { key: string; value: RelayInformationDocument };
|
||||||
pubkeyRelayWeights: {
|
pubkeyRelayWeights: {
|
||||||
key: string;
|
key: string;
|
||||||
value: { pubkey: string; relays: Record<string, number>; updated: number };
|
value: { pubkey: string; relays: Record<string, number>; updated: number };
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
import { Relay } from "../classes/relay";
|
||||||
import { NostrEvent } from "../types/nostr-event";
|
import { NostrEvent } from "../types/nostr-event";
|
||||||
import { Relay, relayPool } from "./relays";
|
import relayPoolService from "./relay-pool";
|
||||||
|
|
||||||
const eventRelays = new Map<string, BehaviorSubject<string[]>>();
|
const eventRelays = new Map<string, BehaviorSubject<string[]>>();
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ export function handleEventFromRelay(relay: Relay, event: NostrEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
relayPool.onRelayCreated.subscribe((relay) => {
|
relayPoolService.onRelayCreated.subscribe((relay) => {
|
||||||
relay.onEvent.subscribe(({ body: event }) => {
|
relay.onEvent.subscribe(({ body: event }) => {
|
||||||
handleEventFromRelay(relay, event);
|
handleEventFromRelay(relay, event);
|
||||||
});
|
});
|
||||||
|
57
src/services/relay-info.ts
Normal file
57
src/services/relay-info.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import db from "./db";
|
||||||
|
|
||||||
|
export type RelayInformationDocument = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
pubkey: string;
|
||||||
|
contact: string;
|
||||||
|
supported_nips: string;
|
||||||
|
software: string;
|
||||||
|
version: string;
|
||||||
|
};
|
||||||
|
export type DnsIdentity = {
|
||||||
|
name: string;
|
||||||
|
domain: string;
|
||||||
|
pubkey: string;
|
||||||
|
relays: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
async function fetchInfo(relay: string) {
|
||||||
|
const url = new URL(relay);
|
||||||
|
url.protocol = url.protocol === "ws:" ? "http" : "https";
|
||||||
|
|
||||||
|
const infoDoc = await fetch(url, { headers: { Accept: "application/nostr+json" } }).then(
|
||||||
|
(res) => res.json() as Promise<RelayInformationDocument>
|
||||||
|
);
|
||||||
|
|
||||||
|
await db.put("relayInfo", infoDoc, relay);
|
||||||
|
|
||||||
|
return infoDoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getInfo(relay: string) {
|
||||||
|
const cached = await db.get("relayInfo", relay);
|
||||||
|
if (cached) return cached;
|
||||||
|
|
||||||
|
// TODO: if it fails, maybe cache a failure message
|
||||||
|
return fetchInfo(relay);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pending: Record<string, ReturnType<typeof getInfo> | undefined> = {};
|
||||||
|
function dedupedGetIdentity(relay: string) {
|
||||||
|
const request = pending[relay];
|
||||||
|
if (request) return request;
|
||||||
|
return (pending[relay] = getInfo(relay));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const relayInfoService = {
|
||||||
|
fetchInfo,
|
||||||
|
getInfo: dedupedGetIdentity,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (import.meta.env.DEV) {
|
||||||
|
// @ts-ignore
|
||||||
|
window.relayInfoService = relayInfoService;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default relayInfoService;
|
@@ -1,7 +1,7 @@
|
|||||||
import { Subject } from "rxjs";
|
import { Subject } from "rxjs";
|
||||||
import { Relay } from "./relay";
|
import { Relay } from "../classes/relay";
|
||||||
|
|
||||||
export class RelayPool {
|
export class RelayPoolService {
|
||||||
relays = new Map<string, Relay>();
|
relays = new Map<string, Relay>();
|
||||||
relayClaims = new Map<string, Set<any>>();
|
relayClaims = new Map<string, Set<any>>();
|
||||||
onRelayCreated = new Subject<Relay>();
|
onRelayCreated = new Subject<Relay>();
|
||||||
@@ -64,11 +64,11 @@ export class RelayPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const relayPool = new RelayPool();
|
const relayPoolService = new RelayPoolService();
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.relayPool = relayPool;
|
window.relayPoolService = relayPoolService;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default relayPool;
|
export default relayPoolService;
|
@@ -1,4 +0,0 @@
|
|||||||
import relayPool, { RelayPool } from "./relay-pool";
|
|
||||||
import { Relay } from "./relay";
|
|
||||||
|
|
||||||
export { relayPool, Relay, RelayPool };
|
|
@@ -3,7 +3,7 @@ import { NostrSubscription } from "../classes/nostr-subscription";
|
|||||||
import { PubkeySubjectCache } from "../classes/pubkey-subject-cache";
|
import { PubkeySubjectCache } from "../classes/pubkey-subject-cache";
|
||||||
import { isRTag, NostrEvent } from "../types/nostr-event";
|
import { isRTag, NostrEvent } from "../types/nostr-event";
|
||||||
import { NostrQuery } from "../types/nostr-query";
|
import { NostrQuery } from "../types/nostr-query";
|
||||||
import { RelayConfig } from "./relays/relay";
|
import { RelayConfig } from "../classes/relay";
|
||||||
import { parseRTag } from "../helpers/nostr-event";
|
import { parseRTag } from "../helpers/nostr-event";
|
||||||
import clientRelaysService from "./client-relays";
|
import clientRelaysService from "./client-relays";
|
||||||
|
|
||||||
|
@@ -19,13 +19,16 @@ import { TrashIcon, UndoIcon } from "../../components/icons";
|
|||||||
import useSubject from "../../hooks/use-subject";
|
import useSubject from "../../hooks/use-subject";
|
||||||
import { RelayFavicon } from "../../components/relay-favicon";
|
import { RelayFavicon } from "../../components/relay-favicon";
|
||||||
import clientRelaysService from "../../services/client-relays";
|
import clientRelaysService from "../../services/client-relays";
|
||||||
import { RelayConfig, RelayMode } from "../../services/relays/relay";
|
import { RelayConfig, RelayMode } from "../../classes/relay";
|
||||||
import { useList } from "react-use";
|
import { useList } from "react-use";
|
||||||
import { RelayUrlInput } from "../../components/relay-url-input";
|
import { RelayUrlInput } from "../../components/relay-url-input";
|
||||||
|
import { useRelayInfo } from "../../hooks/use-client-relays copy";
|
||||||
|
|
||||||
export const RelaysView = () => {
|
export const RelaysView = () => {
|
||||||
const relays = useSubject(clientRelaysService.relays);
|
const relays = useSubject(clientRelaysService.relays);
|
||||||
|
|
||||||
|
const info = useRelayInfo("wss://nostr.wine");
|
||||||
|
|
||||||
const [pendingAdd, addActions] = useList<RelayConfig>([]);
|
const [pendingAdd, addActions] = useList<RelayConfig>([]);
|
||||||
const [pendingRemove, removeActions] = useList<RelayConfig>([]);
|
const [pendingRemove, removeActions] = useList<RelayConfig>([]);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user