Show label for paid relays

This commit is contained in:
hzrd149 2023-08-12 15:56:29 -05:00
parent 03d84eb1cd
commit ce550f588c
5 changed files with 66 additions and 9 deletions

View File

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Show label for paid relays

View File

@ -10,6 +10,7 @@ export type RelayInformationDocument = {
supported_nips?: number[]; supported_nips?: number[];
software: string; software: string;
version: string; version: string;
payments_url?: string
}; };
async function fetchInfo(relay: string) { async function fetchInfo(relay: string) {

View File

@ -7,6 +7,8 @@ import {
CardFooter, CardFooter,
CardHeader, CardHeader,
CardProps, CardProps,
Checkbox,
CheckboxProps,
Flex, Flex,
Heading, Heading,
IconButton, IconButton,
@ -17,24 +19,21 @@ import {
ModalContent, ModalContent,
ModalHeader, ModalHeader,
ModalOverlay, ModalOverlay,
ModalProps, Tag,
useDisclosure, useDisclosure,
useToast, useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom"; import { Link as RouterLink } from "react-router-dom";
import { useRelayInfo } from "../../../hooks/use-relay-info"; import { useRelayInfo } from "../../../hooks/use-relay-info";
import { RelayFavicon } from "../../../components/relay-favicon"; import { RelayFavicon } from "../../../components/relay-favicon";
import { CodeIcon, ExternalLinkIcon, RepostIcon } from "../../../components/icons"; import { CodeIcon, RepostIcon } from "../../../components/icons";
import { UserLink } from "../../../components/user-link"; import { UserLink } from "../../../components/user-link";
import { UserAvatar } from "../../../components/user-avatar"; import { UserAvatar } from "../../../components/user-avatar";
import { useClientRelays, useReadRelayUrls } from "../../../hooks/use-client-relays"; import { useClientRelays } from "../../../hooks/use-client-relays";
import clientRelaysService from "../../../services/client-relays"; import clientRelaysService from "../../../services/client-relays";
import { RelayMode } from "../../../classes/relay"; import { RelayMode } from "../../../classes/relay";
import { UserDnsIdentityIcon } from "../../../components/user-dns-identity-icon"; import { UserDnsIdentityIcon } from "../../../components/user-dns-identity-icon";
import { useCurrentAccount } from "../../../hooks/use-current-account"; import { useCurrentAccount } from "../../../hooks/use-current-account";
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 styled from "@emotion/styled";
import { PropsWithChildren, useCallback } from "react"; import { PropsWithChildren, useCallback } from "react";
import RawJson from "../../../components/debug-modals/raw-json"; import RawJson from "../../../components/debug-modals/raw-json";
@ -68,6 +67,8 @@ export function RelayMetadata({ url }: { url: string }) {
<UserDnsIdentityIcon pubkey={info.pubkey} onlyIcon /> <UserDnsIdentityIcon pubkey={info.pubkey} onlyIcon />
</Flex> </Flex>
)} )}
<Metadata name="Software">{info?.software}</Metadata>
<Metadata name="Version">{info?.version}</Metadata>
</Box> </Box>
); );
} }
@ -75,9 +76,9 @@ export function RelayMetadata({ url }: { url: string }) {
export function RelayJoinAction({ url, ...props }: { url: string } & Omit<ButtonProps, "children" | "onClick">) { export function RelayJoinAction({ url, ...props }: { url: string } & Omit<ButtonProps, "children" | "onClick">) {
const account = useCurrentAccount(); const account = useCurrentAccount();
const clientRelays = useClientRelays(); const clientRelays = useClientRelays();
const joined = clientRelays.some((r) => r.url === url); const relayConfig = clientRelays.find((r) => r.url === url);
return joined ? ( return relayConfig ? (
<Button <Button
colorScheme="red" colorScheme="red"
variant="outline" variant="outline"
@ -99,6 +100,26 @@ export function RelayJoinAction({ url, ...props }: { url: string } & Omit<Button
); );
} }
export function RelayModeAction({
url,
...props
}: { url: string } & Omit<CheckboxProps, "children" | "isChecked" | "onChange">) {
const clientRelays = useClientRelays();
const relayConfig = clientRelays.find((r) => r.url === url);
return relayConfig ? (
<Checkbox
isChecked={!!(relayConfig.mode & RelayMode.WRITE)}
onChange={(e) => {
clientRelaysService.updateRelay(relayConfig.url, e.target.checked ? RelayMode.WRITE : RelayMode.READ);
}}
{...props}
>
Write
</Checkbox>
) : null;
}
export function RelayDebugButton({ url, ...props }: { url: string } & Omit<IconButtonProps, "icon" | "aria-label">) { export function RelayDebugButton({ url, ...props }: { url: string } & Omit<IconButtonProps, "icon" | "aria-label">) {
const { info } = useRelayInfo(url); const { info } = useRelayInfo(url);
const debugModal = useDisclosure(); const debugModal = useDisclosure();
@ -162,6 +183,7 @@ export function RelayShareButton({
} }
export default function RelayCard({ url, ...props }: { url: string } & Omit<CardProps, "children">) { export default function RelayCard({ url, ...props }: { url: string } & Omit<CardProps, "children">) {
const { info } = useRelayInfo(url);
return ( return (
<> <>
<Card variant="outline" {...props}> <Card variant="outline" {...props}>
@ -169,6 +191,11 @@ export default function RelayCard({ url, ...props }: { url: string } & Omit<Card
<RelayFavicon relay={url} size="xs" /> <RelayFavicon relay={url} size="xs" />
<Heading size="md" isTruncated> <Heading size="md" isTruncated>
<RouterLink to={`/r/${encodeURIComponent(url)}`}>{url}</RouterLink> <RouterLink to={`/r/${encodeURIComponent(url)}`}>{url}</RouterLink>
{info?.payments_url && (
<Tag as="a" variant="solid" colorScheme="green" size="sm" ml="2" target="_blank" href={info.payments_url}>
Paid
</Tag>
)}
</Heading> </Heading>
</CardHeader> </CardHeader>
<CardBody px="2" py="0" display="flex" flexDirection="column" gap="2"> <CardBody px="2" py="0" display="flex" flexDirection="column" gap="2">
@ -176,6 +203,7 @@ export default function RelayCard({ url, ...props }: { url: string } & Omit<Card
</CardBody> </CardBody>
<CardFooter p="2" as={Flex} gap="2"> <CardFooter p="2" as={Flex} gap="2">
<RelayJoinAction url={url} size="sm" /> <RelayJoinAction url={url} size="sm" />
<RelayModeAction url={url} />
<RelayShareButton relay={url} ml="auto" size="sm" /> <RelayShareButton relay={url} ml="auto" size="sm" />
<RelayDebugButton url={url} size="sm" title="Show raw NIP-11 metadata" /> <RelayDebugButton url={url} size="sm" title="Show raw NIP-11 metadata" />

View File

@ -9,6 +9,7 @@ import {
TabPanel, TabPanel,
TabPanels, TabPanels,
Tabs, Tabs,
Tag,
useDisclosure, useDisclosure,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
@ -30,6 +31,19 @@ function RelayPage({ relay }: { relay: string }) {
<Flex gap="2" alignItems="center" wrap="wrap" justifyContent="space-between"> <Flex gap="2" alignItems="center" wrap="wrap" justifyContent="space-between">
<Heading isTruncated size={{ base: "md", sm: "lg" }}> <Heading isTruncated size={{ base: "md", sm: "lg" }}>
{relay} {relay}
{info?.payments_url && (
<Tag
as="a"
variant="solid"
colorScheme="green"
size={{ base: "sm", md: "lg" }}
ml="2"
target="_blank"
href={info.payments_url}
>
Paid
</Tag>
)}
</Heading> </Heading>
<ButtonGroup size={["sm", "md"]}> <ButtonGroup size={["sm", "md"]}>
<RelayDebugButton url={relay} ml="auto" /> <RelayDebugButton url={relay} ml="auto" />

View File

@ -1,5 +1,6 @@
import { useOutletContext, Link as RouterLink } from "react-router-dom"; import { useOutletContext, Link as RouterLink } from "react-router-dom";
import { Button, Flex, Heading, Spacer, StackDivider, VStack } from "@chakra-ui/react"; import { Button, Flex, Heading, Spacer, StackDivider, Tag, VStack } from "@chakra-ui/react";
import { useUserRelays } from "../../hooks/use-user-relays"; import { useUserRelays } from "../../hooks/use-user-relays";
import useTimelineLoader from "../../hooks/use-timeline-loader"; import useTimelineLoader from "../../hooks/use-timeline-loader";
import { truncatedId } from "../../helpers/nostr/event"; import { truncatedId } from "../../helpers/nostr/event";
@ -11,14 +12,22 @@ import { RelayFavicon } from "../../components/relay-favicon";
import { RelayDebugButton, RelayJoinAction, RelayMetadata, RelayShareButton } from "../relays/components/relay-card"; import { RelayDebugButton, RelayJoinAction, RelayMetadata, RelayShareButton } from "../relays/components/relay-card";
import IntersectionObserverProvider from "../../providers/intersection-observer"; import IntersectionObserverProvider from "../../providers/intersection-observer";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback"; import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import { useRelayInfo } from "../../hooks/use-relay-info";
function Relay({ url, reviews }: { url: string; reviews: NostrEvent[] }) { function Relay({ url, reviews }: { url: string; reviews: NostrEvent[] }) {
const { info } = useRelayInfo(url);
return ( return (
<Flex p="2" gap="2" direction="column"> <Flex p="2" gap="2" direction="column">
<Flex gap="2" alignItems="center"> <Flex gap="2" alignItems="center">
<RelayFavicon relay={url} size="xs" /> <RelayFavicon relay={url} size="xs" />
<Heading size="md" isTruncated> <Heading size="md" isTruncated>
<RouterLink to={`/r/${encodeURIComponent(url)}`}>{url}</RouterLink> <RouterLink to={`/r/${encodeURIComponent(url)}`}>{url}</RouterLink>
{info?.payments_url && (
<Tag as="a" variant="solid" colorScheme="green" size="sm" ml="2" target="_blank" href={info.payments_url}>
Paid
</Tag>
)}
</Heading> </Heading>
<Spacer /> <Spacer />
<RelayDebugButton url={url} size="sm" /> <RelayDebugButton url={url} size="sm" />