Add debug modal for community posts

This commit is contained in:
hzrd149 2023-10-21 17:45:39 -05:00
parent 8871aedae5
commit 7e92713182
5 changed files with 197 additions and 17 deletions

View File

@ -0,0 +1,46 @@
import { Modal, ModalOverlay, ModalContent, ModalBody, ModalCloseButton, Flex } from "@chakra-ui/react";
import { ModalProps } from "@chakra-ui/react";
import { nip19 } from "nostr-tools";
import { NostrEvent } from "../../types/nostr-event";
import RawJson from "./raw-json";
import RawValue from "./raw-value";
import RawPre from "./raw-pre";
import userMetadataService from "../../services/user-metadata";
import { getUserDisplayName } from "../../helpers/user-metadata";
import { getEventCoordinate } from "../../helpers/nostr/events";
export default function CommunityPostDebugModal({
event,
community,
approvals,
...props
}: { event: NostrEvent; community: NostrEvent; approvals: NostrEvent[] } & Omit<ModalProps, "children">) {
return (
<Modal {...props}>
<ModalOverlay />
<ModalContent>
<ModalCloseButton />
<ModalBody p="4">
<Flex gap="2" direction="column">
<RawValue heading="Event Id" value={event.id} />
<RawValue heading="Encoded id (NIP-19)" value={nip19.noteEncode(event.id)} />
<RawValue heading="Community Coordinate" value={getEventCoordinate(community)} />
<RawPre heading="Content" value={event.content} />
<RawJson heading="JSON" json={event} />
{approvals.map((approval) => (
<RawJson
key={approval.id}
heading={`Approval by ${getUserDisplayName(
userMetadataService.getSubject(approval.pubkey).value,
approval.pubkey,
)}`}
json={approval}
/>
))}
</Flex>
</ModalBody>
</ModalContent>
</Modal>
);
}

View File

@ -0,0 +1,87 @@
import { MenuItem, useDisclosure } from "@chakra-ui/react";
import { nip19 } from "nostr-tools";
import { CustomMenuIconButton, MenuIconButtonProps } from "../../../components/menu-icon-button";
import { useCurrentAccount } from "../../../hooks/use-current-account";
import { NostrEvent } from "../../../types/nostr-event";
import { useMuteModalContext } from "../../../providers/mute-modal-provider";
import useUserMuteFunctions from "../../../hooks/use-user-mute-functions";
import { useDeleteEventContext } from "../../../providers/delete-event-provider";
import { getSharableEventAddress } from "../../../helpers/nip19";
import {
CodeIcon,
CopyToClipboardIcon,
ExternalLinkIcon,
MuteIcon,
RepostIcon,
TrashIcon,
UnmuteIcon,
} from "../../../components/icons";
import { buildAppSelectUrl } from "../../../helpers/nostr/apps";
import CommunityPostDebugModal from "../../../components/debug-modals/community-post-debug-modal";
export default function CommunityPostMenu({
event,
community,
approvals,
...props
}: Omit<MenuIconButtonProps, "children"> & { event: NostrEvent; community: NostrEvent; approvals: NostrEvent[] }) {
const account = useCurrentAccount();
const debugModal = useDisclosure();
// const { isMuted, unmute } = useUserMuteFunctions(event.pubkey);
// const { openModal } = useMuteModalContext();
const { deleteEvent } = useDeleteEventContext();
const noteId = nip19.noteEncode(event.id);
const address = getSharableEventAddress(event);
return (
<>
<CustomMenuIconButton {...props}>
{address && (
<MenuItem onClick={() => window.open(buildAppSelectUrl(address), "_blank")} icon={<ExternalLinkIcon />}>
View in app...
</MenuItem>
)}
{/* {account?.pubkey !== event.pubkey && (
<MenuItem
onClick={isMuted ? unmute : () => openModal(event.pubkey)}
icon={isMuted ? <UnmuteIcon /> : <MuteIcon />}
color="red.500"
>
{isMuted ? "Unmute User" : "Mute User"}
</MenuItem>
)} */}
<MenuItem onClick={() => window.navigator.clipboard.writeText("nostr:" + address)} icon={<RepostIcon />}>
Copy Share Link
</MenuItem>
{noteId && (
<MenuItem onClick={() => window.navigator.clipboard.writeText(noteId)} icon={<CopyToClipboardIcon />}>
Copy Note ID
</MenuItem>
)}
{account?.pubkey === event.pubkey && (
<MenuItem icon={<TrashIcon />} color="red.500" onClick={() => deleteEvent(event)}>
Delete Note
</MenuItem>
)}
<MenuItem onClick={debugModal.onOpen} icon={<CodeIcon />}>
View Raw
</MenuItem>
</CustomMenuIconButton>
{debugModal.isOpen && (
<CommunityPostDebugModal
event={event}
isOpen={debugModal.isOpen}
onClose={debugModal.onClose}
size="6xl"
approvals={approvals}
community={community}
/>
)}
</>
);
}

