Skip to content

Commit

Permalink
feat: nip-66 relay status fetching
Browse files Browse the repository at this point in the history
  • Loading branch information
verbiricha committed Jan 1, 2024
1 parent 96404b4 commit 1d47dfe
Show file tree
Hide file tree
Showing 12 changed files with 106 additions and 88 deletions.
2 changes: 1 addition & 1 deletion apps/badges/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const metadata = {
metadataBase: new URL('https://badges.page'),
metadataBase: new URL("https://badges.page"),
title: "Badges",
description: "Create, collect and award badges",
};
Expand Down
33 changes: 0 additions & 33 deletions apps/emojis/app/utils.ts

This file was deleted.

6 changes: 5 additions & 1 deletion apps/emojis/ui/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import { theme } from "./theme";

const cacheAdapter = new NDKCacheAdapterDexie({ dbName: "emojis" });
const ndk = new NDK({
explicitRelayUrls: ["wss://nos.lol", "wss://relay.nostr.band", "wss://frens.nostr1.com"],
explicitRelayUrls: [
"wss://nos.lol",
"wss://relay.nostr.band",
"wss://frens.nostr1.com",
],
outboxRelayUrls: ["wss://purplepag.es"],
enableOutboxModel: true,
cacheAdapter,
Expand Down
6 changes: 3 additions & 3 deletions apps/relays/app/components/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export default function Footer(props: StackProps) {
{...props}
>
<Text color="chakra-subtle-text">
Made with ❤️ by{" "}
<Link variant="brand" href="https://snort.social/verbiricha">
verbiricha
Relay data provided by
<Link variant="brand" href="https://nostr.watch">
nostr.watch
</Link>
</Text>
<Text color="chakra-subtle-text">
Expand Down
13 changes: 7 additions & 6 deletions apps/relays/app/components/relay-link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@ import { useRelays } from "@ngine/core";

import Link from "./link";
import RelayFavicon from "./relay-favicon";
import { encodeRelayURL } from "../utils";
import { humanize, encode } from "@lib/urls";

interface RelayLinkProps {
url: string;
}

export default function RelayLink({ url }: RelayLinkProps) {
const relays = useRelays();
const isInMyRelays = useMemo(() => relays.includes(url), [relays, url]);
const encoded = useMemo(() => `/relay/${encodeRelayURL(url)}`, [url]);
const domain = useMemo(() => {
return url.replace("ws://", "").replace("wss://", "");
}, [url]);
const isInMyRelays = useMemo(
() => relays.map(humanize).includes(humanize(url)),
[relays, url],
);
const encoded = useMemo(() => `/relay/${encode(url)}`, [url]);
const domain = useMemo(() => humanize(url), [url]);
return (
<Link key={url} href={encoded}>
<HStack spacing={2}>
Expand Down
5 changes: 2 additions & 3 deletions apps/relays/app/components/relay-metadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import RelayFavicon from "./relay-favicon";
import RelaySummary from "./relay-summary";
import ToggleRelay from "./toggle-relay";
import { RelayMetadata } from "../hooks/useRelayMetadata";
import { humanize } from "@lib/urls";

export default function Metadata({
url,
Expand All @@ -15,9 +16,7 @@ export default function Metadata({
url: string;
metadata: RelayMetadata;
}) {
const domain = useMemo(() => {
return url.replace("ws://", "").replace("wss://", "");
}, [url]);
const domain = useMemo(() => humanize(url), [url]);

return (
<Stack>
Expand Down
74 changes: 35 additions & 39 deletions apps/relays/app/components/relays.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,58 +3,73 @@
import { useRouter } from "next/navigation";
import { useState, useMemo } from "react";
import {
Alert,
AlertIcon,
Stack,
Button,
Text,
Icon,
Input,
InputGroup,
InputLeftElement,
InputRightElement,
Stat,
StatLabel,
StatNumber,
StatGroup,
} from "@chakra-ui/react";
import { useQuery } from "@tanstack/react-query";
import { useIntl, FormattedMessage } from "react-intl";
import { useRelays } from "@ngine/core";

import RelayLink from "./relay-link";
import RelayIcon from "./relay-icon";
import { encodeRelayURL } from "../utils";
import { humanize, encode } from "@lib/urls";
import useRelayStatus from "@hooks/useRelayStatus";

export default function Relays() {
const { formatMessage } = useIntl();
const router = useRouter();
const [relay, setRelay] = useState("");
const { events, online, offline } = useRelayStatus();
const myRelays = useRelays();
const { data, isFetched, isError } = useQuery({
queryKey: ["relays"],
queryFn: () =>
fetch(`https://api.nostr.watch/v1/online`).then((r) => r.json()),
});
const relaySet = useMemo(() => new Set(myRelays.map(humanize)), [myRelays]);

function relayScore(url: string) {
if (myRelays.includes(url)) {
return 1;
if (relaySet.has(humanize(url))) {
return 42;
}
return 0;
}

const relays = useMemo(() => {
const raw = data
? [...data].sort((a, b) => relayScore(b) - relayScore(a))
: [];
const raw = [...online].sort((a, b) => relayScore(b) - relayScore(a));
return raw
.filter((url) => url.toLowerCase().includes(relay.toLowerCase()))
.slice(0, 21);
}, [relay, data, myRelays]);
}, [online, relay]);

function goToRelay(url: string) {
router.push(`/relay/${encodeRelayURL(url)}`);
router.push(`/relay/${encode(url)}`);
}

return (
<Stack spacing={4} w="100%">
<StatGroup>
{/* @ts-ignore */}
<Stat align="center">
<StatNumber>{events.length}</StatNumber>
<StatLabel>Relays</StatLabel>
</Stat>

{/* @ts-ignore */}
<Stat align="center">
<StatNumber>{online.length}</StatNumber>
<StatLabel>Online</StatLabel>
</Stat>

{/* @ts-ignore */}
<Stat align="center">
<StatNumber>{offline.length}</StatNumber>
<StatLabel>Offline</StatLabel>
</Stat>
</StatGroup>
<InputGroup>
<InputLeftElement
pointerEvents="none"
Expand Down Expand Up @@ -93,28 +108,9 @@ export default function Relays() {
</InputRightElement>
</InputGroup>
<Stack>
{isError && (
<Alert>
<AlertIcon />
<FormattedMessage
id="cant-fetch-relays"
description="Error message shown when can't fetch online relays"
defaultMessage="Could not fetch online relays"
/>
</Alert>
)}
{isFetched &&
relays.map((url: string) => <RelayLink key={url} url={url} />)}
{isFetched && relays.length === 0 && (
<Text color="chakra-subtle-text">
<FormattedMessage
id="no-relays-found"
description="Message shown when no known relay matches the search pattern"
defaultMessage="No relays match the term `{ relay }`"
values={{ relay }}
/>
</Text>
)}
{relays.map((url) => (
<RelayLink key={url} url={url} />
))}
</Stack>
</Stack>
);
Expand Down
2 changes: 1 addition & 1 deletion apps/relays/app/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function relayToTag(r: Relay): Tag {
}

export function tagToRelay(t: Tag): Relay {
const url = t[1].replace(/\/$/, "");
const url = t[1];

if (t[2] === "read") {
return { url, read: true, write: false };
Expand Down
41 changes: 41 additions & 0 deletions apps/relays/hooks/useRelayStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { NDKKind, NDKSubscriptionCacheUsage } from "@nostr-dev-kit/ndk";
import { useEvent, useEvents } from "@ngine/core";

export function useStatus(url: string) {
const event = useEvent(
{
kinds: [30_066 as NDKKind],
"#d": [url],
authors: [
"151c17c9d234320cf0f189af7b761f63419fd6c38c6041587a008b7682e4640f",
],
},
{
cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST,
},
);
return event;
}

export default function useRelayStatus() {
const { events } = useEvents(
{
kinds: [30_066 as NDKKind],
authors: [
"151c17c9d234320cf0f189af7b761f63419fd6c38c6041587a008b7682e4640f",
],
},
{
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL,
},
);
const online = events
.filter((e) => e.tagValue("s") === "online")
.map((e) => e.tagValue("d"))
.filter((s) => s) as string[];
const offline = events
.filter((e) => e.tagValue("s") !== "online")
.map((e) => e.tagValue("d"))
.filter((s) => s) as string[];
return { events, online, offline };
}
8 changes: 8 additions & 0 deletions apps/relays/lib/urls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function encode(url: string): string {
url = url.trim();
return encodeURIComponent(humanize(url));
}

export function humanize(url: string) {
return url.replace("ws://", "").replace("wss://", "").replace(/\/$/, "");
}
2 changes: 2 additions & 0 deletions apps/relays/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"baseUrl": "./",
"paths": {
"@ui/*": ["ui/*"],
"@hooks/*": ["hooks/*"],
"@lib/*": ["lib/*"],
}
},
"include": [
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const relaysAtom = atom<Relay[]>((get) => {
relayList?.tags
.filter((t) => t[0] === "r")
.map((t) => {
const url = t[1].replace(/\/$/, "");
const url = t[1];
const read = t.length === 2 || t[2] === "read";
const write = t.length === 2 || t[2] === "write";
return { url, read, write };
Expand Down

2 comments on commit 1d47dfe

@vercel
Copy link

@vercel vercel bot commented on 1d47dfe Jan 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 1d47dfe Jan 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.