diff --git a/.changeset/slimy-stingrays-behave.md b/.changeset/slimy-stingrays-behave.md
new file mode 100644
index 000000000..343cfd938
--- /dev/null
+++ b/.changeset/slimy-stingrays-behave.md
@@ -0,0 +1,5 @@
+---
+"nostrudel": minor
+---
+
+Show relay recommendations in timeline
diff --git a/src/components/timeline-page/generic-note-timeline/index.tsx b/src/components/timeline-page/generic-note-timeline/index.tsx
index 490316e8c..369ece93c 100644
--- a/src/components/timeline-page/generic-note-timeline/index.tsx
+++ b/src/components/timeline-page/generic-note-timeline/index.tsx
@@ -9,6 +9,8 @@ import { Kind } from "nostr-tools";
import { STREAM_KIND } from "../../../helpers/nostr/stream";
import StreamNote from "./stream-note";
import { ErrorBoundary } from "../../error-boundary";
+import RelayCard from "../../../views/relays/components/relay-card";
+import { safeRelayUrl } from "../../../helpers/url";
const RenderEvent = React.memo(({ event }: { event: NostrEvent }) => {
switch (event.kind) {
@@ -18,6 +20,9 @@ const RenderEvent = React.memo(({ event }: { event: NostrEvent }) => {
return ;
case STREAM_KIND:
return ;
+ case 2:
+ const safeUrl = safeRelayUrl(event.content);
+ return safeUrl ? : null;
default:
return Unknown event kind: {event.kind};
}
diff --git a/src/views/home/following-tab.tsx b/src/views/home/following-tab.tsx
index 401482f5f..b21e7199c 100644
--- a/src/views/home/following-tab.tsx
+++ b/src/views/home/following-tab.tsx
@@ -1,9 +1,11 @@
+import { useCallback } from "react";
import { Flex, FormControl, FormLabel, Switch } from "@chakra-ui/react";
import { useSearchParams } from "react-router-dom";
+import { Kind } from "nostr-tools";
+
import { isReply, truncatedId } from "../../helpers/nostr/event";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useUserContacts } from "../../hooks/use-user-contacts";
-import { useCallback } from "react";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useCurrentAccount } from "../../hooks/use-current-account";
import RequireCurrentAccount from "../../providers/require-current-account";
@@ -34,7 +36,7 @@ function FollowingTabBody() {
const timeline = useTimelineLoader(
`${truncatedId(account.pubkey)}-following`,
readRelays,
- { authors: following, kinds: [1, 6] },
+ { authors: following, kinds: [Kind.Text, Kind.Repost, 2] },
{ enabled: following.length > 0, eventFilter }
);
diff --git a/src/views/relays/components/relay-card.tsx b/src/views/relays/components/relay-card.tsx
index 9cacb30ef..12b46ba41 100644
--- a/src/views/relays/components/relay-card.tsx
+++ b/src/views/relays/components/relay-card.tsx
@@ -19,11 +19,12 @@ import {
ModalOverlay,
ModalProps,
useDisclosure,
+ useToast,
} from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { useRelayInfo } from "../../../hooks/use-relay-info";
import { RelayFavicon } from "../../../components/relay-favicon";
-import { CodeIcon, ExternalLinkIcon } from "../../../components/icons";
+import { CodeIcon, ExternalLinkIcon, RepostIcon } from "../../../components/icons";
import { UserLink } from "../../../components/user-link";
import { UserAvatar } from "../../../components/user-avatar";
import { useClientRelays, useReadRelayUrls } from "../../../hooks/use-client-relays";
@@ -35,8 +36,12 @@ import useSubject from "../../../hooks/use-subject";
import useTimelineLoader from "../../../hooks/use-timeline-loader";
import RelayReviewNote from "./relay-review-note";
import styled from "@emotion/styled";
-import { PropsWithChildren } from "react";
+import { PropsWithChildren, useCallback } from "react";
import RawJson from "../../../components/debug-modals/raw-json";
+import { DraftNostrEvent } from "../../../types/nostr-event";
+import dayjs from "dayjs";
+import { useSigningContext } from "../../../providers/signing-provider";
+import { nostrPostAction } from "../../../classes/nostr-post-action";
const B = styled.span`
font-weight: bold;
@@ -116,10 +121,50 @@ export function RelayDebugButton({ url, ...props }: { url: string } & Omit) {
+ const toast = useToast();
+ const { requestSignature } = useSigningContext();
+
+ const recommendRelay = useCallback(async () => {
+ try {
+ const writeRelays = clientRelaysService.getWriteUrls();
+
+ const draft: DraftNostrEvent = {
+ kind: 2,
+ content: relay,
+ tags: [],
+ created_at: dayjs().unix(),
+ };
+
+ const signed = await requestSignature(draft);
+ if (!signed) return;
+
+ const post = nostrPostAction(writeRelays, signed);
+ await post.onComplete;
+ } catch (e) {
+ if (e instanceof Error) toast({ description: e.message, status: "error" });
+ }
+ }, []);
+
+ return (
+ }
+ aria-label="Recommend Relay"
+ title="Recommend Relay"
+ onClick={recommendRelay}
+ variant="ghost"
+ {...props}
+ />
+ );
+}
+
export default function RelayCard({ url, ...props }: { url: string } & Omit) {
return (
<>
-
+
@@ -132,7 +177,8 @@ export default function RelayCard({ url, ...props }: { url: string } & Omit
-
+
+
-
+
+
-
-
+
diff --git a/src/views/user/streams.tsx b/src/views/user/streams.tsx
index fc0a2f9ee..1041695d6 100644
--- a/src/views/user/streams.tsx
+++ b/src/views/user/streams.tsx
@@ -33,7 +33,7 @@ export default function UserStreamsTab() {
callback={callback}>
{streams.map((stream) => (
-
+
))}