mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-27 20:17:05 +02:00
Show relay recommendations in timeline
This commit is contained in:
5
.changeset/slimy-stingrays-behave.md
Normal file
5
.changeset/slimy-stingrays-behave.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"nostrudel": minor
|
||||
---
|
||||
|
||||
Show relay recommendations in timeline
|
@@ -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 <RepostNote event={event} />;
|
||||
case STREAM_KIND:
|
||||
return <StreamNote event={event} />;
|
||||
case 2:
|
||||
const safeUrl = safeRelayUrl(event.content);
|
||||
return safeUrl ? <RelayCard url={safeUrl} /> : null;
|
||||
default:
|
||||
return <Text>Unknown event kind: {event.kind}</Text>;
|
||||
}
|
||||
|
@@ -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 }
|
||||
);
|
||||
|
||||
|
@@ -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<IconB
|
||||
);
|
||||
}
|
||||
|
||||
export function RelayShareButton({
|
||||
relay,
|
||||
...props
|
||||
}: { relay: string } & Omit<IconButtonProps, "icon" | "aria-label">) {
|
||||
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 (
|
||||
<IconButton
|
||||
icon={<RepostIcon />}
|
||||
aria-label="Recommend Relay"
|
||||
title="Recommend Relay"
|
||||
onClick={recommendRelay}
|
||||
variant="ghost"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default function RelayCard({ url, ...props }: { url: string } & Omit<CardProps, "children">) {
|
||||
return (
|
||||
<>
|
||||
<Card {...props}>
|
||||
<Card variant="outline" {...props}>
|
||||
<CardHeader display="flex" gap="2" alignItems="center" p="2">
|
||||
<RelayFavicon relay={url} size="xs" />
|
||||
<Heading size="md" isTruncated>
|
||||
@@ -132,7 +177,8 @@ export default function RelayCard({ url, ...props }: { url: string } & Omit<Card
|
||||
<CardFooter p="2" as={Flex} gap="2">
|
||||
<RelayJoinAction url={url} size="sm" />
|
||||
|
||||
<RelayDebugButton url={url} ml="auto" size="sm" />
|
||||
<RelayShareButton relay={url} ml="auto" size="sm" />
|
||||
<RelayDebugButton url={url} size="sm" />
|
||||
<Button
|
||||
as="a"
|
||||
href={`https://nostr.watch/relay/${new URL(url).host}`}
|
||||
|
@@ -53,7 +53,7 @@ export default function RelaysView() {
|
||||
</Flex>
|
||||
<SimpleGrid minChildWidth="25rem" spacing="2">
|
||||
{filteredRelays.map((url) => (
|
||||
<RelayCard key={url} url={url} variant="outline" />
|
||||
<RelayCard key={url} url={url} variant="outline" maxW="xl" />
|
||||
))}
|
||||
</SimpleGrid>
|
||||
|
||||
@@ -63,7 +63,7 @@ export default function RelaysView() {
|
||||
<Heading size="lg">Discovered Relays</Heading>
|
||||
<SimpleGrid minChildWidth="25rem" spacing="2">
|
||||
{discoveredRelays.map((url) => (
|
||||
<RelayCard key={url} url={url} variant="outline" />
|
||||
<RelayCard key={url} url={url} variant="outline" maxW="xl" />
|
||||
))}
|
||||
</SimpleGrid>
|
||||
</>
|
||||
|
@@ -58,9 +58,9 @@ function StreamsPage() {
|
||||
<RelaySelectionButton ml="auto" />
|
||||
</Flex>
|
||||
<IntersectionObserverProvider callback={callback}>
|
||||
<SimpleGrid minChildWidth="20rem" spacing="2">
|
||||
<SimpleGrid minChildWidth={["full", "20rem"]} spacing="2">
|
||||
{streams.map((stream) => (
|
||||
<StreamCard key={stream.event.id} stream={stream} />
|
||||
<StreamCard key={stream.event.id} stream={stream} maxW="lg" />
|
||||
))}
|
||||
</SimpleGrid>
|
||||
<TimelineActionAndStatus timeline={timeline} />
|
||||
|
@@ -32,7 +32,7 @@ export default function UserNotesTab() {
|
||||
readRelays,
|
||||
{
|
||||
authors: [pubkey],
|
||||
kinds: [Kind.Text, Kind.Repost, STREAM_KIND],
|
||||
kinds: [Kind.Text, Kind.Repost, STREAM_KIND, 2],
|
||||
},
|
||||
{ eventFilter }
|
||||
);
|
||||
|
@@ -8,7 +8,7 @@ import useSubject from "../../hooks/use-subject";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import RelayReviewNote from "../relays/components/relay-review-note";
|
||||
import { RelayFavicon } from "../../components/relay-favicon";
|
||||
import { RelayDebugButton, RelayJoinAction, RelayMetadata } from "../relays/components/relay-card";
|
||||
import { RelayDebugButton, RelayJoinAction, RelayMetadata, RelayShareButton } from "../relays/components/relay-card";
|
||||
import IntersectionObserverProvider from "../../providers/intersection-observer";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
|
||||
@@ -21,12 +21,12 @@ function Relay({ url, reviews }: { url: string; reviews: NostrEvent[] }) {
|
||||
{url}
|
||||
</Heading>
|
||||
<Spacer />
|
||||
<RelayJoinAction url={url} size="sm" />
|
||||
<RelayDebugButton url={url} size="sm" />
|
||||
<RelayShareButton relay={url} size="sm" />
|
||||
<Button as={RouterLink} to={`/global?relay=${url}`} size="sm">
|
||||
Notes
|
||||
</Button>
|
||||
|
||||
<RelayDebugButton url={url} size="sm" />
|
||||
<RelayJoinAction url={url} size="sm" />
|
||||
</Flex>
|
||||
<RelayMetadata url={url} />
|
||||
<Flex py="0" direction="column" gap="2">
|
||||
|
@@ -33,7 +33,7 @@ export default function UserStreamsTab() {
|
||||
<IntersectionObserverProvider<string> callback={callback}>
|
||||
<SimpleGrid minChildWidth="20rem" spacing="2">
|
||||
{streams.map((stream) => (
|
||||
<StreamCard key={stream.event.id} stream={stream} />
|
||||
<StreamCard key={stream.event.id} stream={stream} maxW="lg" />
|
||||
))}
|
||||
</SimpleGrid>
|
||||
<TimelineActionAndStatus timeline={timeline} />
|
||||
|
Reference in New Issue
Block a user