This commit is contained in:
hzrd149
2023-10-19 09:07:43 -05:00
parent 0d00f71882
commit 22c7554b0b
5 changed files with 103 additions and 87 deletions

View File

@@ -17,6 +17,7 @@ export default function EmbeddedCommunity({
<Card
as={LinkBox}
variant="outline"
maxW="lg"
gap="2"
overflow="hidden"
borderRadius="xl"

View File

@@ -53,9 +53,9 @@ function CommunityCard({ community, ...props }: Omit<CardProps, "children"> & {
</LinkOverlay>
</Heading>
</CardHeader>
<CardBody>
{/* <CardBody>
<CommunityDescription community={community} maxLength={128} flex={1} />
</CardBody>
</CardBody> */}
<CardFooter display="flex" alignItems="center" gap="2" pt="0">
<UserAvatarLink pubkey={community.pubkey} size="sm" />
<Text>by</Text>

View File

@@ -1,12 +1,22 @@
import { Button, Center, Flex, Heading, Link, SimpleGrid, Text } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { Navigate } from "react-router-dom";
import { nip19 } from "nostr-tools";
import VerticalPageLayout from "../../components/vertical-page-layout";
import { ErrorBoundary } from "../../components/error-boundary";
import useSubscribedCommunitiesList from "../../hooks/use-subscribed-communities-list";
import { useCurrentAccount } from "../../hooks/use-current-account";
import { Navigate } from "react-router-dom";
import { EmbedEventPointer } from "../../components/embed-event";
import { AddressPointer } from "nostr-tools/lib/nip19";
import useReplaceableEvent from "../../hooks/use-replaceable-event";
import CommunityCard from "./components/community-card";
function LoadCommunityCard({ pointer }: { pointer: AddressPointer }) {
const community = useReplaceableEvent(pointer);
if (!community) return <span>{nip19.naddrEncode(pointer)}</span>;
return <CommunityCard community={community} />;
}
function CommunitiesHomePage() {
const account = useCurrentAccount()!;
@@ -22,8 +32,8 @@ function CommunitiesHomePage() {
{communities.length > 0 ? (
<SimpleGrid spacing="2" columns={{ base: 1, lg: 2 }}>
{communities.map((pointer) => (
<ErrorBoundary>
<EmbedEventPointer pointer={{ type: "naddr", data: pointer }} />
<ErrorBoundary key={pointer.kind + pointer.pubkey + pointer.identifier}>
<LoadCommunityCard pointer={pointer} />
</ErrorBoundary>
))}
</SimpleGrid>

View File

@@ -0,0 +1,83 @@
import { useCallback, useMemo, useState } from "react";
import { Card, IconButton, Text, useToast } from "@chakra-ui/react";
import { useCurrentAccount } from "../../../hooks/use-current-account";
import useEventReactions from "../../../hooks/use-event-reactions";
import { useSigningContext } from "../../../providers/signing-provider";
import { draftEventReaction } from "../../../helpers/nostr/reactions";
import clientRelaysService from "../../../services/client-relays";
import { getCommunityRelays } from "../../../helpers/nostr/communities";
import { unique } from "../../../helpers/array";
import eventReactionsService from "../../../services/event-reactions";
import NostrPublishAction from "../../../classes/nostr-publish-action";
import { ChevronDownIcon, ChevronUpIcon } from "../../../components/icons";
import { NostrEvent } from "../../../types/nostr-event";
export default function PostVoteButtons({ event, community }: { event: NostrEvent; community: NostrEvent }) {
const account = useCurrentAccount();
const reactions = useEventReactions(event.id);
const toast = useToast();
const voteReactions = useMemo(() => {
return reactions?.filter((r) => r.content === "+" || r.content === "-") ?? [];
}, [reactions]);
const vote = useMemo(() => {
return voteReactions.reduce((t, r) => {
if (r.content === "+") return t + 1;
else if (r.content === "-") return t - 1;
return t;
}, 0);
}, [voteReactions]);
const myVote = reactions?.find((e) => e.pubkey === account?.pubkey);
const { requestSignature } = useSigningContext();
const [loading, setLoading] = useState(false);
const addVote = useCallback(
async (vote: string) => {
setLoading(true);
try {
const draft = draftEventReaction(event, vote);
const signed = await requestSignature(draft);
if (signed) {
const writeRelays = clientRelaysService.getWriteUrls();
const communityRelays = getCommunityRelays(community);
new NostrPublishAction("Reaction", unique([...writeRelays, ...communityRelays]), signed);
eventReactionsService.handleEvent(signed);
}
} catch (e) {
if (e instanceof Error) toast({ description: e.message, status: "error" });
}
setLoading(false);
},
[event, community, requestSignature],
);
return (
<Card direction="column" alignItems="center" borderRadius="lg">
<IconButton
aria-label="up vote"
title="up vote"
icon={<ChevronUpIcon boxSize={6} />}
size="sm"
variant={myVote?.content === "+" ? "solid" : "ghost"}
isLoading={loading}
onClick={() => addVote("+")}
isDisabled={!account || !!myVote}
colorScheme={myVote ? "primary" : "gray"}
/>
{voteReactions.length > 0 && <Text my="1">{vote}</Text>}
<IconButton
aria-label="down vote"
title="down vote"
icon={<ChevronDownIcon boxSize={6} />}
size="sm"
variant={myVote?.content === "-" ? "solid" : "ghost"}
isLoading={loading}
onClick={() => addVote("-")}
isDisabled={!account || !!myVote}
/>
</Card>
);
}

View File

@@ -1,5 +1,5 @@
import { useCallback, useMemo, useRef, useState } from "react";
import { Box, Card, Flex, IconButton, Text, useToast } from "@chakra-ui/react";
import { useRef } from "react";
import { Flex } from "@chakra-ui/react";
import { useOutletContext } from "react-router-dom";
import { unique } from "../../../helpers/array";
@@ -20,85 +20,7 @@ import useSingleEvent from "../../../hooks/use-single-event";
import { useAdditionalRelayContext } from "../../../providers/additional-relay-context";
import IntersectionObserverProvider, { useRegisterIntersectionEntity } from "../../../providers/intersection-observer";
import TimelineActionAndStatus from "../../../components/timeline-page/timeline-action-and-status";
import { ChevronUpIcon } from "../../../components/icons";
import { ChevronDownIcon } from "@chakra-ui/icons";
import useEventReactions from "../../../hooks/use-event-reactions";
import { useMeasure, useStartTyping } from "react-use";
import { draftEventReaction } from "../../../helpers/nostr/reactions";
import eventReactionsService from "../../../services/event-reactions";
import NostrPublishAction from "../../../classes/nostr-publish-action";
import clientRelaysService from "../../../services/client-relays";
import { useSigningContext } from "../../../providers/signing-provider";
import { useCurrentAccount } from "../../../hooks/use-current-account";
function ApprovalVoteButtons({ event, community }: { event: NostrEvent; community: NostrEvent }) {
const account = useCurrentAccount();
const reactions = useEventReactions(event.id);
const toast = useToast();
const voteReactions = useMemo(() => {
return reactions?.filter((r) => r.content === "+" || r.content === "-") ?? [];
}, [reactions]);
const vote = useMemo(() => {
return voteReactions.reduce((t, r) => {
if (r.content === "+") return t + 1;
else if (r.content === "-") return t - 1;
return t;
}, 0);
}, [voteReactions]);
const myVote = reactions?.find((e) => e.pubkey === account?.pubkey);
const { requestSignature } = useSigningContext();
const [loading, setLoading] = useState(false);
const addVote = useCallback(
async (vote: string) => {
setLoading(true);
try {
const draft = draftEventReaction(event, vote);
const signed = await requestSignature(draft);
if (signed) {
const writeRelays = clientRelaysService.getWriteUrls();
const communityRelays = getCommunityRelays(community);
new NostrPublishAction("Reaction", unique([...writeRelays, ...communityRelays]), signed);
eventReactionsService.handleEvent(signed);
}
} catch (e) {
if (e instanceof Error) toast({ description: e.message, status: "error" });
}
setLoading(false);
},
[event, community, requestSignature],
);
return (
<Card direction="column" alignItems="center" borderRadius="lg">
<IconButton
aria-label="up vote"
title="up vote"
icon={<ChevronUpIcon boxSize={6} />}
size="sm"
variant={myVote?.content === "+" ? "solid" : "ghost"}
isLoading={loading}
onClick={() => addVote("+")}
isDisabled={!account || !!myVote}
colorScheme={myVote ? "primary" : "gray"}
/>
{voteReactions.length > 0 && <Text my="1">{vote}</Text>}
<IconButton
aria-label="down vote"
title="down vote"
icon={<ChevronDownIcon boxSize={6} />}
size="sm"
variant={myVote?.content === "-" ? "solid" : "ghost"}
isLoading={loading}
onClick={() => addVote("-")}
isDisabled={!account || !!myVote}
/>
</Card>
);
}
import PostVoteButtons from "../components/post-vote-buttions";
function ApprovedEvent({ approval, community }: { approval: NostrEvent; community: NostrEvent }) {
const ref = useRef<HTMLDivElement | null>(null);
@@ -116,7 +38,7 @@ function ApprovedEvent({ approval, community }: { approval: NostrEvent; communit
if (!event) return;
return (
<Flex ref={ref} gap="2" alignItems="flex-start" overflow="hidden">
<ApprovalVoteButtons event={event} community={community} />
<PostVoteButtons event={event} community={community} />
<EmbedEvent event={event} flex={1} />
</Flex>
);