diff --git a/apps/desktop/src/routes/relays/components/relayItem.tsx b/apps/desktop/src/routes/relays/components/relayItem.tsx
index 171f404f..72754927 100644
--- a/apps/desktop/src/routes/relays/components/relayItem.tsx
+++ b/apps/desktop/src/routes/relays/components/relayItem.tsx
@@ -1,9 +1,9 @@
-import { useRelaylist } from "@lume/ark";
-import { PlusIcon, ShareIcon } from "@lume/icons";
+import { User, useRelaylist } from "@lume/ark";
+import { PlusIcon, SearchIcon } from "@lume/icons";
import { normalizeRelayUrl } from "nostr-fetch";
import { Link } from "react-router-dom";
-export function RelayItem({ url }: { url: string }) {
+export function RelayItem({ url, users }: { url: string; users?: string[] }) {
const domain = new URL(url).hostname;
const { connectRelay } = useRelaylist();
@@ -18,19 +18,35 @@ export function RelayItem({ url }: { url: string }) {
+ {users ? (
+
+ {users.slice(0, 4).map((item) => (
+
+
+
+
+
+ ))}
+ {users.length > 4 ? (
+
+ +{users.length - 4}
+
+ ) : null}
+
+ ) : null}
-
+
Inspect
diff --git a/apps/desktop/src/routes/relays/components/relayList.tsx b/apps/desktop/src/routes/relays/components/relayList.tsx
index ef84d8cd..9d7c7458 100644
--- a/apps/desktop/src/routes/relays/components/relayList.tsx
+++ b/apps/desktop/src/routes/relays/components/relayList.tsx
@@ -1,4 +1,4 @@
-import { useArk, useRelay } from "@lume/ark";
+import { useArk, useRelaylist } from "@lume/ark";
import { LoaderIcon, PlusIcon, ShareIcon } from "@lume/icons";
import { User } from "@lume/ui";
import { useQuery } from "@tanstack/react-query";
@@ -7,7 +7,7 @@ import { VList } from "virtua";
export function RelayList() {
const ark = useArk();
- const { connectRelay } = useRelay();
+ const { connectRelay } = useRelaylist();
const { status, data } = useQuery({
queryKey: ["relays"],
queryFn: async () => {
diff --git a/apps/desktop/src/routes/relays/components/sidebar.tsx b/apps/desktop/src/routes/relays/components/sidebar.tsx
index 93fd441d..89b49315 100644
--- a/apps/desktop/src/routes/relays/components/sidebar.tsx
+++ b/apps/desktop/src/routes/relays/components/sidebar.tsx
@@ -1,14 +1,15 @@
-import { useArk } from "@lume/ark";
-import { CancelIcon, RefreshIcon } from "@lume/icons";
+import { useArk, useRelaylist } from "@lume/ark";
+import { CancelIcon, LoaderIcon, RefreshIcon } from "@lume/icons";
import { cn } from "@lume/utils";
-import { NDKKind } from "@nostr-dev-kit/ndk";
+import { NDKKind, NDKSubscriptionCacheUsage } from "@nostr-dev-kit/ndk";
import { useQuery } from "@tanstack/react-query";
import { RelayForm } from "./relayForm";
export function RelaySidebar({ className }: { className?: string }) {
const ark = useArk();
- const { status, data, refetch } = useQuery({
+ const { removeRelay } = useRelaylist();
+ const { status, data, isRefetching, refetch } = useQuery({
queryKey: ["relay-personal"],
queryFn: async () => {
const event = await ark.getEventByFilter({
@@ -16,12 +17,15 @@ export function RelaySidebar({ className }: { className?: string }) {
kinds: [NDKKind.RelayList],
authors: [ark.account.pubkey],
},
+ cache: NDKSubscriptionCacheUsage.ONLY_RELAY,
});
-
if (!event) return [];
return event.tags.filter((tag) => tag[0] === "r");
},
refetchOnWindowFocus: false,
+ refetchOnMount: false,
+ refetchOnReconnect: false,
+ staleTime: Infinity,
});
const currentRelays = new Set(
@@ -42,17 +46,19 @@ export function RelaySidebar({ className }: { className?: string }) {
onClick={() => refetch()}
className="inline-flex items-center justify-center w-6 h-6 rounded-md shrink-0 hover:bg-neutral-100 dark:hover:bg-neutral-900"
>
-
+
{status === "pending" ? (
-
Loading...
+
+
+
) : !data.length ? (
-
-
- You not have personal relay list yet
-
+
) : (
data.map((item) => (
@@ -73,7 +79,10 @@ export function RelaySidebar({ className }: { className?: string }) {
)}
- {item[1].replace("wss://", "").replace("ws://", "")}
+ {item[1]
+ .replace("wss://", "")
+ .replace("ws://", "")
+ .replace("/", "")}
@@ -84,9 +93,10 @@ export function RelaySidebar({ className }: { className?: string }) {
) : null}
diff --git a/apps/desktop/src/routes/relays/follows.tsx b/apps/desktop/src/routes/relays/follows.tsx
index afddd214..6b1a31e4 100644
--- a/apps/desktop/src/routes/relays/follows.tsx
+++ b/apps/desktop/src/routes/relays/follows.tsx
@@ -1,18 +1,25 @@
import { useArk } from "@lume/ark";
-import { LoaderIcon, PlusIcon, ShareIcon } from "@lume/icons";
+import { LoaderIcon } from "@lume/icons";
import { useQuery } from "@tanstack/react-query";
import { VList } from "virtua";
import { RelayItem } from "./components/relayItem";
export function RelayFollowsScreen() {
const ark = useArk();
- const { isLoading, data: relays } = useQuery({
+ const {
+ isLoading,
+ isError,
+ data: relays,
+ } = useQuery({
queryKey: ["relay-follows"],
queryFn: async ({ signal }: { signal: AbortSignal }) => {
- return await ark.getAllRelaysFromContacts();
+ const data = await ark.getAllRelaysFromContacts({ signal });
+ if (!data) throw new Error("Failed to get relay list from contacts");
+ return data;
},
refetchOnMount: false,
refetchOnWindowFocus: false,
+ refetchOnReconnect: false,
});
if (isLoading) {
@@ -23,10 +30,18 @@ export function RelayFollowsScreen() {
);
}
+ if (isError || !relays) {
+ return (
+
+ );
+ }
+
return (
- {relays.map((item: string) => (
-
+ {[...relays].map(([key, value]) => (
+
))}
);
diff --git a/apps/desktop/src/routes/relays/global.tsx b/apps/desktop/src/routes/relays/global.tsx
index 249119f9..dba51189 100644
--- a/apps/desktop/src/routes/relays/global.tsx
+++ b/apps/desktop/src/routes/relays/global.tsx
@@ -14,6 +14,7 @@ export function RelayGlobalScreen() {
},
refetchOnMount: false,
refetchOnWindowFocus: false,
+ refetchOnReconnect: false,
});
if (isLoading) {
diff --git a/packages/ark/src/ark.ts b/packages/ark/src/ark.ts
index c092b641..a8173d54 100644
--- a/packages/ark/src/ark.ts
+++ b/packages/ark/src/ark.ts
@@ -254,9 +254,12 @@ export class Ark {
}
}
- public async getEventByFilter({ filter }: { filter: NDKFilter }) {
+ public async getEventByFilter({
+ filter,
+ cache,
+ }: { filter: NDKFilter; cache?: NDKSubscriptionCacheUsage }) {
const event = await this.ndk.fetchEvent(filter, {
- cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST,
+ cacheUsage: cache || NDKSubscriptionCacheUsage.CACHE_FIRST,
});
if (!event) return null;
@@ -352,7 +355,7 @@ export class Ark {
}
}
- public async getAllRelaysFromContacts() {
+ public async getAllRelaysFromContacts({ signal }: { signal: AbortSignal }) {
const fetcher = NostrFetcher.withCustomPool(ndkAdapter(this.ndk));
const connectedRelays = this.ndk.pool
.connectedRelays()
@@ -367,15 +370,21 @@ export class Ark {
},
{ kinds: [NDKKind.RelayList] },
1,
+ { abortSignal: signal },
);
- for await (const { author, events } of relayEvents) {
- if (events[0]) {
- for (const tag of events[0].tags) {
- const users = relayMap.get(tag[1]);
+ console.log(relayEvents);
- if (!users) relayMap.set(tag[1], [author]);
- users.push(author);
+ for await (const { author, events } of relayEvents) {
+ if (events.length) {
+ const relayTags = events[0].tags.filter((item) => item[0] === "r");
+ for (const tag of relayTags) {
+ const item = relayMap.get(tag[1]);
+ if (item?.length) {
+ item.push(author);
+ } else {
+ relayMap.set(tag[1], [author]);
+ }
}
}
}
diff --git a/packages/ark/src/hooks/useRelayList.ts b/packages/ark/src/hooks/useRelayList.ts
index 19817ffc..85753c69 100644
--- a/packages/ark/src/hooks/useRelayList.ts
+++ b/packages/ark/src/hooks/useRelayList.ts
@@ -1,5 +1,6 @@
-import { NDKKind, NDKRelayUrl, NDKTag } from "@nostr-dev-kit/ndk";
+import { NDKKind, NDKTag } from "@nostr-dev-kit/ndk";
import { useMutation, useQueryClient } from "@tanstack/react-query";
+import { normalizeRelayUrl } from "nostr-fetch";
import { useArk } from "./useArk";
export function useRelaylist() {
@@ -8,7 +9,7 @@ export function useRelaylist() {
const connectRelay = useMutation({
mutationFn: async (
- relay: NDKRelayUrl,
+ relay: WebSocket["url"],
purpose?: "read" | "write" | undefined,
) => {
// Cancel any outgoing refetches
@@ -16,11 +17,10 @@ export function useRelaylist() {
queryKey: ["relay-personal"],
});
+ const relayUrl = normalizeRelayUrl(relay);
+
// Snapshot the previous value
- const prevRelays: NDKTag[] = queryClient.getQueryData([
- "relays",
- ark.account.pubkey,
- ]);
+ const prevRelays: NDKTag[] = queryClient.getQueryData(["relay-personal"]);
// create new relay list if not exist
if (!prevRelays) {
@@ -36,13 +36,13 @@ export function useRelaylist() {
await ark.createEvent({
kind: NDKKind.RelayList,
- tags: [...prevRelays, ["r", relay, purpose ?? ""]],
+ tags: [...prevRelays, ["r", relayUrl, purpose ?? ""]],
});
// Optimistically update to the new value
queryClient.setQueryData(["relay-personal"], (prev: NDKTag[]) => [
...prev,
- ["r", relay, purpose ?? ""],
+ ["r", relayUrl, purpose ?? ""],
]);
// Return a context object with the snapshotted value
@@ -56,17 +56,14 @@ export function useRelaylist() {
});
const removeRelay = useMutation({
- mutationFn: async (relay: NDKRelayUrl) => {
+ mutationFn: async (relay: WebSocket["url"]) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({
queryKey: ["relay-personal"],
});
// Snapshot the previous value
- const prevRelays: NDKTag[] = queryClient.getQueryData([
- "relays",
- ark.account.pubkey,
- ]);
+ const prevRelays: NDKTag[] = queryClient.getQueryData(["relay-personal"]);
if (!prevRelays) return;