mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-11 21:29:26 +02:00
add vote buttons to wiki pages
This commit is contained in:
parent
260e2433b1
commit
3e2906e66b
@ -40,9 +40,7 @@ export default function EmbeddedWikiPage({ page: page, ...props }: Omit<CardProp
|
||||
</Text>
|
||||
</CardHeader>
|
||||
<CardBody p="2" overflow="hidden">
|
||||
<Text color="GrayText" noOfLines={2}>
|
||||
{getPageSummary(page)}
|
||||
</Text>
|
||||
<Text noOfLines={2}>{getPageSummary(page)}</Text>
|
||||
</CardBody>
|
||||
{showFooter && (
|
||||
<CardFooter>
|
||||
|
80
src/components/reactions/event-vote-buttions.tsx
Normal file
80
src/components/reactions/event-vote-buttions.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { Flex, FlexProps, IconButton, IconButtonProps, Text } from "@chakra-ui/react";
|
||||
|
||||
import { ChevronDownIcon, ChevronUpIcon, DislikeIcon, LikeIcon } from "../icons";
|
||||
import useCurrentAccount from "../../hooks/use-current-account";
|
||||
import useEventReactions from "../../hooks/use-event-reactions";
|
||||
import { draftEventReaction, getEventReactionScore, groupReactions } from "../../helpers/nostr/reactions";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { useAdditionalRelayContext } from "../../providers/local/additional-relay-context";
|
||||
import { usePublishEvent } from "../../providers/global/publish-provider";
|
||||
|
||||
export default function EventVoteButtons({
|
||||
event,
|
||||
chevrons = true,
|
||||
inline = false,
|
||||
variant = "ghost",
|
||||
...props
|
||||
}: Omit<FlexProps, "children"> & {
|
||||
event: NostrEvent;
|
||||
chevrons?: boolean;
|
||||
inline?: boolean;
|
||||
variant?: IconButtonProps["variant"];
|
||||
}) {
|
||||
const account = useCurrentAccount();
|
||||
const publish = usePublishEvent();
|
||||
const reactions = useEventReactions(event.id);
|
||||
const additionalRelays = useAdditionalRelayContext();
|
||||
|
||||
const grouped = useMemo(() => groupReactions(reactions ?? []), [reactions]);
|
||||
const { vote, up, down } = getEventReactionScore(grouped);
|
||||
|
||||
const hasUpVote = !!account && !!up?.pubkeys.includes(account.pubkey);
|
||||
const hasDownVote = !!account && !!down?.pubkeys.includes(account.pubkey);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const addVote = useCallback(
|
||||
async (vote: string) => {
|
||||
setLoading(true);
|
||||
const draft = draftEventReaction(event, vote);
|
||||
await publish("Reaction", draft, additionalRelays);
|
||||
setLoading(false);
|
||||
},
|
||||
[event, publish, additionalRelays],
|
||||
);
|
||||
|
||||
const upIcon = chevrons ? <ChevronUpIcon boxSize={6} /> : <LikeIcon />;
|
||||
const downIcon = chevrons ? <ChevronDownIcon boxSize={6} /> : <DislikeIcon />;
|
||||
|
||||
return (
|
||||
<Flex flexDirection={inline ? "row" : "column"} alignItems="center" {...props}>
|
||||
<IconButton
|
||||
aria-label="up vote"
|
||||
title="up vote"
|
||||
icon={upIcon}
|
||||
size="sm"
|
||||
variant={hasUpVote ? "solid" : variant}
|
||||
isLoading={loading}
|
||||
onClick={() => addVote("+")}
|
||||
isDisabled={!account || !!hasUpVote || !!hasDownVote}
|
||||
colorScheme={hasUpVote ? "primary" : "gray"}
|
||||
/>
|
||||
{(up || down) && vote > 0 && (
|
||||
<Text p="2" lineHeight="1em">
|
||||
{vote}
|
||||
</Text>
|
||||
)}
|
||||
<IconButton
|
||||
aria-label="down vote"
|
||||
title="down vote"
|
||||
icon={downIcon}
|
||||
size="sm"
|
||||
variant={hasDownVote ? "solid" : variant}
|
||||
isLoading={loading}
|
||||
onClick={() => addVote("-")}
|
||||
isDisabled={!account || !!hasUpVote || !!hasDownVote}
|
||||
colorScheme={hasDownVote ? "primary" : "gray"}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -85,13 +85,6 @@ export function buildApprovalMap(events: Iterable<NostrEvent>, mods: string[]) {
|
||||
return approvals;
|
||||
}
|
||||
|
||||
export function getCommunityPostVote(grouped: ReactionGroup[]) {
|
||||
const up = grouped.find((r) => r.emoji === "+");
|
||||
const down = grouped.find((r) => r.emoji === "-");
|
||||
const vote = (up?.pubkeys.length ?? 0) - (down?.pubkeys.length ?? 0);
|
||||
return { up, down, vote };
|
||||
}
|
||||
|
||||
export function getEventCommunityPointer(event: NostrEvent) {
|
||||
const communityTag = event.tags.filter(isATag).find((t) => t[1].startsWith(COMMUNITY_DEFINITION_KIND + ":"));
|
||||
return communityTag ? parseCoordinate(communityTag[1], true) : null;
|
||||
|
@ -41,3 +41,10 @@ export function draftEventReaction(event: NostrEvent, emoji = "+", url?: string)
|
||||
|
||||
return draft;
|
||||
}
|
||||
|
||||
export function getEventReactionScore(grouped: ReactionGroup[]) {
|
||||
const up = grouped.find((r) => r.emoji === "+");
|
||||
const down = grouped.find((r) => r.emoji === "-");
|
||||
const vote = (up?.pubkeys.length ?? 0) - (down?.pubkeys.length ?? 0);
|
||||
return { up, down, vote };
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { memo } from "react";
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import { Card, Flex } from "@chakra-ui/react";
|
||||
|
||||
import PostVoteButtons from "./post-vote-buttions";
|
||||
import EventVoteButtons from "../../../components/reactions/event-vote-buttions";
|
||||
import CommunityPost from "./community-post";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
|
||||
@ -9,7 +9,9 @@ const ApprovedEvent = memo(
|
||||
({ event, approvals, showCommunity }: { event: NostrEvent; approvals: NostrEvent[]; showCommunity?: boolean }) => {
|
||||
return (
|
||||
<Flex gap="2" alignItems="flex-start">
|
||||
<PostVoteButtons event={event} flexShrink={0} />
|
||||
<Card borderRadius="lg">
|
||||
<EventVoteButtons event={event} flexShrink={0} />
|
||||
</Card>
|
||||
<CommunityPost event={event} approvals={approvals} flex={1} showCommunity={showCommunity} />
|
||||
</Flex>
|
||||
);
|
||||
|
@ -1,63 +0,0 @@
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { Card, CardProps, IconButton, Text } from "@chakra-ui/react";
|
||||
|
||||
import useCurrentAccount from "../../../hooks/use-current-account";
|
||||
import useEventReactions from "../../../hooks/use-event-reactions";
|
||||
import { draftEventReaction, groupReactions } from "../../../helpers/nostr/reactions";
|
||||
import { getCommunityPostVote } from "../../../helpers/nostr/communities";
|
||||
import { ChevronDownIcon, ChevronUpIcon } from "../../../components/icons";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import { useAdditionalRelayContext } from "../../../providers/local/additional-relay-context";
|
||||
import { usePublishEvent } from "../../../providers/global/publish-provider";
|
||||
|
||||
export default function PostVoteButtons({ event, ...props }: Omit<CardProps, "children"> & { event: NostrEvent }) {
|
||||
const account = useCurrentAccount();
|
||||
const publish = usePublishEvent();
|
||||
const reactions = useEventReactions(event.id);
|
||||
const additionalRelays = useAdditionalRelayContext();
|
||||
|
||||
const grouped = useMemo(() => groupReactions(reactions ?? []), [reactions]);
|
||||
const { vote, up, down } = getCommunityPostVote(grouped);
|
||||
|
||||
const hasUpVote = !!account && !!up?.pubkeys.includes(account.pubkey);
|
||||
const hasDownVote = !!account && !!down?.pubkeys.includes(account.pubkey);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const addVote = useCallback(
|
||||
async (vote: string) => {
|
||||
setLoading(true);
|
||||
const draft = draftEventReaction(event, vote);
|
||||
await publish("Reaction", draft, additionalRelays);
|
||||
setLoading(false);
|
||||
},
|
||||
[event, publish, additionalRelays],
|
||||
);
|
||||
|
||||
return (
|
||||
<Card direction="column" alignItems="center" borderRadius="lg" {...props}>
|
||||
<IconButton
|
||||
aria-label="up vote"
|
||||
title="up vote"
|
||||
icon={<ChevronUpIcon boxSize={6} />}
|
||||
size="sm"
|
||||
variant={hasUpVote ? "solid" : "ghost"}
|
||||
isLoading={loading}
|
||||
onClick={() => addVote("+")}
|
||||
isDisabled={!account || !!hasUpVote || !!hasDownVote}
|
||||
colorScheme={hasUpVote ? "primary" : "gray"}
|
||||
/>
|
||||
{(up || down) && <Text my="1">{vote}</Text>}
|
||||
<IconButton
|
||||
aria-label="down vote"
|
||||
title="down vote"
|
||||
icon={<ChevronDownIcon boxSize={6} />}
|
||||
size="sm"
|
||||
variant={hasDownVote ? "solid" : "ghost"}
|
||||
isLoading={loading}
|
||||
onClick={() => addVote("-")}
|
||||
isDisabled={!account || !!hasUpVote || !!hasDownVote}
|
||||
colorScheme={hasDownVote ? "primary" : "gray"}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
@ -5,7 +5,6 @@ import {
|
||||
COMMUNITY_APPROVAL_KIND,
|
||||
buildApprovalMap,
|
||||
getCommunityMods,
|
||||
getCommunityPostVote,
|
||||
getCommunityRelays,
|
||||
} from "../../../helpers/nostr/communities";
|
||||
import useSubject from "../../../hooks/use-subject";
|
||||
@ -14,7 +13,7 @@ import IntersectionObserverProvider from "../../../providers/local/intersection-
|
||||
import TimelineActionAndStatus from "../../../components/timeline-page/timeline-action-and-status";
|
||||
import useUserMuteFilter from "../../../hooks/use-user-mute-filter";
|
||||
import useEventsReactions from "../../../hooks/use-events-reactions";
|
||||
import { groupReactions } from "../../../helpers/nostr/reactions";
|
||||
import { getEventReactionScore, groupReactions } from "../../../helpers/nostr/reactions";
|
||||
import ApprovedEvent from "../components/community-approved-post";
|
||||
import { RouterContext } from "../community-home";
|
||||
|
||||
@ -40,7 +39,7 @@ export default function CommunityTrendingView() {
|
||||
const dir: Record<string, number> = {};
|
||||
for (const [id, reactions] of Object.entries(eventReactions)) {
|
||||
const grouped = groupReactions(reactions);
|
||||
const { vote } = getCommunityPostVote(grouped);
|
||||
const { vote } = getEventReactionScore(grouped);
|
||||
dir[id] = vote;
|
||||
}
|
||||
return dir;
|
||||
|
@ -6,13 +6,13 @@ import useReplaceableEvent from "../../hooks/use-replaceable-event";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import { WIKI_PAGE_KIND, getPageTitle } from "../../helpers/nostr/wiki";
|
||||
import Timestamp from "../../components/timestamp";
|
||||
import DebugEventButton from "../../components/debug-modal/debug-event-button";
|
||||
import WikiPageHeader from "./components/wiki-page-header";
|
||||
import DiffViewer from "../../components/diff/diff-viewer";
|
||||
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
|
||||
import MarkdownContent from "./components/markdown";
|
||||
import { WIKI_RELAYS } from "../../const";
|
||||
import UserName from "../../components/user/user-name";
|
||||
import WikiPageMenu from "./components/wioki-page-menu";
|
||||
|
||||
function WikiComparePage({ base, diff }: { base: NostrEvent; diff: NostrEvent }) {
|
||||
const vertical = useBreakpointValue({ base: true, lg: false }) ?? false;
|
||||
@ -24,8 +24,8 @@ function WikiComparePage({ base, diff }: { base: NostrEvent; diff: NostrEvent })
|
||||
|
||||
<Flex gap="4" direction={vertical ? "column" : "row"}>
|
||||
<Box flex={1}>
|
||||
<ButtonGroup float="right">
|
||||
<DebugEventButton event={base} />
|
||||
<ButtonGroup float="right" size="sm">
|
||||
<WikiPageMenu page={base} aria-label="Page Optinos" />
|
||||
</ButtonGroup>
|
||||
<Heading>
|
||||
<UserName pubkey={base.pubkey} />
|
||||
@ -35,8 +35,8 @@ function WikiComparePage({ base, diff }: { base: NostrEvent; diff: NostrEvent })
|
||||
</Text>
|
||||
</Box>
|
||||
<Box flex={1}>
|
||||
<ButtonGroup float="right">
|
||||
<DebugEventButton event={diff} />
|
||||
<ButtonGroup float="right" size="sm">
|
||||
<WikiPageMenu page={diff} aria-label="Page Optinos" />
|
||||
</ButtonGroup>
|
||||
<Heading>
|
||||
<UserName pubkey={diff.pubkey} />
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Divider,
|
||||
Flex,
|
||||
Heading,
|
||||
SimpleGrid,
|
||||
Spinner,
|
||||
@ -34,6 +35,7 @@ import NoteZapButton from "../../components/note/note-zap-button";
|
||||
import ZapBubbles from "../../components/note/timeline-note/components/zap-bubbles";
|
||||
import QuoteRepostButton from "../../components/note/quote-repost-button";
|
||||
import WikiPageMenu from "./components/wioki-page-menu";
|
||||
import EventVoteButtons from "../../components/reactions/event-vote-buttions";
|
||||
|
||||
function ForkAlert({ page, address }: { page: NostrEvent; address: nip19.AddressPointer }) {
|
||||
const topic = getPageTopic(page);
|
||||
@ -103,22 +105,27 @@ function WikiPagePage({ page }: { page: NostrEvent }) {
|
||||
<VerticalPageLayout>
|
||||
<WikiPageHeader />
|
||||
|
||||
<Box>
|
||||
<ButtonGroup float="right">
|
||||
<QuoteRepostButton event={page} />
|
||||
<NoteZapButton event={page} showEventPreview={false} />
|
||||
<WikiPageMenu page={page} aria-label="Page Options" />
|
||||
</ButtonGroup>
|
||||
<Heading>{getPageTitle(page)}</Heading>
|
||||
<Text>
|
||||
by <UserLink pubkey={page.pubkey} /> - <Timestamp timestamp={page.created_at} />
|
||||
</Text>
|
||||
{address && <ForkAlert page={page} address={address} />}
|
||||
{defer?.address && <DeferAlert page={page} address={defer.address} />}
|
||||
<Divider my="2" />
|
||||
<MarkdownContent event={page} />
|
||||
<ZapBubbles event={page} mt="4" />
|
||||
</Box>
|
||||
<Flex wrap="wrap">
|
||||
<Box>
|
||||
<Heading>{getPageTitle(page)}</Heading>
|
||||
<Text>
|
||||
by <UserLink pubkey={page.pubkey} /> - <Timestamp timestamp={page.created_at} />
|
||||
</Text>
|
||||
</Box>
|
||||
<Flex alignItems="flex-end" gap="2" ml="auto">
|
||||
<EventVoteButtons event={page} inline chevrons={false} />
|
||||
<ButtonGroup size="sm">
|
||||
<QuoteRepostButton event={page} />
|
||||
<NoteZapButton event={page} showEventPreview={false} />
|
||||
<WikiPageMenu page={page} aria-label="Page Options" />
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
{address && <ForkAlert page={page} address={address} />}
|
||||
{defer?.address && <DeferAlert page={page} address={defer.address} />}
|
||||
<ZapBubbles event={page} />
|
||||
<Divider />
|
||||
<MarkdownContent event={page} />
|
||||
|
||||
{forks.length > 0 && (
|
||||
<>
|
||||
|
Loading…
x
Reference in New Issue
Block a user