mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-17 21:31:43 +01:00
support switching cache relay without reload
This commit is contained in:
parent
a957ddb5d2
commit
ea15fa1dd1
@ -1,194 +0,0 @@
|
||||
import { nanoid } from "nanoid";
|
||||
import { Filter, NostrEvent } from "nostr-tools";
|
||||
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||
import { IConnectionPool } from "applesauce-net/connection";
|
||||
import { isFilterEqual } from "applesauce-core/helpers";
|
||||
|
||||
import ControlledObservable from "./controlled-observable";
|
||||
import PersistentSubscription from "./persistent-subscription";
|
||||
import Dataflow01 from "../components/icons/dataflow-01";
|
||||
import processManager from "../services/process-manager";
|
||||
import { localRelay } from "../services/local-relay";
|
||||
import { eventStore } from "../services/event-store";
|
||||
import Process from "./process";
|
||||
|
||||
/** @deprecated use MultiSubscription from applesauce-net */
|
||||
export default class MultiSubscription {
|
||||
static OPEN = "open";
|
||||
static CLOSED = "closed";
|
||||
|
||||
id: string;
|
||||
pool: IConnectionPool;
|
||||
name: string;
|
||||
process: Process;
|
||||
filters: Filter[] = [];
|
||||
|
||||
relays = new Set<AbstractRelay>();
|
||||
subscriptions = new Map<AbstractRelay, PersistentSubscription>();
|
||||
|
||||
useCache = true;
|
||||
cacheSubscription: PersistentSubscription | null = null;
|
||||
onCacheEvent = new ControlledObservable<NostrEvent>();
|
||||
|
||||
state = MultiSubscription.CLOSED;
|
||||
onEvent = new ControlledObservable<NostrEvent>();
|
||||
seenEvents = new Set<string>();
|
||||
|
||||
constructor(pool: IConnectionPool, name: string) {
|
||||
this.id = nanoid(8);
|
||||
this.pool = pool;
|
||||
this.name = name;
|
||||
this.process = new Process("MultiSubscription", this);
|
||||
this.process.name = this.name;
|
||||
this.process.icon = Dataflow01;
|
||||
|
||||
processManager.registerProcess(this.process);
|
||||
}
|
||||
private handleEvent(event: NostrEvent, fromCache = false) {
|
||||
if (this.seenEvents.has(event.id)) return;
|
||||
|
||||
if (fromCache) this.onCacheEvent.next(event);
|
||||
else this.onEvent.next(event);
|
||||
|
||||
this.seenEvents.add(event.id);
|
||||
}
|
||||
|
||||
setFilters(filters: Filter[]) {
|
||||
if (isFilterEqual(this.filters, filters)) return;
|
||||
this.filters = filters;
|
||||
this.updateSubscriptions();
|
||||
}
|
||||
|
||||
setRelays(relays: Iterable<string | URL | AbstractRelay>) {
|
||||
const newRelays = Array.from(relays).map((relay) => {
|
||||
if (typeof relay === "string" || relay instanceof URL) return this.pool.getConnection(relay);
|
||||
return relay;
|
||||
});
|
||||
|
||||
// remove relays
|
||||
for (const relay of this.relays) {
|
||||
if (!newRelays.includes(relay)) {
|
||||
this.relays.delete(relay);
|
||||
const sub = this.subscriptions.get(relay);
|
||||
if (sub) {
|
||||
sub.destroy();
|
||||
this.subscriptions.delete(relay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add relays
|
||||
for (const relay of newRelays) {
|
||||
this.relays.add(relay);
|
||||
}
|
||||
|
||||
this.updateSubscriptions();
|
||||
}
|
||||
|
||||
private updateSubscriptions() {
|
||||
// close all subscriptions if not open
|
||||
if (this.state !== MultiSubscription.OPEN) {
|
||||
for (const [relay, subscription] of this.subscriptions) subscription.close();
|
||||
this.cacheSubscription?.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// else open and update subscriptions
|
||||
for (const relay of this.relays) {
|
||||
let subscription = this.subscriptions.get(relay);
|
||||
if (!subscription || !isFilterEqual(subscription.filters, this.filters) || subscription.closed) {
|
||||
if (!subscription) {
|
||||
subscription = new PersistentSubscription(relay, {
|
||||
onevent: (event) => this.handleEvent(eventStore.add(event, relay.url)),
|
||||
});
|
||||
|
||||
this.process.addChild(subscription.process);
|
||||
this.subscriptions.set(relay, subscription);
|
||||
}
|
||||
|
||||
if (subscription) {
|
||||
subscription.filters = this.filters;
|
||||
subscription.update().catch((err) => {
|
||||
// eat error
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.useCache) {
|
||||
// create cache sub if it does not exist
|
||||
if (!this.cacheSubscription && localRelay) {
|
||||
this.cacheSubscription = new PersistentSubscription(localRelay as AbstractRelay, {
|
||||
onevent: (event) => this.handleEvent(eventStore.add(event, localRelay!.url), true),
|
||||
});
|
||||
this.process.addChild(this.cacheSubscription.process);
|
||||
}
|
||||
|
||||
// update cache sub filters if they changed
|
||||
if (
|
||||
this.cacheSubscription &&
|
||||
(!isFilterEqual(this.cacheSubscription.filters, this.filters) || this.cacheSubscription.closed)
|
||||
) {
|
||||
this.cacheSubscription.filters = this.filters;
|
||||
this.cacheSubscription.update().catch((err) => {
|
||||
// eat error
|
||||
});
|
||||
}
|
||||
} else if (this.cacheSubscription?.closed === false) {
|
||||
this.cacheSubscription.close();
|
||||
}
|
||||
}
|
||||
|
||||
publish(event: NostrEvent) {
|
||||
return Promise.allSettled(
|
||||
Array.from(this.relays).map(async (relay) => {
|
||||
if (!relay.connected) await relay.connect();
|
||||
return await relay.publish(event);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
open() {
|
||||
if (this.state === MultiSubscription.OPEN) return this;
|
||||
|
||||
this.state = MultiSubscription.OPEN;
|
||||
this.updateSubscriptions();
|
||||
this.process.active = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
waitForAllConnection(): Promise<void> {
|
||||
return Promise.allSettled(
|
||||
Array.from(this.relays)
|
||||
.filter((r) => !r.connected)
|
||||
.map((r) => r.connect()),
|
||||
).then((v) => void 0);
|
||||
}
|
||||
close() {
|
||||
if (this.state !== MultiSubscription.OPEN) return this;
|
||||
|
||||
// forget all seen events
|
||||
this.forgetEvents();
|
||||
// unsubscribe from relay messages
|
||||
this.state = MultiSubscription.CLOSED;
|
||||
this.process.active = false;
|
||||
|
||||
// close all
|
||||
this.updateSubscriptions();
|
||||
|
||||
return this;
|
||||
}
|
||||
forgetEvents() {
|
||||
// forget all seen events
|
||||
this.seenEvents.clear();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
for (const [relay, sub] of this.subscriptions) {
|
||||
sub.destroy();
|
||||
}
|
||||
|
||||
this.process.remove();
|
||||
processManager.unregisterProcess(this.process);
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
import { NostrConnectConnectionMethods } from "applesauce-signer";
|
||||
import { Filter, NostrEvent } from "nostr-tools";
|
||||
import relayPoolService from "../services/relay-pool";
|
||||
import { MultiSubscription } from "applesauce-net/subscription";
|
||||
|
||||
import relayPoolService from "../services/relay-pool";
|
||||
|
||||
export function createNostrConnectConnection(): NostrConnectConnectionMethods {
|
||||
const sub = new MultiSubscription(relayPoolService);
|
||||
|
||||
|
@ -10,7 +10,6 @@ import { getPubkeysMentionedInContent } from "../helpers/nostr/post";
|
||||
import { TORRENT_COMMENT_KIND } from "../helpers/nostr/torrents";
|
||||
import { getPubkeysFromList } from "../helpers/nostr/lists";
|
||||
import { eventStore, queryStore } from "../services/event-store";
|
||||
import RelaySet from "./relay-set";
|
||||
|
||||
export const NotificationTypeSymbol = Symbol("notificationType");
|
||||
|
||||
|
@ -9,9 +9,7 @@ import { shareLatestValue } from "applesauce-core/observable";
|
||||
import { MultiSubscription } from "applesauce-net/subscription";
|
||||
|
||||
import { logger } from "../helpers/debug";
|
||||
import { isReplaceable } from "../helpers/nostr/event";
|
||||
import { mergeFilter } from "../helpers/nostr/filter";
|
||||
import { localRelay } from "../services/local-relay";
|
||||
import SuperMap from "./super-map";
|
||||
import ChunkedRequest from "./chunked-request";
|
||||
import relayPoolService from "../services/relay-pool";
|
||||
@ -19,6 +17,7 @@ import Process from "./process";
|
||||
import AlignHorizontalCentre02 from "../components/icons/align-horizontal-centre-02";
|
||||
import processManager from "../services/process-manager";
|
||||
import { eventStore, queryStore } from "../services/event-store";
|
||||
import { getCacheRelay } from "../services/cache-relay";
|
||||
|
||||
const BLOCK_SIZE = 100;
|
||||
|
||||
@ -85,9 +84,6 @@ export default class TimelineLoader {
|
||||
private handleEvent(event: NostrEvent, fromCache = false) {
|
||||
event = eventStore.add(event);
|
||||
|
||||
// publish to local relay
|
||||
if (!fromCache && this.useCache && localRelay && !this.seenInCache.has(event.id)) localRelay.publish(event);
|
||||
|
||||
if (fromCache) this.seenInCache.add(event.id);
|
||||
}
|
||||
private handleChunkFinished() {
|
||||
@ -137,8 +133,9 @@ export default class TimelineLoader {
|
||||
|
||||
// recreate cache chunk loader
|
||||
if (this.cacheLoader) this.disconnectFromChunkLoader(this.cacheLoader);
|
||||
if (localRelay && this.useCache) {
|
||||
this.cacheLoader = new ChunkedRequest(localRelay, this.filters, this.log.extend("cache-relay"));
|
||||
const cacheRelay = getCacheRelay();
|
||||
if (cacheRelay && this.useCache) {
|
||||
this.cacheLoader = new ChunkedRequest(cacheRelay, this.filters, this.log.extend("cache-relay"));
|
||||
this.connectToChunkLoader(this.cacheLoader);
|
||||
}
|
||||
|
||||
|
6
src/hooks/use-cache-relay.ts
Normal file
6
src/hooks/use-cache-relay.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { useObservable } from "applesauce-react/hooks";
|
||||
import { cacheRelay$ } from "../services/cache-relay";
|
||||
|
||||
export default function useCacheRelay() {
|
||||
return useObservable(cacheRelay$);
|
||||
}
|
@ -11,7 +11,7 @@ import { DraftNostrEvent } from "../../types/nostr-event";
|
||||
import clientRelaysService from "../../services/client-relays";
|
||||
import RelaySet from "../../classes/relay-set";
|
||||
import { getAllRelayHints } from "../../helpers/nostr/event";
|
||||
import { localRelay } from "../../services/local-relay";
|
||||
import { getCacheRelay } from "../../services/cache-relay";
|
||||
import deleteEventService from "../../services/delete-events";
|
||||
import { eventStore } from "../../services/event-store";
|
||||
import useCurrentAccount from "../../hooks/use-current-account";
|
||||
@ -139,7 +139,8 @@ export default function PublishProvider({ children }: PropsWithChildren) {
|
||||
setLog((arr) => arr.concat(entry));
|
||||
|
||||
// send it to the local relay
|
||||
if (localRelay) localRelay.publish(signed);
|
||||
const cacheRelay = getCacheRelay();
|
||||
if (cacheRelay) cacheRelay.publish(signed);
|
||||
|
||||
// pass it to other services
|
||||
eventStore.add(signed);
|
||||
|
@ -1,7 +1,8 @@
|
||||
import dayjs from "dayjs";
|
||||
import { BehaviorSubject, pairwise } from "rxjs";
|
||||
import { CacheRelay, openDB } from "nostr-idb";
|
||||
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||
import { fakeVerifyEvent, isFromCache } from "applesauce-core/helpers";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import { logger } from "../helpers/debug";
|
||||
import { safeRelayUrl } from "../helpers/relay";
|
||||
@ -10,16 +11,6 @@ import MemoryRelay from "../classes/memory-relay";
|
||||
import localSettings from "./local-settings";
|
||||
import { eventStore } from "./event-store";
|
||||
|
||||
// save the local relay from query params to localStorage
|
||||
const params = new URLSearchParams(location.search);
|
||||
const paramRelay = params.get("localRelay");
|
||||
// save the cache relay to localStorage
|
||||
if (paramRelay) {
|
||||
localStorage.setItem("localRelay", paramRelay);
|
||||
params.delete("localRelay");
|
||||
if (params.size === 0) location.search = params.toString();
|
||||
}
|
||||
|
||||
export const NOSTR_RELAY_TRAY_URL = "ws://localhost:4869/";
|
||||
export async function checkNostrRelayTray() {
|
||||
return new Promise((res) => {
|
||||
@ -37,28 +28,27 @@ export async function checkNostrRelayTray() {
|
||||
});
|
||||
}
|
||||
|
||||
const log = logger.extend(`LocalRelay`);
|
||||
const log = logger.extend(`cache-relay`);
|
||||
export const localDatabase = await openDB();
|
||||
|
||||
// Setup relay
|
||||
function createInternalRelay() {
|
||||
return new CacheRelay(localDatabase, { maxEvents: localSettings.idbMaxEvents.value });
|
||||
}
|
||||
async function createRelay() {
|
||||
const localRelayURL = localStorage.getItem("localRelay");
|
||||
|
||||
if (localRelayURL) {
|
||||
if (localRelayURL === ":none:") {
|
||||
return null;
|
||||
}
|
||||
if (localRelayURL === ":memory:") {
|
||||
// create a cache relay instance
|
||||
async function createRelay(url: string) {
|
||||
if (url) {
|
||||
if (url === ":none:") return null;
|
||||
|
||||
if (url === ":memory:") {
|
||||
return new MemoryRelay();
|
||||
} else if (localRelayURL === "nostr-idb://wasm-worker" && WasmRelay.SUPPORTED) {
|
||||
} else if (url === "nostr-idb://wasm-worker" && WasmRelay.SUPPORTED) {
|
||||
return new WasmRelay();
|
||||
} else if (localRelayURL.startsWith("nostr-idb://")) {
|
||||
} else if (url.startsWith("nostr-idb://")) {
|
||||
return createInternalRelay();
|
||||
} else if (safeRelayUrl(localRelayURL)) {
|
||||
return new AbstractRelay(safeRelayUrl(localRelayURL)!, { verifyEvent: fakeVerifyEvent });
|
||||
} else if (safeRelayUrl(url)) {
|
||||
return new AbstractRelay(safeRelayUrl(url)!, { verifyEvent: fakeVerifyEvent });
|
||||
}
|
||||
} else if (window.CACHE_RELAY_ENABLED) {
|
||||
const protocol = location.protocol === "https:" ? "wss:" : "ws:";
|
||||
@ -66,16 +56,18 @@ async function createRelay() {
|
||||
verifyEvent: fakeVerifyEvent,
|
||||
});
|
||||
}
|
||||
|
||||
return createInternalRelay();
|
||||
}
|
||||
|
||||
async function connectRelay() {
|
||||
const relay = await createRelay();
|
||||
// create and connect to the cache relay
|
||||
async function connectRelay(url: string) {
|
||||
const relay = await createRelay(url);
|
||||
if (!relay) return relay;
|
||||
|
||||
try {
|
||||
await relay.connect();
|
||||
log("Connected");
|
||||
log(`Connected to ${relay.url}`);
|
||||
|
||||
if (relay instanceof AbstractRelay) {
|
||||
// set the base timeout to 2 second
|
||||
@ -89,34 +81,67 @@ async function connectRelay() {
|
||||
}
|
||||
}
|
||||
|
||||
export const localRelay = await connectRelay();
|
||||
export const cacheRelay$ = new BehaviorSubject<
|
||||
AbstractRelay | CacheRelay | WasmRelay | AbstractRelay | MemoryRelay | null
|
||||
>(null);
|
||||
|
||||
// keep the relay connection alive
|
||||
setInterval(() => {
|
||||
if (localRelay && !localRelay.connected) localRelay.connect().then(() => log("Reconnected"));
|
||||
}, 1000 * 5);
|
||||
// create a new cache relay instance when the url changes
|
||||
localSettings.cacheRelayURL.subscribe(async (url) => {
|
||||
if (cacheRelay$.value && cacheRelay$.value.url === url) return;
|
||||
|
||||
const relay = await connectRelay(url);
|
||||
cacheRelay$.next(relay);
|
||||
});
|
||||
|
||||
// keep the relay connected
|
||||
cacheRelay$.subscribe((relay) => {
|
||||
if (!relay) return;
|
||||
|
||||
const i = setInterval(() => {
|
||||
if (!relay.connected) relay.connect();
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(i);
|
||||
});
|
||||
|
||||
// disconnect from old cache relays
|
||||
cacheRelay$.pipe(pairwise()).subscribe(([prev, current]) => {
|
||||
if (prev) {
|
||||
log(`Disconnecting from ${prev.url}`);
|
||||
prev.close();
|
||||
}
|
||||
});
|
||||
|
||||
/** set the cache relay URL and waits for it to connect */
|
||||
export async function setCacheRelayURL(url: string) {
|
||||
return new Promise<void>((res) => {
|
||||
const sub = cacheRelay$.subscribe(() => {
|
||||
res();
|
||||
sub.unsubscribe();
|
||||
});
|
||||
localSettings.cacheRelayURL.next(url);
|
||||
});
|
||||
}
|
||||
|
||||
export function getCacheRelay() {
|
||||
return cacheRelay$.value;
|
||||
}
|
||||
|
||||
// every minute, prune the database
|
||||
setInterval(() => {
|
||||
if (localRelay instanceof WasmRelay) {
|
||||
const relay = getCacheRelay();
|
||||
|
||||
if (relay instanceof WasmRelay) {
|
||||
const days = localSettings.wasmPersistForDays.value;
|
||||
if (days) {
|
||||
log(`Removing all events older than ${days} days in WASM relay`);
|
||||
localRelay.worker?.delete(["REQ", "prune", { until: dayjs().subtract(days, "days").unix() }]);
|
||||
relay.worker?.delete(["REQ", "prune", { until: dayjs().subtract(days, "days").unix() }]);
|
||||
}
|
||||
}
|
||||
}, 60_000);
|
||||
|
||||
// watch for new events and send them to the cache relay
|
||||
if (localRelay) {
|
||||
eventStore.database.inserted.subscribe((event) => {
|
||||
if (!isFromCache(event)) localRelay.publish(event);
|
||||
});
|
||||
}
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
//@ts-expect-error debug
|
||||
window.localDatabase = localDatabase;
|
||||
//@ts-expect-error debug
|
||||
window.localRelay = localRelay;
|
||||
}
|
||||
eventStore.database.inserted.subscribe((event) => {
|
||||
const relay = getCacheRelay();
|
||||
if (relay && !isFromCache(event)) relay.publish(event);
|
||||
});
|
@ -10,9 +10,9 @@ import { logger } from "../helpers/debug";
|
||||
import { eventStore } from "./event-store";
|
||||
import relayPoolService from "./relay-pool";
|
||||
import PersistentSubscription from "../classes/persistent-subscription";
|
||||
import { localRelay } from "./local-relay";
|
||||
import { isFromCache, markFromCache } from "applesauce-core/helpers";
|
||||
import { markFromCache } from "applesauce-core/helpers";
|
||||
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||
import { cacheRelay$, getCacheRelay } from "./cache-relay";
|
||||
|
||||
export type RequestOptions = {
|
||||
/** Always request the event from the relays */
|
||||
@ -110,10 +110,12 @@ class ChannelMetadataService {
|
||||
log = logger.extend("ChannelMetadata");
|
||||
|
||||
constructor() {
|
||||
if (localRelay) {
|
||||
const loader = this.loaders.get(localRelay as AbstractRelay);
|
||||
cacheRelay$.subscribe((cacheRelay) => {
|
||||
if (!cacheRelay) return;
|
||||
|
||||
const loader = this.loaders.get(cacheRelay as AbstractRelay);
|
||||
loader.isCache = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleEvent(event: NostrEvent) {
|
||||
@ -121,8 +123,6 @@ class ChannelMetadataService {
|
||||
|
||||
const channelId = getChannelPointer(event)?.id;
|
||||
if (!channelId) return;
|
||||
|
||||
if (!isFromCache(event)) localRelay?.publish(event);
|
||||
}
|
||||
|
||||
private requestChannelMetadataFromRelays(relays: Iterable<string>, channelId: string) {
|
||||
@ -137,8 +137,9 @@ class ChannelMetadataService {
|
||||
requestMetadata(relays: Iterable<string>, channelId: string, opts: RequestOptions = {}) {
|
||||
const loaded = this.loaded.get(channelId);
|
||||
|
||||
if (!loaded && localRelay) {
|
||||
this.loaders.get(localRelay as AbstractRelay).requestMetadata(channelId);
|
||||
const cacheRelay = getCacheRelay();
|
||||
if (!loaded && cacheRelay) {
|
||||
this.loaders.get(cacheRelay as AbstractRelay).requestMetadata(channelId);
|
||||
}
|
||||
|
||||
if (opts?.alwaysRequest || (!loaded && opts.ignoreCache)) {
|
||||
|
@ -3,7 +3,7 @@ import { clearDB, deleteDB as nostrIDBDelete } from "nostr-idb";
|
||||
|
||||
import { SchemaV1, SchemaV10, SchemaV2, SchemaV3, SchemaV4, SchemaV5, SchemaV6, SchemaV7, SchemaV9 } from "./schema";
|
||||
import { logger } from "../../helpers/debug";
|
||||
import { localDatabase } from "../local-relay";
|
||||
import { localDatabase } from "../cache-relay";
|
||||
|
||||
const log = logger.extend("Database");
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import accountService from "./account";
|
||||
import channelMetadataService from "./channel-metadata";
|
||||
import { eventStore, queryStore } from "./event-store";
|
||||
import { localRelay } from "./local-relay";
|
||||
import localSettings from "./local-settings";
|
||||
import readStatusService from "./read-status";
|
||||
import relayInfoService from "./relay-info";
|
||||
@ -29,12 +28,6 @@ const noStrudel = {
|
||||
/** Signing queue */
|
||||
signingService,
|
||||
|
||||
/**
|
||||
* Cache relay interface
|
||||
* @type MemoryRelay|WasmRelay|CacheRelay|Relay|undefined
|
||||
*/
|
||||
cacheRelay: localRelay,
|
||||
|
||||
// other internal services
|
||||
replaceableEventLoader,
|
||||
userSearchDirectory,
|
||||
|
@ -10,9 +10,9 @@ import SuperMap from "../classes/super-map";
|
||||
import BatchIdentifierLoader from "../classes/batch-identifier-loader";
|
||||
import BookOpen01 from "../components/icons/book-open-01";
|
||||
import processManager from "./process-manager";
|
||||
import { localRelay } from "./local-relay";
|
||||
import relayPoolService from "./relay-pool";
|
||||
import { eventStore } from "./event-store";
|
||||
import { getCacheRelay } from "./cache-relay";
|
||||
|
||||
class DictionaryService {
|
||||
log = logger.extend("DictionaryService");
|
||||
@ -68,8 +68,9 @@ class DictionaryService {
|
||||
const subject = this.topics.get(topic);
|
||||
if (subject.value && !alwaysRequest) return subject;
|
||||
|
||||
if (localRelay) {
|
||||
this.loaders.get(localRelay as AbstractRelay).requestEvents(topic);
|
||||
const cacheRelay = getCacheRelay();
|
||||
if (cacheRelay) {
|
||||
this.loaders.get(cacheRelay as AbstractRelay).requestEvents(topic);
|
||||
}
|
||||
|
||||
const relays = relayPoolService.getRelays(urls);
|
||||
@ -83,9 +84,10 @@ class DictionaryService {
|
||||
handleEvent(event: NostrEvent) {
|
||||
event = this.store.add(event);
|
||||
|
||||
const cacheRelay = getCacheRelay();
|
||||
// pretend it came from the local relay
|
||||
// TODO: remove this once DictionaryService uses subscriptions from event store
|
||||
if (localRelay) this.loaders.get(localRelay as AbstractRelay).handleEvent(event);
|
||||
if (cacheRelay) this.loaders.get(cacheRelay as AbstractRelay).handleEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import stringify from "json-stringify-deterministic";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import SuperMap from "../classes/super-map";
|
||||
import { localRelay } from "./local-relay";
|
||||
import { getCacheRelay } from "./cache-relay";
|
||||
|
||||
class EventCountService {
|
||||
subjects = new SuperMap<string, BehaviorSubject<number | undefined>>(
|
||||
@ -20,9 +20,11 @@ class EventCountService {
|
||||
|
||||
if (sub.value === undefined || alwaysRequest) {
|
||||
// try to get a count from the local relay
|
||||
localRelay?.count(Array.isArray(filter) ? filter : [filter], {}).then((count) => {
|
||||
if (Number.isFinite(count)) sub.next(count);
|
||||
});
|
||||
getCacheRelay()
|
||||
?.count(Array.isArray(filter) ? filter : [filter], {})
|
||||
.then((count) => {
|
||||
if (Number.isFinite(count)) sub.next(count);
|
||||
});
|
||||
}
|
||||
|
||||
return sub;
|
||||
|
@ -3,13 +3,13 @@ import _throttle from "lodash.throttle";
|
||||
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||
|
||||
import SuperMap from "../classes/super-map";
|
||||
import { localRelay } from "./local-relay";
|
||||
import relayPoolService from "./relay-pool";
|
||||
import Process from "../classes/process";
|
||||
import { LightningIcon } from "../components/icons";
|
||||
import processManager from "./process-manager";
|
||||
import BatchRelationLoader from "../classes/batch-relation-loader";
|
||||
import { logger } from "../helpers/debug";
|
||||
import { getCacheRelay } from "./cache-relay";
|
||||
|
||||
class EventReactionsService {
|
||||
log = logger.extend("EventReactionsService");
|
||||
@ -33,8 +33,9 @@ class EventReactionsService {
|
||||
requestReactions(uid: string, urls: Iterable<string | URL | AbstractRelay>, alwaysRequest = false) {
|
||||
if (this.loaded.get(uid) && !alwaysRequest) return;
|
||||
|
||||
if (localRelay) {
|
||||
this.loaders.get(localRelay as AbstractRelay).requestEvents(uid);
|
||||
const cacheRelay = getCacheRelay();
|
||||
if (cacheRelay) {
|
||||
this.loaders.get(cacheRelay as AbstractRelay).requestEvents(uid);
|
||||
}
|
||||
|
||||
const relays = relayPoolService.getRelays(urls);
|
||||
|
@ -3,13 +3,13 @@ import _throttle from "lodash.throttle";
|
||||
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||
|
||||
import SuperMap from "../classes/super-map";
|
||||
import { localRelay } from "./local-relay";
|
||||
import relayPoolService from "./relay-pool";
|
||||
import Process from "../classes/process";
|
||||
import { LightningIcon } from "../components/icons";
|
||||
import processManager from "./process-manager";
|
||||
import BatchRelationLoader from "../classes/batch-relation-loader";
|
||||
import { logger } from "../helpers/debug";
|
||||
import { getCacheRelay } from "./cache-relay";
|
||||
|
||||
class EventZapsService {
|
||||
log = logger.extend("EventZapsService");
|
||||
@ -33,8 +33,9 @@ class EventZapsService {
|
||||
requestZaps(uid: string, urls: Iterable<string | URL | AbstractRelay>, alwaysRequest = true) {
|
||||
if (this.loaded.get(uid) && !alwaysRequest) return;
|
||||
|
||||
if (localRelay) {
|
||||
this.loaders.get(localRelay as AbstractRelay).requestEvents(uid);
|
||||
const cacheRelay = getCacheRelay();
|
||||
if (cacheRelay) {
|
||||
this.loaders.get(cacheRelay as AbstractRelay).requestEvents(uid);
|
||||
}
|
||||
|
||||
const relays = relayPoolService.getRelays(urls);
|
||||
|
@ -63,6 +63,9 @@ const deviceId = new LocalStorageEntry("device-id", nanoid());
|
||||
const ntfyTopic = new LocalStorageEntry("ntfy-topic", nanoid());
|
||||
const ntfyServer = new LocalStorageEntry("ntfy-server", "https://ntfy.sh");
|
||||
|
||||
// cache relay
|
||||
const cacheRelayURL = new LocalStorageEntry("cache-relay-url", "");
|
||||
|
||||
// bakery
|
||||
const bakeryURL = new LocalStorageEntry<string>("bakery-url", "");
|
||||
|
||||
@ -84,6 +87,7 @@ const localSettings = {
|
||||
ntfyTopic,
|
||||
ntfyServer,
|
||||
bakeryURL,
|
||||
cacheRelayURL,
|
||||
};
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
|
@ -1,6 +1,4 @@
|
||||
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||
import RelayPool from "../classes/relay-pool";
|
||||
import { localRelay } from "./local-relay";
|
||||
import { offlineMode } from "./offline-mode";
|
||||
|
||||
const relayPoolService = new RelayPool();
|
||||
@ -19,12 +17,6 @@ offlineMode.subscribe((offline) => {
|
||||
}
|
||||
});
|
||||
|
||||
// add local relay
|
||||
if (localRelay instanceof AbstractRelay) {
|
||||
relayPoolService.relays.set(localRelay.url, localRelay);
|
||||
localRelay.onnotice = (notice) => relayPoolService.handleRelayNotice(localRelay as AbstractRelay, notice);
|
||||
}
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
// @ts-expect-error debug
|
||||
window.relayPoolService = relayPoolService;
|
||||
|
@ -1,15 +1,15 @@
|
||||
import _throttle from "lodash.throttle";
|
||||
import { Filter } from "nostr-tools";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
import { BehaviorSubject, mergeMap } from "rxjs";
|
||||
|
||||
import SuperMap from "../classes/super-map";
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
import relayInfoService from "./relay-info";
|
||||
import { localRelay } from "./local-relay";
|
||||
import { MONITOR_STATS_KIND, SELF_REPORTED_KIND, getRelayURL } from "../helpers/nostr/relay-stats";
|
||||
import relayPoolService from "./relay-pool";
|
||||
import { alwaysVerify } from "./verify-event";
|
||||
import { eventStore } from "./event-store";
|
||||
import { getCacheRelay } from "./cache-relay";
|
||||
|
||||
const MONITOR_PUBKEY = "151c17c9d234320cf0f189af7b761f63419fd6c38c6041587a008b7682e4640f";
|
||||
const MONITOR_RELAY = "wss://relay.nostr.watch";
|
||||
@ -24,12 +24,12 @@ class RelayStatsService {
|
||||
|
||||
constructor() {
|
||||
// load all stats from cache and subscribe to future ones
|
||||
localRelay?.subscribe([{ kinds: [SELF_REPORTED_KIND, MONITOR_STATS_KIND] }], {
|
||||
onevent: (e) => this.handleEvent(e, false),
|
||||
getCacheRelay()?.subscribe([{ kinds: [SELF_REPORTED_KIND, MONITOR_STATS_KIND] }], {
|
||||
onevent: (e) => this.handleEvent(e),
|
||||
});
|
||||
}
|
||||
|
||||
handleEvent(event: NostrEvent, cache = true) {
|
||||
handleEvent(event: NostrEvent) {
|
||||
if (!alwaysVerify(event)) return;
|
||||
|
||||
// ignore all events before NIP-66 start date
|
||||
@ -42,15 +42,9 @@ class RelayStatsService {
|
||||
|
||||
const sub = this.monitorStats.get(relay);
|
||||
if (event.kind === SELF_REPORTED_KIND) {
|
||||
if (!sub.value || event.created_at > sub.value.created_at) {
|
||||
sub.next(event);
|
||||
if (cache && localRelay) localRelay.publish(event);
|
||||
}
|
||||
if (!sub.value || event.created_at > sub.value.created_at) sub.next(event);
|
||||
} else if (event.kind === MONITOR_STATS_KIND) {
|
||||
if (!sub.value || event.created_at > sub.value.created_at) {
|
||||
sub.next(event);
|
||||
if (cache && localRelay) localRelay.publish(event);
|
||||
}
|
||||
if (!sub.value || event.created_at > sub.value.created_at) sub.next(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
import { Filter, NostrEvent } from "nostr-tools";
|
||||
import { ReplaceableLoader } from "applesauce-loaders/loaders";
|
||||
|
||||
import { localRelay } from "./local-relay";
|
||||
import { truncateId } from "../helpers/string";
|
||||
import { eventStore } from "./event-store";
|
||||
import rxNostr from "./rx-nostr";
|
||||
import { Observable } from "rxjs";
|
||||
import { COMMON_CONTACT_RELAYS } from "../const";
|
||||
import { isFromCache } from "applesauce-core/helpers";
|
||||
import { getCacheRelay } from "./cache-relay";
|
||||
|
||||
export type RequestOptions = {
|
||||
/** Always request the event from the relays */
|
||||
@ -23,9 +22,10 @@ export function getHumanReadableCoordinate(kind: number, pubkey: string, d?: str
|
||||
// load events from cache relay
|
||||
export function cacheRequest(filters: Filter[]) {
|
||||
return new Observable<NostrEvent>((observer) => {
|
||||
if (!localRelay) return observer.complete();
|
||||
const relay = getCacheRelay();
|
||||
if (!relay) return observer.complete();
|
||||
|
||||
const sub = localRelay.subscribe(filters, {
|
||||
const sub = relay.subscribe(filters, {
|
||||
onevent: (event) => observer.next(event),
|
||||
oneose: () => {
|
||||
sub.close();
|
||||
|
@ -1,9 +1,6 @@
|
||||
import { kinds } from "nostr-tools";
|
||||
import _throttle from "lodash.throttle";
|
||||
import { combineLatest, distinct, filter } from "rxjs";
|
||||
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||
import { USER_BLOSSOM_SERVER_LIST_KIND } from "blossom-client-sdk";
|
||||
import { isFromCache } from "applesauce-core/helpers";
|
||||
|
||||
import { COMMON_CONTACT_RELAYS } from "../const";
|
||||
import { logger } from "../helpers/debug";
|
||||
@ -15,7 +12,6 @@ import { eventStore, queryStore } from "./event-store";
|
||||
import { Account } from "../classes/accounts/account";
|
||||
import { MultiSubscription } from "applesauce-net/subscription";
|
||||
import relayPoolService from "./relay-pool";
|
||||
import { localRelay } from "./local-relay";
|
||||
import { APP_SETTING_IDENTIFIER, APP_SETTINGS_KIND } from "../helpers/app-settings";
|
||||
|
||||
const log = logger.extend("UserEventSync");
|
||||
@ -49,18 +45,11 @@ function downloadEvents(account: Account) {
|
||||
if (mailboxes?.outboxes && mailboxes.outboxes.length > 0) {
|
||||
log(`Loading delete events`);
|
||||
const sub = new MultiSubscription(relayPoolService);
|
||||
sub.setRelays(
|
||||
localRelay
|
||||
? [...mailboxes.outboxes.map((r) => relayPoolService.requestRelay(r)), localRelay as AbstractRelay]
|
||||
: mailboxes.outboxes,
|
||||
);
|
||||
sub.setRelays(mailboxes.outboxes);
|
||||
sub.setFilters([{ kinds: [kinds.EventDeletion], authors: [account.pubkey] }]);
|
||||
|
||||
sub.open();
|
||||
sub.onEvent.subscribe((e) => {
|
||||
eventStore.add(e);
|
||||
if (!isFromCache(e) && localRelay) localRelay.publish(e);
|
||||
});
|
||||
sub.onEvent.subscribe((e) => eventStore.add(e));
|
||||
|
||||
cleanup.push(() => sub.close());
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import WebRtcRelayClient from "../classes/webrtc/webrtc-relay-client";
|
||||
import WebRtcRelayServer from "../classes/webrtc/webrtc-relay-server";
|
||||
import NostrWebRTCPeer from "../classes/webrtc/nostr-webrtc-peer";
|
||||
import verifyEventMethod from "./verify-event";
|
||||
import { localRelay } from "./local-relay";
|
||||
import localSettings from "./local-settings";
|
||||
import { DEFAULT_ICE_SERVERS } from "../const";
|
||||
|
||||
@ -130,7 +129,7 @@ const signer = new SimpleSigner(localSettings.webRtcLocalIdentity.value);
|
||||
const broker = new NostrWebRtcBroker(signer, new SimplePool(), ["wss://nos.lol", "wss://nostrue.com"]);
|
||||
broker.iceServers = DEFAULT_ICE_SERVERS;
|
||||
|
||||
const webRtcRelaysService = new WebRtcRelaysService(broker, localRelay as AbstractRelay | null);
|
||||
const webRtcRelaysService = new WebRtcRelaysService(broker, null);
|
||||
|
||||
webRtcRelaysService.start();
|
||||
|
||||
|
@ -1,15 +1,23 @@
|
||||
import { useState } from "react";
|
||||
import { useAsync } from "react-use";
|
||||
import { Button, Card, CardBody, CardHeader, Heading, Link, Text } from "@chakra-ui/react";
|
||||
|
||||
import { NOSTR_RELAY_TRAY_URL, checkNostrRelayTray, localRelay } from "../../../../services/local-relay";
|
||||
import { NOSTR_RELAY_TRAY_URL, checkNostrRelayTray, setCacheRelayURL } from "../../../../services/cache-relay";
|
||||
import useCacheRelay from "../../../../hooks/use-cache-relay";
|
||||
|
||||
export default function CitrineRelayCard() {
|
||||
const { value: available, loading: checking } = useAsync(checkNostrRelayTray);
|
||||
|
||||
const enabled = localRelay?.url.startsWith(NOSTR_RELAY_TRAY_URL);
|
||||
const enable = () => {
|
||||
localStorage.setItem("localRelay", NOSTR_RELAY_TRAY_URL);
|
||||
location.reload();
|
||||
const cacheRelay = useCacheRelay();
|
||||
const enabled = cacheRelay?.url.startsWith(NOSTR_RELAY_TRAY_URL);
|
||||
|
||||
const [enabling, setEnabling] = useState(false);
|
||||
const enable = async () => {
|
||||
try {
|
||||
setEnabling(true);
|
||||
await setCacheRelayURL(NOSTR_RELAY_TRAY_URL);
|
||||
} catch (error) {}
|
||||
setEnabling(false);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -20,7 +28,14 @@ export default function CitrineRelayCard() {
|
||||
GitHub
|
||||
</Link>
|
||||
{available ? (
|
||||
<Button size="sm" colorScheme="primary" ml="auto" isLoading={checking} onClick={enable} isDisabled={enabled}>
|
||||
<Button
|
||||
size="sm"
|
||||
colorScheme="primary"
|
||||
ml="auto"
|
||||
isLoading={checking || enabling}
|
||||
onClick={enable}
|
||||
isDisabled={enabled}
|
||||
>
|
||||
{enabled ? "Enabled" : "Enable"}
|
||||
</Button>
|
||||
) : (
|
||||
|
@ -17,11 +17,13 @@ export default function EnableWithDelete({
|
||||
enable,
|
||||
enabled,
|
||||
wipe,
|
||||
isLoading,
|
||||
...props
|
||||
}: Omit<ButtonGroupProps, "children"> & {
|
||||
enable: () => void;
|
||||
enabled: boolean;
|
||||
wipe: () => Promise<void>;
|
||||
isLoading?: boolean;
|
||||
}) {
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const wipeDatabase = useCallback(async () => {
|
||||
@ -39,7 +41,7 @@ export default function EnableWithDelete({
|
||||
{enabled ? "Enabled" : "Enable"}
|
||||
</Button>
|
||||
<Menu>
|
||||
<MenuButton as={IconButton} icon={<ChevronDownIcon />} aria-label="More options" />
|
||||
<MenuButton as={IconButton} icon={<ChevronDownIcon />} aria-label="More options" isLoading={isLoading} />
|
||||
<MenuList>
|
||||
<MenuItem icon={<Trash01 />} color="red.500" onClick={wipeDatabase}>
|
||||
Clear Database
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { Button, Card, CardBody, CardHeader, Heading, Text } from "@chakra-ui/react";
|
||||
|
||||
import { localRelay } from "../../../../services/local-relay";
|
||||
import useCacheRelay from "../../../../hooks/use-cache-relay";
|
||||
import localSettings from "../../../../services/local-settings";
|
||||
|
||||
export default function HostedRelayCard() {
|
||||
const enabled = localRelay?.url.includes(location.host + "/local-relay");
|
||||
const cacheRelay = useCacheRelay();
|
||||
const enabled = cacheRelay?.url.includes(location.host + "/local-relay");
|
||||
const enable = () => {
|
||||
localStorage.removeItem("localRelay");
|
||||
location.reload();
|
||||
localSettings.cacheRelayURL.clear();
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -1,15 +1,23 @@
|
||||
import { useState } from "react";
|
||||
import { Button, Card, CardBody, CardFooter, CardHeader, Heading, Text } from "@chakra-ui/react";
|
||||
import { CacheRelay, clearDB } from "nostr-idb";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import { localDatabase, localRelay } from "../../../../services/local-relay";
|
||||
import { localDatabase, setCacheRelayURL } from "../../../../services/cache-relay";
|
||||
import EnableWithDelete from "../components/enable-with-delete";
|
||||
import useCacheRelay from "../../../../hooks/use-cache-relay";
|
||||
|
||||
export default function InternalRelayCard() {
|
||||
const enabled = localRelay instanceof CacheRelay;
|
||||
const enable = () => {
|
||||
localStorage.setItem("localRelay", "nostr-idb://internal");
|
||||
location.reload();
|
||||
const cacheRelay = useCacheRelay();
|
||||
const enabled = cacheRelay instanceof CacheRelay;
|
||||
|
||||
const [enabling, setEnabling] = useState(false);
|
||||
const enable = async () => {
|
||||
try {
|
||||
setEnabling(true);
|
||||
await setCacheRelayURL("nostr-idb://internal");
|
||||
} catch (error) {}
|
||||
setEnabling(false);
|
||||
};
|
||||
|
||||
const wipe = async () => {
|
||||
@ -20,7 +28,7 @@ export default function InternalRelayCard() {
|
||||
<Card borderColor={enabled ? "primary.500" : undefined} variant="outline">
|
||||
<CardHeader p="4" display="flex" gap="2" alignItems="center">
|
||||
<Heading size="md">Browser Cache</Heading>
|
||||
<EnableWithDelete size="sm" ml="auto" enable={enable} enabled={enabled} wipe={wipe} />
|
||||
<EnableWithDelete size="sm" ml="auto" enable={enable} enabled={enabled} wipe={wipe} isLoading={enabling} />
|
||||
</CardHeader>
|
||||
<CardBody p="4" pt="0">
|
||||
<Text mb="2">Use the browsers built-in database to cache events.</Text>
|
||||
|
@ -1,21 +1,29 @@
|
||||
import { useState } from "react";
|
||||
import { Button, Card, CardBody, CardFooter, CardHeader, Heading, Text } from "@chakra-ui/react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import { localRelay } from "../../../../services/local-relay";
|
||||
import MemoryRelay from "../../../../classes/memory-relay";
|
||||
import useCacheRelay from "../../../../hooks/use-cache-relay";
|
||||
import { setCacheRelayURL } from "../../../../services/cache-relay";
|
||||
|
||||
export default function MemoryRelayCard() {
|
||||
const enabled = localRelay instanceof MemoryRelay;
|
||||
const enable = () => {
|
||||
localStorage.setItem("localRelay", ":memory:");
|
||||
location.reload();
|
||||
const cacheRelay = useCacheRelay();
|
||||
const enabled = cacheRelay instanceof MemoryRelay;
|
||||
|
||||
const [enabling, setEnabling] = useState(false);
|
||||
const enable = async () => {
|
||||
try {
|
||||
setEnabling(true);
|
||||
await setCacheRelayURL(":memory:");
|
||||
} catch (error) {}
|
||||
setEnabling(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card borderColor={enabled ? "primary.500" : undefined} variant="outline">
|
||||
<CardHeader p="4" display="flex" gap="2" alignItems="center">
|
||||
<Heading size="md">In-memory Cache</Heading>
|
||||
<Button size="sm" colorScheme="primary" ml="auto" onClick={enable} isDisabled={enabled}>
|
||||
<Button size="sm" colorScheme="primary" ml="auto" onClick={enable} isDisabled={enabled} isLoading={enabling}>
|
||||
{enabled ? "Enabled" : "Enable"}
|
||||
</Button>
|
||||
</CardHeader>
|
||||
|
@ -1,18 +1,27 @@
|
||||
import { Button, Card, CardBody, CardHeader, Heading, Text } from "@chakra-ui/react";
|
||||
import { localRelay } from "../../../../services/local-relay";
|
||||
import useCacheRelay from "../../../../hooks/use-cache-relay";
|
||||
import localSettings from "../../../../services/local-settings";
|
||||
import { useState } from "react";
|
||||
import { setCacheRelayURL } from "../../../../services/cache-relay";
|
||||
|
||||
export default function NoRelayCard() {
|
||||
const enabled = localRelay === null;
|
||||
const enable = () => {
|
||||
localStorage.setItem("localRelay", ":none:");
|
||||
location.reload();
|
||||
const cacheRelay = useCacheRelay();
|
||||
const enabled = cacheRelay === null;
|
||||
|
||||
const [enabling, setEnabling] = useState(false);
|
||||
const enable = async () => {
|
||||
try {
|
||||
setEnabling(true);
|
||||
await setCacheRelayURL(":none:");
|
||||
} catch (error) {}
|
||||
setEnabling(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card borderColor={enabled ? "primary.500" : undefined} variant="outline">
|
||||
<CardHeader p="4" display="flex" gap="2" alignItems="center">
|
||||
<Heading size="md">No Cache</Heading>
|
||||
<Button size="sm" colorScheme="primary" ml="auto" onClick={enable} isDisabled={enabled}>
|
||||
<Button size="sm" colorScheme="primary" ml="auto" onClick={enable} isDisabled={enabled} isLoading={enabling}>
|
||||
{enabled ? "Enabled" : "Enable"}
|
||||
</Button>
|
||||
</CardHeader>
|
||||
|
@ -1,15 +1,23 @@
|
||||
import { useAsync } from "react-use";
|
||||
import { Button, Card, CardBody, CardHeader, Heading, Link, Text } from "@chakra-ui/react";
|
||||
|
||||
import { NOSTR_RELAY_TRAY_URL, checkNostrRelayTray, localRelay } from "../../../../services/local-relay";
|
||||
import { NOSTR_RELAY_TRAY_URL, checkNostrRelayTray, setCacheRelayURL } from "../../../../services/cache-relay";
|
||||
import useCacheRelay from "../../../../hooks/use-cache-relay";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function NostrRelayTrayCard() {
|
||||
const cacheRelay = useCacheRelay();
|
||||
const { value: available, loading: checking } = useAsync(checkNostrRelayTray);
|
||||
|
||||
const enabled = localRelay?.url.startsWith(NOSTR_RELAY_TRAY_URL);
|
||||
const enable = () => {
|
||||
localStorage.setItem("localRelay", NOSTR_RELAY_TRAY_URL);
|
||||
location.reload();
|
||||
const enabled = cacheRelay?.url.startsWith(NOSTR_RELAY_TRAY_URL);
|
||||
|
||||
const [enabling, setEnabling] = useState(false);
|
||||
const enable = async () => {
|
||||
try {
|
||||
setEnabling(true);
|
||||
await setCacheRelayURL(NOSTR_RELAY_TRAY_URL);
|
||||
} catch (error) {}
|
||||
setEnabling(false);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -20,7 +28,14 @@ export default function NostrRelayTrayCard() {
|
||||
GitHub
|
||||
</Link>
|
||||
{available || enabled ? (
|
||||
<Button size="sm" colorScheme="primary" ml="auto" isLoading={checking} onClick={enable} isDisabled={enabled}>
|
||||
<Button
|
||||
size="sm"
|
||||
colorScheme="primary"
|
||||
ml="auto"
|
||||
isLoading={checking || enabling}
|
||||
onClick={enable}
|
||||
isDisabled={enabled}
|
||||
>
|
||||
{enabled ? "Enabled" : "Enable"}
|
||||
</Button>
|
||||
) : (
|
||||
|
@ -1,20 +1,27 @@
|
||||
import { useState } from "react";
|
||||
import { Button, Card, CardBody, CardFooter, CardHeader, Heading, Link, Text } from "@chakra-ui/react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import { localRelay } from "../../../../services/local-relay";
|
||||
import WasmRelay from "../../../../services/wasm-relay";
|
||||
import EnableWithDelete from "./enable-with-delete";
|
||||
import useCacheRelay from "../../../../hooks/use-cache-relay";
|
||||
import { setCacheRelayURL } from "../../../../services/cache-relay";
|
||||
|
||||
export default function WasmRelayCard() {
|
||||
const enabled = localRelay instanceof WasmRelay;
|
||||
const enable = () => {
|
||||
localStorage.setItem("localRelay", "nostr-idb://wasm-worker");
|
||||
location.reload();
|
||||
const cacheRelay = useCacheRelay();
|
||||
const enabled = cacheRelay instanceof WasmRelay;
|
||||
const [enabling, setEnabling] = useState(false);
|
||||
const enable = async () => {
|
||||
try {
|
||||
setEnabling(true);
|
||||
await setCacheRelayURL("nostr-idb://wasm-worker");
|
||||
} catch (error) {}
|
||||
setEnabling(false);
|
||||
};
|
||||
|
||||
const wipe = async () => {
|
||||
if (localRelay instanceof WasmRelay) {
|
||||
await localRelay.wipe();
|
||||
if (cacheRelay instanceof WasmRelay) {
|
||||
await cacheRelay.wipe();
|
||||
} else {
|
||||
// import and delete database
|
||||
console.log("Importing worker to wipe database");
|
||||
@ -27,7 +34,7 @@ export default function WasmRelayCard() {
|
||||
<Card borderColor={enabled ? "primary.500" : undefined} variant="outline">
|
||||
<CardHeader p="4" display="flex" gap="2" alignItems="center">
|
||||
<Heading size="md">Internal SQLite Cache</Heading>
|
||||
<EnableWithDelete size="sm" ml="auto" enable={enable} enabled={enabled} wipe={wipe} />
|
||||
<EnableWithDelete size="sm" ml="auto" enable={enable} enabled={enabled} wipe={wipe} isLoading={enabling} />
|
||||
</CardHeader>
|
||||
<CardBody p="4" pt="0">
|
||||
<Text mb="2">
|
||||
|
13
src/views/relays/cache/database/index.tsx
vendored
13
src/views/relays/cache/database/index.tsx
vendored
@ -1,19 +1,20 @@
|
||||
import { lazy } from "react";
|
||||
import { Flex, Heading, Link, Text } from "@chakra-ui/react";
|
||||
import { Link, Text } from "@chakra-ui/react";
|
||||
import { CacheRelay } from "nostr-idb";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import BackButton from "../../../../components/router/back-button";
|
||||
import { localRelay } from "../../../../services/local-relay";
|
||||
import WasmRelay from "../../../../services/wasm-relay";
|
||||
import MemoryRelay from "../../../../classes/memory-relay";
|
||||
import SimpleView from "../../../../components/layout/presets/simple-view";
|
||||
import useCacheRelay from "../../../../hooks/use-cache-relay";
|
||||
|
||||
const MemoryDatabasePage = lazy(() => import("./memory"));
|
||||
const WasmDatabasePage = lazy(() => import("./wasm"));
|
||||
const InternalDatabasePage = lazy(() => import("./internal"));
|
||||
|
||||
export default function DatabaseView() {
|
||||
const cacheRelay = useCacheRelay();
|
||||
|
||||
let content = (
|
||||
<Text>
|
||||
noStrudel does not have access to the selected cache relays database{" "}
|
||||
@ -23,9 +24,9 @@ export default function DatabaseView() {
|
||||
</Text>
|
||||
);
|
||||
|
||||
if (localRelay instanceof WasmRelay) content = <WasmDatabasePage />;
|
||||
else if (localRelay instanceof CacheRelay) content = <InternalDatabasePage />;
|
||||
else if (localRelay instanceof MemoryRelay) content = <MemoryDatabasePage />;
|
||||
if (cacheRelay instanceof WasmRelay) content = <WasmDatabasePage />;
|
||||
else if (cacheRelay instanceof CacheRelay) content = <InternalDatabasePage />;
|
||||
else if (cacheRelay instanceof MemoryRelay) content = <MemoryDatabasePage />;
|
||||
|
||||
return <SimpleView title="Event Cache">{content}</SimpleView>;
|
||||
}
|
||||
|
2
src/views/relays/cache/database/internal.tsx
vendored
2
src/views/relays/cache/database/internal.tsx
vendored
@ -20,7 +20,7 @@ import { useAsync } from "react-use";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
import { useObservable } from "applesauce-react/hooks";
|
||||
|
||||
import { localDatabase } from "../../../../services/local-relay";
|
||||
import { localDatabase } from "../../../../services/cache-relay";
|
||||
import EventKindsPieChart from "../../../../components/charts/event-kinds-pie-chart";
|
||||
import EventKindsTable from "../../../../components/charts/event-kinds-table";
|
||||
import ImportEventsButton from "./components/import-events-button";
|
||||
|
38
src/views/relays/cache/database/memory.tsx
vendored
38
src/views/relays/cache/database/memory.tsx
vendored
@ -1,8 +1,8 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { ButtonGroup, Card, Flex, Heading, Text } from "@chakra-ui/react";
|
||||
import { useObservable } from "applesauce-react/hooks";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
|
||||
import { localRelay } from "../../../../services/local-relay";
|
||||
import EventKindsPieChart from "../../../../components/charts/event-kinds-pie-chart";
|
||||
import EventKindsTable from "../../../../components/charts/event-kinds-table";
|
||||
import ImportEventsButton from "./components/import-events-button";
|
||||
@ -10,37 +10,39 @@ import ExportEventsButton from "./components/export-events-button";
|
||||
import MemoryRelay from "../../../../classes/memory-relay";
|
||||
import { getSortedKinds } from "../../../../helpers/nostr/event";
|
||||
import useForceUpdate from "../../../../hooks/use-force-update";
|
||||
|
||||
async function importEvents(events: NostrEvent[]) {
|
||||
for (const event of events) {
|
||||
localRelay?.publish(event);
|
||||
}
|
||||
}
|
||||
async function exportEvents() {
|
||||
if (localRelay instanceof MemoryRelay) {
|
||||
return Array.from(localRelay.store.database.iterateTime(0, Infinity));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
import { cacheRelay$ } from "../../../../services/cache-relay";
|
||||
|
||||
export default function MemoryDatabasePage() {
|
||||
const cacheRelay = useObservable(cacheRelay$);
|
||||
const update = useForceUpdate();
|
||||
|
||||
useEffect(() => {
|
||||
if (localRelay instanceof MemoryRelay) {
|
||||
const sub = localRelay.store.database.inserted.subscribe(update);
|
||||
if (cacheRelay instanceof MemoryRelay) {
|
||||
const sub = cacheRelay.store.database.inserted.subscribe(update);
|
||||
return () => sub.unsubscribe();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const importEvents = async (events: NostrEvent[]) => {
|
||||
for (const event of events) {
|
||||
cacheRelay?.publish(event);
|
||||
}
|
||||
};
|
||||
const exportEvents = async () => {
|
||||
if (cacheRelay instanceof MemoryRelay) {
|
||||
return Array.from(cacheRelay.store.database.iterateTime(0, Infinity));
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const count = useMemo(() => {
|
||||
if (localRelay instanceof MemoryRelay) return localRelay.store.database.events.size;
|
||||
if (cacheRelay instanceof MemoryRelay) return cacheRelay.store.database.events.size;
|
||||
return 0;
|
||||
}, [update]);
|
||||
|
||||
const kinds = useMemo(() => {
|
||||
if (localRelay instanceof MemoryRelay) {
|
||||
return getSortedKinds(Array.from(localRelay.store.database.iterateTime(0, Infinity)));
|
||||
if (cacheRelay instanceof MemoryRelay) {
|
||||
return getSortedKinds(Array.from(cacheRelay.store.database.iterateTime(0, Infinity)));
|
||||
}
|
||||
return {};
|
||||
}, [update]);
|
||||
|
14
src/views/relays/cache/database/wasm.tsx
vendored
14
src/views/relays/cache/database/wasm.tsx
vendored
@ -17,7 +17,7 @@ import {
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
import { useObservable } from "applesauce-react/hooks";
|
||||
|
||||
import { localRelay } from "../../../../services/local-relay";
|
||||
import { cacheRelay$ } from "../../../../services/cache-relay";
|
||||
import WasmRelay from "../../../../services/wasm-relay";
|
||||
import EventKindsPieChart from "../../../../components/charts/event-kinds-pie-chart";
|
||||
import EventKindsTable from "../../../../components/charts/event-kinds-table";
|
||||
@ -26,9 +26,9 @@ import ExportEventsButton from "./components/export-events-button";
|
||||
import localSettings from "../../../../services/local-settings";
|
||||
|
||||
export default function WasmDatabasePage() {
|
||||
const relay = localRelay;
|
||||
if (!(relay instanceof WasmRelay)) return null;
|
||||
const worker = relay.worker;
|
||||
const cacheRelay = useObservable(cacheRelay$);
|
||||
if (!(cacheRelay instanceof WasmRelay)) return null;
|
||||
const worker = cacheRelay.worker;
|
||||
if (!worker) return null;
|
||||
|
||||
const [summary, setSummary] = useState<Record<string, number>>();
|
||||
@ -82,13 +82,13 @@ export default function WasmDatabasePage() {
|
||||
const deleteDatabase = useCallback(async () => {
|
||||
try {
|
||||
setDeleting(true);
|
||||
if (localRelay instanceof WasmRelay) {
|
||||
await localRelay.wipe();
|
||||
if (cacheRelay instanceof WasmRelay) {
|
||||
await cacheRelay.wipe();
|
||||
location.reload();
|
||||
}
|
||||
} catch (error) {}
|
||||
setDeleting(false);
|
||||
}, []);
|
||||
}, [cacheRelay]);
|
||||
|
||||
useEffect(() => {
|
||||
refresh();
|
||||
|
6
src/views/relays/cache/index.tsx
vendored
6
src/views/relays/cache/index.tsx
vendored
@ -1,7 +1,5 @@
|
||||
import { Box, Button, Divider, Flex, Heading, Text, useDisclosure } from "@chakra-ui/react";
|
||||
|
||||
import BackButton from "../../../components/router/back-button";
|
||||
import { localRelay } from "../../../services/local-relay";
|
||||
import { ChevronDownIcon, ChevronUpIcon } from "../../../components/icons";
|
||||
import WasmRelay from "../../../services/wasm-relay";
|
||||
import WasmRelayCard from "./components/wasm-relay-card";
|
||||
@ -12,9 +10,11 @@ import HostedRelayCard from "./components/hosted-relay-card";
|
||||
import MemoryRelayCard from "./components/memory-relay-card";
|
||||
import NoRelayCard from "./components/no-relay-card";
|
||||
import SimpleView from "../../../components/layout/presets/simple-view";
|
||||
import useCacheRelay from "../../../hooks/use-cache-relay";
|
||||
|
||||
export default function CacheRelayView() {
|
||||
const showAdvanced = useDisclosure({ defaultIsOpen: localRelay?.url === ":none:" || localRelay?.url === ":memory:" });
|
||||
const cacheRelay = useCacheRelay();
|
||||
const showAdvanced = useDisclosure({ defaultIsOpen: cacheRelay?.url === ":none:" || cacheRelay?.url === ":memory:" });
|
||||
|
||||
return (
|
||||
<SimpleView title="Cache Relay">
|
||||
|
@ -7,11 +7,12 @@ import UserName from "../../../../components/user/user-name";
|
||||
import WebRtcRelayClient from "../../../../classes/webrtc/webrtc-relay-client";
|
||||
import WebRtcRelayServer from "../../../../classes/webrtc/webrtc-relay-server";
|
||||
import NostrWebRTCPeer from "../../../../classes/webrtc/nostr-webrtc-peer";
|
||||
import { localRelay } from "../../../../services/local-relay";
|
||||
import { cacheRelay$ } from "../../../../services/cache-relay";
|
||||
import useCurrentAccount from "../../../../hooks/use-current-account";
|
||||
import useUserContactList from "../../../../hooks/use-user-contact-list";
|
||||
import { getPubkeysFromList } from "../../../../helpers/nostr/lists";
|
||||
import useForceUpdate from "../../../../hooks/use-force-update";
|
||||
import { useObservable } from "applesauce-react/hooks";
|
||||
|
||||
export default function Connection({
|
||||
call,
|
||||
@ -24,6 +25,7 @@ export default function Connection({
|
||||
client: WebRtcRelayClient;
|
||||
server: WebRtcRelayServer;
|
||||
}) {
|
||||
const cacheRelay = useObservable(cacheRelay$);
|
||||
const update = useForceUpdate();
|
||||
useInterval(update, 1000);
|
||||
// const toggleRead = () => {
|
||||
@ -35,10 +37,10 @@ export default function Connection({
|
||||
|
||||
const [sending, setSending] = useState(false);
|
||||
const sendEvents = async () => {
|
||||
if (!account?.pubkey || !localRelay) return;
|
||||
if (!account?.pubkey || !cacheRelay) return;
|
||||
|
||||
setSending(true);
|
||||
const sub = localRelay.subscribe([{ authors: [account.pubkey] }], {
|
||||
const sub = cacheRelay.subscribe([{ authors: [account.pubkey] }], {
|
||||
onevent: (event) => {
|
||||
client.publish(event);
|
||||
update();
|
||||
@ -52,12 +54,12 @@ export default function Connection({
|
||||
|
||||
const [requesting, setRequesting] = useState(false);
|
||||
const requestEvents = async () => {
|
||||
if (!contacts || !localRelay) return;
|
||||
if (!contacts || !cacheRelay) return;
|
||||
|
||||
setRequesting(true);
|
||||
const sub = client.subscribe([{ authors: getPubkeysFromList(contacts).map((p) => p.pubkey) }], {
|
||||
onevent: (event) => {
|
||||
if (localRelay) localRelay.publish(event);
|
||||
if (cacheRelay) cacheRelay.publish(event);
|
||||
update();
|
||||
},
|
||||
oneose: () => {
|
||||
|
@ -4,22 +4,25 @@ import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||
|
||||
import useSearchRelays from "../../../hooks/use-search-relays";
|
||||
import { useRelayInfo } from "../../../hooks/use-relay-info";
|
||||
import { localRelay } from "../../../services/local-relay";
|
||||
import WasmRelay from "../../../services/wasm-relay";
|
||||
import relayPoolService from "../../../services/relay-pool";
|
||||
import useCacheRelay from "../../../hooks/use-cache-relay";
|
||||
|
||||
export function useSearchRelay(relay?: string) {
|
||||
const cacheRelay = useCacheRelay();
|
||||
if (!relay) return undefined;
|
||||
if (relay === "local") return localRelay as AbstractRelay;
|
||||
if (relay === "local") return cacheRelay as AbstractRelay;
|
||||
else return relayPoolService.requestRelay(relay);
|
||||
}
|
||||
|
||||
const SearchRelayPicker = forwardRef<any, Omit<SelectProps, "children">>(({ value, onChange, ...props }) => {
|
||||
const searchRelays = useSearchRelays();
|
||||
const { info: localRelayInfo } = useRelayInfo(localRelay instanceof AbstractRelay ? localRelay : undefined, true);
|
||||
const cacheRelay = useCacheRelay();
|
||||
|
||||
const { info: cacheRelayInfo } = useRelayInfo(cacheRelay instanceof AbstractRelay ? cacheRelay : undefined, true);
|
||||
const localSearchSupported =
|
||||
localRelay instanceof WasmRelay ||
|
||||
(localRelay instanceof AbstractRelay && !!localRelayInfo?.supported_nips?.includes(50));
|
||||
cacheRelay instanceof WasmRelay ||
|
||||
(cacheRelay instanceof AbstractRelay && !!cacheRelayInfo?.supported_nips?.includes(50));
|
||||
|
||||
return (
|
||||
<Select w="auto" value={value} onChange={onChange} {...props}>
|
||||
|
@ -14,17 +14,18 @@ import QRCodeScannerButton from "../../components/qr-code/qr-code-scanner-button
|
||||
import SearchResults from "./components/search-results";
|
||||
import useSearchRelays from "../../hooks/use-search-relays";
|
||||
import { useRelayInfo } from "../../hooks/use-relay-info";
|
||||
import { localRelay } from "../../services/local-relay";
|
||||
import WasmRelay from "../../services/wasm-relay";
|
||||
import relayPoolService from "../../services/relay-pool";
|
||||
import useCacheRelay from "../../hooks/use-cache-relay";
|
||||
|
||||
export function SearchPage() {
|
||||
const cacheRelay = useCacheRelay();
|
||||
const navigate = useNavigate();
|
||||
const searchRelays = useSearchRelays();
|
||||
const { info: localRelayInfo } = useRelayInfo(localRelay instanceof AbstractRelay ? localRelay : undefined, true);
|
||||
const { info: cacheRelayInfo } = useRelayInfo(cacheRelay instanceof AbstractRelay ? cacheRelay : undefined, true);
|
||||
const localSearchSupported =
|
||||
localRelay instanceof WasmRelay ||
|
||||
(localRelay instanceof AbstractRelay && !!localRelayInfo?.supported_nips?.includes(50));
|
||||
cacheRelay instanceof WasmRelay ||
|
||||
(cacheRelay instanceof AbstractRelay && !!cacheRelayInfo?.supported_nips?.includes(50));
|
||||
|
||||
const autoFocusSearch = useBreakpointValue({ base: false, lg: true });
|
||||
|
||||
@ -33,21 +34,21 @@ export function SearchPage() {
|
||||
|
||||
const relayURL = params.get("relay");
|
||||
const searchRelay = useMemo(() => {
|
||||
if (relayURL === "local") return localRelay;
|
||||
if (relayURL === "local") return cacheRelay;
|
||||
else if (relayURL) return relayPoolService.requestRelay(relayURL);
|
||||
else if (localSearchSupported) return localRelay;
|
||||
else if (localSearchSupported) return cacheRelay;
|
||||
else return relayPoolService.requestRelay(searchRelays[0]);
|
||||
}, [relayURL, localSearchSupported, localRelay, searchRelays[0]]);
|
||||
}, [relayURL, localSearchSupported, cacheRelay, searchRelays[0]]);
|
||||
|
||||
const { register, handleSubmit, setValue } = useForm({
|
||||
defaultValues: { query: searchQuery, relay: searchRelay === localRelay ? "local" : searchRelay?.url },
|
||||
defaultValues: { query: searchQuery, relay: searchRelay === cacheRelay ? "local" : searchRelay?.url },
|
||||
mode: "all",
|
||||
});
|
||||
|
||||
// reset the relay when the search relay changes
|
||||
useEffect(
|
||||
() => setValue("relay", searchRelay === localRelay ? "local" : searchRelay?.url),
|
||||
[searchRelay, localRelay],
|
||||
() => setValue("relay", searchRelay === cacheRelay ? "local" : searchRelay?.url),
|
||||
[searchRelay, cacheRelay],
|
||||
);
|
||||
|
||||
const handleSearchText = (text: string) => {
|
||||
|
@ -3,9 +3,6 @@ import {
|
||||
Badge,
|
||||
Box,
|
||||
Flex,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Link,
|
||||
LinkBox,
|
||||
Select,
|
||||
@ -30,7 +27,6 @@ import { combineLatest, map } from "rxjs";
|
||||
import relayPoolService from "../../../services/relay-pool";
|
||||
import { RelayFavicon } from "../../../components/relay-favicon";
|
||||
import HoverLinkOverlay from "../../../components/hover-link-overlay";
|
||||
import { localRelay } from "../../../services/local-relay";
|
||||
import { IconRelayAuthButton, useRelayAuthMethod } from "../../../components/relays/relay-auth-button";
|
||||
import RelayConnectSwitch from "../../../components/relays/relay-connect-switch";
|
||||
import useRouteSearchValue from "../../../hooks/use-route-search-value";
|
||||
@ -40,6 +36,7 @@ import Timestamp from "../../../components/timestamp";
|
||||
import localSettings from "../../../services/local-settings";
|
||||
import useForceUpdate from "../../../hooks/use-force-update";
|
||||
import DefaultAuthModeSelect from "../../../components/settings/default-auth-mode-select";
|
||||
import useCacheRelay from "../../../hooks/use-cache-relay";
|
||||
|
||||
function RelayCard({ relay }: { relay: AbstractRelay }) {
|
||||
return (
|
||||
@ -105,11 +102,12 @@ export default function TaskManagerRelays() {
|
||||
const update = useForceUpdate();
|
||||
useInterval(update, 2000);
|
||||
|
||||
const cacheRelay = useCacheRelay();
|
||||
const { value: tab, setValue: setTab } = useRouteSearchValue("tab", TABS[0]);
|
||||
const tabIndex = TABS.indexOf(tab);
|
||||
|
||||
const relays = Array.from(relayPoolService.relays.values())
|
||||
.filter((r) => r !== localRelay)
|
||||
.filter((r) => r !== cacheRelay)
|
||||
.sort((a, b) => +b.connected - +a.connected || a.url.localeCompare(b.url));
|
||||
|
||||
const observable = useMemo(
|
||||
@ -137,7 +135,7 @@ export default function TaskManagerRelays() {
|
||||
<TabPanels>
|
||||
<TabPanel p="0">
|
||||
<SimpleGrid spacing="2" columns={{ base: 1, md: 2 }} p="2">
|
||||
{localRelay instanceof AbstractRelay && <RelayCard relay={localRelay} />}
|
||||
{cacheRelay instanceof AbstractRelay && <RelayCard relay={cacheRelay} />}
|
||||
{relays.map((relay) => (
|
||||
<RelayCard key={relay.url} relay={relay} />
|
||||
))}
|
||||
|
@ -25,7 +25,6 @@ import { useLocation, useSearchParams } from "react-router-dom";
|
||||
|
||||
import VerticalPageLayout from "../../../components/vertical-page-layout";
|
||||
import BackButton from "../../../components/router/back-button";
|
||||
import { localRelay } from "../../../services/local-relay";
|
||||
import Play from "../../../components/icons/play";
|
||||
import ClockRewind from "../../../components/icons/clock-rewind";
|
||||
import HistoryDrawer from "./history-drawer";
|
||||
@ -39,6 +38,7 @@ import { validateRelayURL } from "../../../helpers/relay";
|
||||
import FilterEditor from "./filter-editor";
|
||||
import { safeJson } from "../../../helpers/parse";
|
||||
import relayPoolService from "../../../services/relay-pool";
|
||||
import useCacheRelay from "../../../hooks/use-cache-relay";
|
||||
|
||||
const EventTimeline = memo(({ events }: { events: NostrEvent[] }) => {
|
||||
return (
|
||||
@ -51,6 +51,7 @@ const EventTimeline = memo(({ events }: { events: NostrEvent[] }) => {
|
||||
});
|
||||
|
||||
export default function EventConsoleView() {
|
||||
const cacheRelay = useCacheRelay();
|
||||
const [params, setParams] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const historyDrawer = useDisclosure();
|
||||
@ -90,8 +91,8 @@ export default function EventConsoleView() {
|
||||
|
||||
if (sub) sub.close();
|
||||
|
||||
if (!localRelay) throw new Error("Local relay disabled");
|
||||
let r = localRelay!;
|
||||
if (!cacheRelay) throw new Error("Local relay disabled");
|
||||
let r = cacheRelay!;
|
||||
if (queryRelay.isOpen) {
|
||||
const url = validateRelayURL(relayURL);
|
||||
if (!relay || relay.url !== url.toString()) {
|
||||
@ -128,7 +129,7 @@ export default function EventConsoleView() {
|
||||
if (e instanceof Error) setError(e.message);
|
||||
}
|
||||
setLoading(false);
|
||||
}, [queryRelay.isOpen, query, relayURL, relay, sub]);
|
||||
}, [queryRelay.isOpen, query, relayURL, relay, sub, cacheRelay]);
|
||||
|
||||
const submitRef = useRef(loadEvents);
|
||||
submitRef.current = loadEvents;
|
||||
|
@ -11,12 +11,14 @@ import useRouteSearchValue from "../../hooks/use-route-search-value";
|
||||
import { subscribeMany } from "../../helpers/relay";
|
||||
import { DEFAULT_SEARCH_RELAYS, WIKI_RELAYS } from "../../const";
|
||||
import { WIKI_PAGE_KIND } from "../../helpers/nostr/wiki";
|
||||
import { localRelay } from "../../services/local-relay";
|
||||
import { cacheRelay$ } from "../../services/cache-relay";
|
||||
import WikiPageResult from "./components/wiki-page-result";
|
||||
import dictionaryService from "../../services/dictionary";
|
||||
import { useWebOfTrust } from "../../providers/global/web-of-trust-provider";
|
||||
import { useObservable } from "applesauce-react/hooks";
|
||||
|
||||
export default function WikiSearchView() {
|
||||
const cacheRelay = useObservable(cacheRelay$);
|
||||
const webOfTrust = useWebOfTrust();
|
||||
const { value: query, setValue: setQuery } = useRouteSearchValue("q");
|
||||
if (!query) return <Navigate to="/wiki" />;
|
||||
@ -46,13 +48,13 @@ export default function WikiSearchView() {
|
||||
oneose: () => remoteSearchSub.close(),
|
||||
});
|
||||
|
||||
if (localRelay) {
|
||||
const localSearchSub: Subscription = localRelay.subscribe([filter], {
|
||||
if (cacheRelay) {
|
||||
const localSearchSub: Subscription = cacheRelay.subscribe([filter], {
|
||||
onevent: handleEvent,
|
||||
oneose: () => localSearchSub.close(),
|
||||
});
|
||||
}
|
||||
}, [query, setResults]);
|
||||
}, [query, setResults, cacheRelay]);
|
||||
|
||||
const sorted = webOfTrust ? webOfTrust.sortByDistanceAndConnections(results, (p) => p.pubkey) : results;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user