mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-27 20:17:05 +02:00
show community members
fix quote repost cache issue
This commit is contained in:
5
.changeset/plenty-laws-rule.md
Normal file
5
.changeset/plenty-laws-rule.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Show community members
|
@@ -20,7 +20,7 @@ export function QuoteRepostButton({
|
|||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
const nevent = getSharableEventAddress(event);
|
const nevent = getSharableEventAddress(event);
|
||||||
openModal({ initContent: "\nnostr:" + nevent });
|
openModal({ cacheFormKey: null, initContent: "\nnostr:" + nevent });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -56,7 +56,7 @@ type FormValues = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type PostModalProps = {
|
export type PostModalProps = {
|
||||||
cacheFormKey?: string;
|
cacheFormKey?: string | null;
|
||||||
initContent?: string;
|
initContent?: string;
|
||||||
initCommunity?: string;
|
initCommunity?: string;
|
||||||
};
|
};
|
||||||
|
@@ -4,14 +4,15 @@ import { useMount, useUnmount } from "react-use";
|
|||||||
|
|
||||||
// TODO: make these caches expire
|
// TODO: make these caches expire
|
||||||
export default function useCacheForm<TFieldValues extends FieldValues = FieldValues>(
|
export default function useCacheForm<TFieldValues extends FieldValues = FieldValues>(
|
||||||
key: string,
|
key: string | null,
|
||||||
getValues: UseFormGetValues<TFieldValues>,
|
getValues: UseFormGetValues<TFieldValues>,
|
||||||
setValue: UseFormSetValue<TFieldValues>,
|
setValue: UseFormSetValue<TFieldValues>,
|
||||||
state: UseFormStateReturn<TFieldValues>,
|
state: UseFormStateReturn<TFieldValues>,
|
||||||
) {
|
) {
|
||||||
const storageKey = key + "-form-values";
|
const storageKey = key && key + "-form-values";
|
||||||
|
|
||||||
useMount(() => {
|
useMount(() => {
|
||||||
|
if (storageKey === null) return;
|
||||||
try {
|
try {
|
||||||
const cached = localStorage.getItem(storageKey);
|
const cached = localStorage.getItem(storageKey);
|
||||||
localStorage.removeItem(storageKey);
|
localStorage.removeItem(storageKey);
|
||||||
@@ -29,12 +30,14 @@ export default function useCacheForm<TFieldValues extends FieldValues = FieldVal
|
|||||||
const stateRef = useRef<UseFormStateReturn<TFieldValues>>(state);
|
const stateRef = useRef<UseFormStateReturn<TFieldValues>>(state);
|
||||||
stateRef.current = state;
|
stateRef.current = state;
|
||||||
useUnmount(() => {
|
useUnmount(() => {
|
||||||
|
if (storageKey === null) return;
|
||||||
if (stateRef.current.isDirty && !stateRef.current.isSubmitted) {
|
if (stateRef.current.isDirty && !stateRef.current.isSubmitted) {
|
||||||
localStorage.setItem(storageKey, JSON.stringify(getValues()));
|
localStorage.setItem(storageKey, JSON.stringify(getValues()));
|
||||||
} else localStorage.removeItem(storageKey);
|
} else localStorage.removeItem(storageKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
return useCallback(() => {
|
return useCallback(() => {
|
||||||
|
if (storageKey === null) return;
|
||||||
localStorage.removeItem(storageKey);
|
localStorage.removeItem(storageKey);
|
||||||
}, [storageKey]);
|
}, [storageKey]);
|
||||||
}
|
}
|
||||||
|
13
src/hooks/use-count-community-members.ts
Normal file
13
src/hooks/use-count-community-members.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { SUBSCRIBED_COMMUNITIES_LIST_IDENTIFIER } from "../helpers/nostr/communities";
|
||||||
|
import { getEventCoordinate } from "../helpers/nostr/events";
|
||||||
|
import { NOTE_LIST_KIND } from "../helpers/nostr/lists";
|
||||||
|
import { NostrEvent } from "../types/nostr-event";
|
||||||
|
import useEventCount from "./use-event-count";
|
||||||
|
|
||||||
|
export default function useCountCommunityMembers(community: NostrEvent) {
|
||||||
|
return useEventCount({
|
||||||
|
"#a": [getEventCoordinate(community)],
|
||||||
|
"#d": [SUBSCRIBED_COMMUNITIES_LIST_IDENTIFIER],
|
||||||
|
kinds: [NOTE_LIST_KIND],
|
||||||
|
});
|
||||||
|
}
|
@@ -9,7 +9,7 @@ import relayPoolService from "./relay-pool";
|
|||||||
// TODO: move this to settings
|
// TODO: move this to settings
|
||||||
const COUNT_RELAY = "wss://relay.nostr.band";
|
const COUNT_RELAY = "wss://relay.nostr.band";
|
||||||
|
|
||||||
const RATE_LIMIT = 10 / 1000;
|
const RATE_LIMIT = 1000;
|
||||||
|
|
||||||
class EventCountService {
|
class EventCountService {
|
||||||
subjects = new SuperMap<string, Subject<number>>(() => new Subject<number>());
|
subjects = new SuperMap<string, Subject<number>>(() => new Subject<number>());
|
||||||
|
@@ -10,6 +10,9 @@ import {
|
|||||||
Heading,
|
Heading,
|
||||||
LinkBox,
|
LinkBox,
|
||||||
LinkOverlay,
|
LinkOverlay,
|
||||||
|
Tag,
|
||||||
|
TagLabel,
|
||||||
|
TagLeftIcon,
|
||||||
Text,
|
Text,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
|
|
||||||
@@ -21,12 +24,17 @@ import CommunityDescription from "./community-description";
|
|||||||
import useCountCommunityPosts from "../hooks/use-count-community-post";
|
import useCountCommunityPosts from "../hooks/use-count-community-post";
|
||||||
import UserAvatarLink from "../../../components/user-avatar-link";
|
import UserAvatarLink from "../../../components/user-avatar-link";
|
||||||
import { UserLink } from "../../../components/user-link";
|
import { UserLink } from "../../../components/user-link";
|
||||||
|
import useCountCommunityMembers from "../../../hooks/use-count-community-members";
|
||||||
|
import { readablizeSats } from "../../../helpers/bolt11";
|
||||||
|
import { CommunityIcon } from "../../../components/icons";
|
||||||
|
import User01 from "../../../components/icons/user-01";
|
||||||
|
|
||||||
function CommunityCard({ community, ...props }: Omit<CardProps, "children"> & { community: NostrEvent }) {
|
function CommunityCard({ community, ...props }: Omit<CardProps, "children"> & { community: NostrEvent }) {
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
useRegisterIntersectionEntity(ref, getEventUID(community));
|
useRegisterIntersectionEntity(ref, getEventUID(community));
|
||||||
|
|
||||||
const name = getCommunityName(community);
|
const name = getCommunityName(community);
|
||||||
|
const countMembers = useCountCommunityMembers(community);
|
||||||
|
|
||||||
// NOTE: disabled because nostr.band has a rate limit
|
// NOTE: disabled because nostr.band has a rate limit
|
||||||
// const notesInLastMonth = useCountCommunityPosts(community);
|
// const notesInLastMonth = useCountCommunityPosts(community);
|
||||||
@@ -60,6 +68,13 @@ function CommunityCard({ community, ...props }: Omit<CardProps, "children"> & {
|
|||||||
<UserAvatarLink pubkey={community.pubkey} size="sm" />
|
<UserAvatarLink pubkey={community.pubkey} size="sm" />
|
||||||
<Text>by</Text>
|
<Text>by</Text>
|
||||||
<UserLink pubkey={community.pubkey} />
|
<UserLink pubkey={community.pubkey} />
|
||||||
|
{countMembers && (
|
||||||
|
<Tag variant="solid" ml="auto" alignSelf="flex-end" textShadow="none">
|
||||||
|
<TagLeftIcon as={User01} boxSize={4} />
|
||||||
|
<TagLabel>{readablizeSats(countMembers)}</TagLabel>
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* {notesInLastMonth !== undefined && <Text ml="auto">{notesInLastMonth} Posts in the past month</Text>} */}
|
{/* {notesInLastMonth !== undefined && <Text ml="auto">{notesInLastMonth} Posts in the past month</Text>} */}
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
|
77
src/views/community/components/community-members-modal.tsx
Normal file
77
src/views/community/components/community-members-modal.tsx
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Flex,
|
||||||
|
Modal,
|
||||||
|
ModalBody,
|
||||||
|
ModalCloseButton,
|
||||||
|
ModalContent,
|
||||||
|
ModalFooter,
|
||||||
|
ModalHeader,
|
||||||
|
ModalOverlay,
|
||||||
|
ModalProps,
|
||||||
|
SimpleGrid,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import { NostrEvent } from "../../../types/nostr-event";
|
||||||
|
import useTimelineLoader from "../../../hooks/use-timeline-loader";
|
||||||
|
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
|
||||||
|
import { SUBSCRIBED_COMMUNITIES_LIST_IDENTIFIER, getCommunityRelays } from "../../../helpers/nostr/communities";
|
||||||
|
import { getEventCoordinate } from "../../../helpers/nostr/events";
|
||||||
|
import { NOTE_LIST_KIND } from "../../../helpers/nostr/lists";
|
||||||
|
import IntersectionObserverProvider from "../../../providers/intersection-observer";
|
||||||
|
import useSubject from "../../../hooks/use-subject";
|
||||||
|
import { useTimelineCurserIntersectionCallback } from "../../../hooks/use-timeline-cursor-intersection-callback";
|
||||||
|
import TimelineActionAndStatus from "../../../components/timeline-page/timeline-action-and-status";
|
||||||
|
import { UserLink } from "../../../components/user-link";
|
||||||
|
import { UserDnsIdentityIcon } from "../../../components/user-dns-identity-icon";
|
||||||
|
import UserAvatarLink from "../../../components/user-avatar-link";
|
||||||
|
|
||||||
|
function UserCard({ pubkey }: { pubkey: string }) {
|
||||||
|
return (
|
||||||
|
<Flex overflow="hidden" gap="4" alignItems="center">
|
||||||
|
<UserAvatarLink pubkey={pubkey} />
|
||||||
|
<Flex direction="column" flex={1} overflow="hidden">
|
||||||
|
<UserLink pubkey={pubkey} fontWeight="bold" />
|
||||||
|
<UserDnsIdentityIcon pubkey={pubkey} />
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ({ community, onClose, ...props }: Omit<ModalProps, "children"> & { community: NostrEvent }) {
|
||||||
|
const communityCoordinate = getEventCoordinate(community);
|
||||||
|
const readRelays = useReadRelayUrls(getCommunityRelays(community));
|
||||||
|
const timeline = useTimelineLoader(`${communityCoordinate}-members`, readRelays, {
|
||||||
|
"#a": [communityCoordinate],
|
||||||
|
"#d": [SUBSCRIBED_COMMUNITIES_LIST_IDENTIFIER],
|
||||||
|
kinds: [NOTE_LIST_KIND],
|
||||||
|
});
|
||||||
|
|
||||||
|
const lists = useSubject(timeline.timeline);
|
||||||
|
const callback = useTimelineCurserIntersectionCallback(timeline);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IntersectionObserverProvider callback={callback}>
|
||||||
|
<Modal onClose={onClose} size="4xl" {...props}>
|
||||||
|
<ModalOverlay />
|
||||||
|
<ModalContent>
|
||||||
|
<ModalHeader px="4" pt="4" pb="0">
|
||||||
|
Community Members
|
||||||
|
</ModalHeader>
|
||||||
|
<ModalCloseButton />
|
||||||
|
<ModalBody p="4">
|
||||||
|
<SimpleGrid columns={{ base: 1, lg: 2 }} spacing="4">
|
||||||
|
{lists.map((list) => (
|
||||||
|
<UserCard key={list.id} pubkey={list.pubkey} />
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
|
<TimelineActionAndStatus timeline={timeline} />
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter px="4" pt="0" pb="4">
|
||||||
|
<Button onClick={onClose}>Close</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
</IntersectionObserverProvider>
|
||||||
|
);
|
||||||
|
}
|
@@ -24,74 +24,90 @@ import { RelayIconStack } from "../../../components/relay-icon-stack";
|
|||||||
import { NostrEvent } from "../../../types/nostr-event";
|
import { NostrEvent } from "../../../types/nostr-event";
|
||||||
import CommunityJoinButton from "../../communities/components/community-subscribe-button";
|
import CommunityJoinButton from "../../communities/components/community-subscribe-button";
|
||||||
import CommunityMenu from "./community-menu";
|
import CommunityMenu from "./community-menu";
|
||||||
|
import useCountCommunityMembers from "../../../hooks/use-count-community-members";
|
||||||
|
import { readablizeSats } from "../../../helpers/bolt11";
|
||||||
|
import CommunityMembersModal from "./community-members-modal";
|
||||||
|
|
||||||
export default function HorizontalCommunityDetails({
|
export default function HorizontalCommunityDetails({
|
||||||
community,
|
community,
|
||||||
...props
|
...props
|
||||||
}: Omit<CardProps, "children"> & { community: NostrEvent }) {
|
}: Omit<CardProps, "children"> & { community: NostrEvent }) {
|
||||||
|
const membersModal = useDisclosure();
|
||||||
const communityRelays = getCommunityRelays(community);
|
const communityRelays = getCommunityRelays(community);
|
||||||
const mods = getCommunityMods(community);
|
const mods = getCommunityMods(community);
|
||||||
const description = getCommunityDescription(community);
|
const description = getCommunityDescription(community);
|
||||||
const rules = getCommunityRules(community);
|
const rules = getCommunityRules(community);
|
||||||
|
|
||||||
const more = useDisclosure();
|
const more = useDisclosure();
|
||||||
|
const countMembers = useCountCommunityMembers(community);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card {...props}>
|
<>
|
||||||
<CardBody>
|
<Card {...props}>
|
||||||
<ButtonGroup float="right">
|
<CardBody>
|
||||||
<CommunityJoinButton community={community} />
|
<ButtonGroup float="right">
|
||||||
<CommunityMenu community={community} aria-label="More" />
|
<CommunityJoinButton community={community} />
|
||||||
</ButtonGroup>
|
<CommunityMenu community={community} aria-label="More" />
|
||||||
{description && (
|
</ButtonGroup>
|
||||||
<>
|
{description && (
|
||||||
<Heading size="sm" mb="2">
|
<>
|
||||||
Description
|
<Heading size="sm" mb="1">
|
||||||
</Heading>
|
Description
|
||||||
<CommunityDescription community={community} mb="2" />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{more.isOpen ? (
|
|
||||||
<SimpleGrid spacing="4" columns={2}>
|
|
||||||
<Box>
|
|
||||||
<Heading size="sm" mb="2">
|
|
||||||
Mods
|
|
||||||
</Heading>
|
</Heading>
|
||||||
<Flex direction="column" gap="2">
|
<CommunityDescription community={community} mb="1" />
|
||||||
{mods.map((pubkey) => (
|
</>
|
||||||
<Flex gap="2">
|
)}
|
||||||
<UserAvatarLink pubkey={pubkey} size="xs" />
|
|
||||||
<UserLink pubkey={pubkey} />
|
{more.isOpen ? (
|
||||||
</Flex>
|
<SimpleGrid spacing="4" columns={2}>
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
{rules && (
|
|
||||||
<Box>
|
<Box>
|
||||||
<Heading size="sm" mb="2">
|
<Heading size="sm" mb="1">
|
||||||
Rules
|
Mods
|
||||||
</Heading>
|
|
||||||
<Text whiteSpace="pre-wrap">{rules}</Text>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{communityRelays.length > 0 && (
|
|
||||||
<Box>
|
|
||||||
<Heading size="sm" mt="4" mb="2">
|
|
||||||
Relays
|
|
||||||
</Heading>
|
</Heading>
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
<RelayIconStack relays={communityRelays} />
|
{mods.map((pubkey) => (
|
||||||
|
<Flex gap="2">
|
||||||
|
<UserAvatarLink pubkey={pubkey} size="xs" />
|
||||||
|
<UserLink pubkey={pubkey} />
|
||||||
|
</Flex>
|
||||||
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
<Box as="button" textAlign="start" cursor="pointer" onClick={membersModal.onOpen}>
|
||||||
</SimpleGrid>
|
<Heading size="sm" mb="1">
|
||||||
) : (
|
Members
|
||||||
<Button variant="link" onClick={more.onOpen} w="full">
|
</Heading>
|
||||||
Show more
|
<Text>{countMembers ? readablizeSats(countMembers) : "unknown"}</Text>
|
||||||
</Button>
|
</Box>
|
||||||
)}
|
{rules && (
|
||||||
</CardBody>
|
<Box>
|
||||||
</Card>
|
<Heading size="sm" mb="1">
|
||||||
|
Rules
|
||||||
|
</Heading>
|
||||||
|
<Text whiteSpace="pre-wrap">{rules}</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{communityRelays.length > 0 && (
|
||||||
|
<Box>
|
||||||
|
<Heading size="sm" mt="4" mb="1">
|
||||||
|
Relays
|
||||||
|
</Heading>
|
||||||
|
<Flex direction="column" gap="2">
|
||||||
|
<RelayIconStack relays={communityRelays} />
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</SimpleGrid>
|
||||||
|
) : (
|
||||||
|
<Button variant="link" onClick={more.onOpen} w="full">
|
||||||
|
Show more
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
{membersModal.isOpen && (
|
||||||
|
<CommunityMembersModal isOpen={membersModal.isOpen} onClose={membersModal.onClose} community={community} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Box, ButtonGroup, Card, CardProps, Flex, Heading, Text } from "@chakra-ui/react";
|
import { Box, ButtonGroup, Card, CardProps, Flex, Heading, Text, useDisclosure } from "@chakra-ui/react";
|
||||||
import {
|
import {
|
||||||
getCommunityDescription,
|
getCommunityDescription,
|
||||||
getCommunityMods,
|
getCommunityMods,
|
||||||
@@ -12,59 +12,78 @@ import { RelayIconStack } from "../../../components/relay-icon-stack";
|
|||||||
import { NostrEvent } from "../../../types/nostr-event";
|
import { NostrEvent } from "../../../types/nostr-event";
|
||||||
import CommunityJoinButton from "../../communities/components/community-subscribe-button";
|
import CommunityJoinButton from "../../communities/components/community-subscribe-button";
|
||||||
import CommunityMenu from "./community-menu";
|
import CommunityMenu from "./community-menu";
|
||||||
|
import useCountCommunityMembers from "../../../hooks/use-count-community-members";
|
||||||
|
import CommunityMembersModal from "./community-members-modal";
|
||||||
|
import { readablizeSats } from "../../../helpers/bolt11";
|
||||||
|
|
||||||
export default function VerticalCommunityDetails({
|
export default function VerticalCommunityDetails({
|
||||||
community,
|
community,
|
||||||
...props
|
...props
|
||||||
}: Omit<CardProps, "children"> & { community: NostrEvent }) {
|
}: Omit<CardProps, "children"> & { community: NostrEvent }) {
|
||||||
|
const membersModal = useDisclosure();
|
||||||
const communityRelays = getCommunityRelays(community);
|
const communityRelays = getCommunityRelays(community);
|
||||||
const mods = getCommunityMods(community);
|
const mods = getCommunityMods(community);
|
||||||
const description = getCommunityDescription(community);
|
const description = getCommunityDescription(community);
|
||||||
const rules = getCommunityRules(community);
|
const rules = getCommunityRules(community);
|
||||||
|
|
||||||
|
const countMembers = useCountCommunityMembers(community);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card p="4" {...props}>
|
<>
|
||||||
{description && (
|
<Card p="4" gap="4" {...props}>
|
||||||
<>
|
{description && (
|
||||||
<Heading size="sm" mb="2">
|
<Box>
|
||||||
About
|
<Heading size="sm" mb="1">
|
||||||
</Heading>
|
About
|
||||||
<CommunityDescription community={community} maxLength={256} showExpand />
|
</Heading>
|
||||||
</>
|
<CommunityDescription community={community} maxLength={256} showExpand />
|
||||||
)}
|
</Box>
|
||||||
<ButtonGroup w="full" my="2">
|
)}
|
||||||
<CommunityJoinButton community={community} flex={1} />
|
<ButtonGroup w="full">
|
||||||
<CommunityMenu community={community} aria-label="More" />
|
<CommunityJoinButton community={community} flex={1} />
|
||||||
</ButtonGroup>
|
<CommunityMenu community={community} aria-label="More" />
|
||||||
<Heading size="sm" mt="4" mb="2">
|
</ButtonGroup>
|
||||||
Mods
|
<Box>
|
||||||
</Heading>
|
<Heading size="sm" mb="1">
|
||||||
<Flex direction="column" gap="2">
|
Mods
|
||||||
{mods.map((pubkey) => (
|
|
||||||
<Flex gap="2">
|
|
||||||
<UserAvatarLink pubkey={pubkey} size="xs" />
|
|
||||||
<UserLink pubkey={pubkey} />
|
|
||||||
</Flex>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
{rules && (
|
|
||||||
<>
|
|
||||||
<Heading size="sm" mt="4" mb="2">
|
|
||||||
Rules
|
|
||||||
</Heading>
|
|
||||||
<Text whiteSpace="pre-wrap">{rules}</Text>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{communityRelays.length > 0 && (
|
|
||||||
<>
|
|
||||||
<Heading size="sm" mt="4" mb="2">
|
|
||||||
Relays
|
|
||||||
</Heading>
|
</Heading>
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
<RelayIconStack relays={communityRelays} />
|
{mods.map((pubkey) => (
|
||||||
|
<Flex gap="2">
|
||||||
|
<UserAvatarLink pubkey={pubkey} size="xs" />
|
||||||
|
<UserLink pubkey={pubkey} />
|
||||||
|
</Flex>
|
||||||
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
</>
|
</Box>
|
||||||
|
<Box as="button" textAlign="start" cursor="pointer" onClick={membersModal.onOpen}>
|
||||||
|
<Heading size="sm" mb="1">
|
||||||
|
Members
|
||||||
|
</Heading>
|
||||||
|
<Text>{countMembers ? readablizeSats(countMembers) : "unknown"}</Text>
|
||||||
|
</Box>
|
||||||
|
{rules && (
|
||||||
|
<Box>
|
||||||
|
<Heading size="sm" mb="1">
|
||||||
|
Rules
|
||||||
|
</Heading>
|
||||||
|
<Text whiteSpace="pre-wrap">{rules}</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{communityRelays.length > 0 && (
|
||||||
|
<Box>
|
||||||
|
<Heading size="sm" mb="1">
|
||||||
|
Relays
|
||||||
|
</Heading>
|
||||||
|
<Flex direction="column" gap="2">
|
||||||
|
<RelayIconStack relays={communityRelays} />
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
{membersModal.isOpen && (
|
||||||
|
<CommunityMembersModal isOpen={membersModal.isOpen} onClose={membersModal.onClose} community={community} />
|
||||||
)}
|
)}
|
||||||
</Card>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user