Show relay recommendations in timeline

This commit is contained in:
hzrd149
2023-08-05 12:53:18 -05:00
parent fa30250a20
commit d984577044
9 changed files with 74 additions and 16 deletions

View File

@@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Show relay recommendations in timeline

View File

@@ -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>;
}

View File

@@ -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 }
);

View File

@@ -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}`}

View File

@@ -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>
</>

View File

@@ -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} />

View File

@@ -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 }
);

View File

@@ -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">

View File

@@ -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} />