View File

@ -1,5 +1,15 @@
import { MouseEventHandler, useCallback, useRef } from "react";
import { AvatarGroup, Card, CardBody, CardFooter, CardHeader, Heading, LinkBox, Text } from "@chakra-ui/react";
import {
AvatarGroup,
Card,
CardBody,
CardFooter,
CardHeader,
CardProps,
Heading,
LinkBox,
Text,
} from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import dayjs from "dayjs";
import { Kind } from "nostr-tools";
@ -17,6 +27,7 @@ import UserAvatarLink from "../../../components/user-avatar-link";
import useUserMuteFilter from "../../../hooks/use-user-mute-filter";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import useSingleEvent from "../../../hooks/use-single-event";
import CommunityPostMenu from "./community-post-menu";
export function ApprovalIcon({ approval }: { approval: NostrEvent }) {
const ref = useRef<HTMLAnchorElement | null>(null);
@ -71,12 +82,17 @@ function Approvals({ approvals }: { approvals: NostrEvent[] }) {
);
}
export function CommunityTextPost({ event, approvals, community }: CommunityPostPropTypes) {
export function CommunityTextPost({
event,
approvals,
community,
...props
}: Omit<CardProps, "children"> & CommunityPostPropTypes) {
const ref = useRef<HTMLDivElement | null>(null);
useRegisterIntersectionEntity(ref, getEventUID(event));
return (
<Card as={LinkBox} overflow="hidden" ref={ref}>
<Card as={LinkBox} ref={ref} {...props}>
<PostSubject event={event} />
<CardBody p="2">
<InlineNoteContent event={event} maxLength={96} />
@ -86,12 +102,25 @@ export function CommunityTextPost({ event, approvals, community }: CommunityPost
Posted {dayjs.unix(event.created_at).fromNow()} by <UserLink pubkey={event.pubkey} fontWeight="bold" />
</Text>
{approvals.length > 0 && <Approvals approvals={approvals} />}
<CommunityPostMenu
event={event}
community={community}
approvals={approvals}
aria-label="More Options"
size="xs"
variant="ghost"
/>
</CardFooter>
</Card>
);
}
export function CommunityRepostPost({ event, approvals, community }: CommunityPostPropTypes) {
export function CommunityRepostPost({
event,
approvals,
community,
...props
}: Omit<CardProps, "children"> & CommunityPostPropTypes) {
const encodedRepost = parseHardcodedNoteContent(event);
const [_, eventId, relay] = event.tags.find(isETag) ?? [];
@ -107,7 +136,7 @@ export function CommunityRepostPost({ event, approvals, community }: CommunityPo
if (repost && muteFilter(repost)) return;
return (
<Card as={LinkBox} overflow="hidden" ref={ref}>
<Card as={LinkBox} ref={ref} {...props}>
{repost && (
<>
<PostSubject event={repost} />
@ -121,17 +150,30 @@ export function CommunityRepostPost({ event, approvals, community }: CommunityPo
Shared {dayjs.unix(event.created_at).fromNow()} by <UserLink pubkey={event.pubkey} fontWeight="bold" />
</Text>
{approvals.length > 0 && <Approvals approvals={approvals} />}
<CommunityPostMenu
event={event}
community={community}
approvals={approvals}
aria-label="More Options"
size="xs"
variant="ghost"
/>
</CardFooter>
</Card>
);
}
export default function CommunityPost({ event, approvals, community }: CommunityPostPropTypes) {
export default function CommunityPost({
event,
approvals,
community,
...props
}: Omit<CardProps, "children"> & CommunityPostPropTypes) {
switch (event.kind) {
case Kind.Text:
return <CommunityTextPost event={event} approvals={approvals} community={community} />;
return <CommunityTextPost event={event} approvals={approvals} community={community} {...props} />;
case Kind.Repost:
return <CommunityRepostPost event={event} approvals={approvals} community={community} />;
return <CommunityRepostPost event={event} approvals={approvals} community={community} {...props} />;
}
return null;
}

View File

@ -1,5 +1,5 @@
import { useCallback, useMemo, useState } from "react";
import { Card, IconButton, Text, useToast } from "@chakra-ui/react";
import { Card, CardProps, IconButton, Text, useToast } from "@chakra-ui/react";
import { useCurrentAccount } from "../../../hooks/use-current-account";
import useEventReactions from "../../../hooks/use-event-reactions";
@ -13,7 +13,11 @@ 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 }) {
export default function PostVoteButtons({
event,
community,
...props
}: Omit<CardProps, "children"> & { event: NostrEvent; community: NostrEvent }) {
const account = useCurrentAccount();
const reactions = useEventReactions(event.id);
const toast = useToast();
@ -50,7 +54,7 @@ export default function PostVoteButtons({ event, community }: { event: NostrEven
);
return (
<Card direction="column" alignItems="center" borderRadius="lg">
<Card direction="column" alignItems="center" borderRadius="lg" {...props}>
<IconButton
aria-label="up vote"
title="up vote"

View File

@ -11,15 +11,14 @@ import TimelineActionAndStatus from "../../../components/timeline-page/timeline-
import PostVoteButtons from "../components/post-vote-buttions";
import TimelineLoader from "../../../classes/timeline-loader";
import CommunityPost from "../components/community-post";
import useUserMuteFilter from "../../../hooks/use-user-mute-filter";
const ApprovedEvent = memo(
({ event, approvals, community }: { event: NostrEvent; approvals: NostrEvent[]; community: NostrEvent }) => {
return (
<Flex gap="2" alignItems="flex-start" overflow="hidden">
<PostVoteButtons event={event} community={community} />
<Flex gap="2" direction="column" flex={1} overflow="hidden">
<CommunityPost event={event} community={community} approvals={approvals} />
</Flex>
<Flex gap="2" alignItems="flex-start">
<PostVoteButtons event={event} community={community} flexShrink={0} />
<CommunityPost event={event} community={community} approvals={approvals} flex={1} />
</Flex>
);
},
@ -27,6 +26,7 @@ const ApprovedEvent = memo(
export default function CommunityNewestView() {
const { community, timeline } = useOutletContext() as { community: NostrEvent; timeline: TimelineLoader };
const muteFilter = useUserMuteFilter();
const mods = getCommunityMods(community);
const events = useSubject(timeline.timeline);
@ -34,7 +34,8 @@ export default function CommunityNewestView() {
const approved = events
.filter((e) => approvalMap.has(e.id))
.map((event) => ({ event, approvals: approvalMap.get(event.id) }));
.map((event) => ({ event, approvals: approvalMap.get(event.id) }))
.filter((e) => !muteFilter(e.event));
const callback = useTimelineCurserIntersectionCallback(timeline);