mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-26 11:37:40 +02:00
Add debug modal for community posts
This commit is contained in:
46
src/components/debug-modals/community-post-debug-modal.tsx
Normal file
46
src/components/debug-modals/community-post-debug-modal.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
87
src/views/community/components/community-post-menu.tsx
Normal file
87
src/views/community/components/community-post-menu.tsx
Normal 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}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@@ -1,5 +1,15 @@
|
|||||||
import { MouseEventHandler, useCallback, useRef } from "react";
|
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 { Link as RouterLink } from "react-router-dom";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { Kind } from "nostr-tools";
|
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 useUserMuteFilter from "../../../hooks/use-user-mute-filter";
|
||||||
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
|
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
|
||||||
import useSingleEvent from "../../../hooks/use-single-event";
|
import useSingleEvent from "../../../hooks/use-single-event";
|
||||||
|
import CommunityPostMenu from "./community-post-menu";
|
||||||
|
|
||||||
export function ApprovalIcon({ approval }: { approval: NostrEvent }) {
|
export function ApprovalIcon({ approval }: { approval: NostrEvent }) {
|
||||||
const ref = useRef<HTMLAnchorElement | null>(null);
|
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);
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
useRegisterIntersectionEntity(ref, getEventUID(event));
|
useRegisterIntersectionEntity(ref, getEventUID(event));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card as={LinkBox} overflow="hidden" ref={ref}>
|
<Card as={LinkBox} ref={ref} {...props}>
|
||||||
<PostSubject event={event} />
|
<PostSubject event={event} />
|
||||||
<CardBody p="2">
|
<CardBody p="2">
|
||||||
<InlineNoteContent event={event} maxLength={96} />
|
<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" />
|
Posted {dayjs.unix(event.created_at).fromNow()} by <UserLink pubkey={event.pubkey} fontWeight="bold" />
|
||||||
</Text>
|
</Text>
|
||||||
{approvals.length > 0 && <Approvals approvals={approvals} />}
|
{approvals.length > 0 && <Approvals approvals={approvals} />}
|
||||||
|
<CommunityPostMenu
|
||||||
|
event={event}
|
||||||
|
community={community}
|
||||||
|
approvals={approvals}
|
||||||
|
aria-label="More Options"
|
||||||
|
size="xs"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CommunityRepostPost({ event, approvals, community }: CommunityPostPropTypes) {
|
export function CommunityRepostPost({
|
||||||
|
event,
|
||||||
|
approvals,
|
||||||
|
community,
|
||||||
|
...props
|
||||||
|
}: Omit<CardProps, "children"> & CommunityPostPropTypes) {
|
||||||
const encodedRepost = parseHardcodedNoteContent(event);
|
const encodedRepost = parseHardcodedNoteContent(event);
|
||||||
|
|
||||||
const [_, eventId, relay] = event.tags.find(isETag) ?? [];
|
const [_, eventId, relay] = event.tags.find(isETag) ?? [];
|
||||||
@@ -107,7 +136,7 @@ export function CommunityRepostPost({ event, approvals, community }: CommunityPo
|
|||||||
if (repost && muteFilter(repost)) return;
|
if (repost && muteFilter(repost)) return;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card as={LinkBox} overflow="hidden" ref={ref}>
|
<Card as={LinkBox} ref={ref} {...props}>
|
||||||
{repost && (
|
{repost && (
|
||||||
<>
|
<>
|
||||||
<PostSubject event={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" />
|
Shared {dayjs.unix(event.created_at).fromNow()} by <UserLink pubkey={event.pubkey} fontWeight="bold" />
|
||||||
</Text>
|
</Text>
|
||||||
{approvals.length > 0 && <Approvals approvals={approvals} />}
|
{approvals.length > 0 && <Approvals approvals={approvals} />}
|
||||||
|
<CommunityPostMenu
|
||||||
|
event={event}
|
||||||
|
community={community}
|
||||||
|
approvals={approvals}
|
||||||
|
aria-label="More Options"
|
||||||
|
size="xs"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CommunityPost({ event, approvals, community }: CommunityPostPropTypes) {
|
export default function CommunityPost({
|
||||||
|
event,
|
||||||
|
approvals,
|
||||||
|
community,
|
||||||
|
...props
|
||||||
|
}: Omit<CardProps, "children"> & CommunityPostPropTypes) {
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case Kind.Text:
|
case Kind.Text:
|
||||||
return <CommunityTextPost event={event} approvals={approvals} community={community} />;
|
return <CommunityTextPost event={event} approvals={approvals} community={community} {...props} />;
|
||||||
case Kind.Repost:
|
case Kind.Repost:
|
||||||
return <CommunityRepostPost event={event} approvals={approvals} community={community} />;
|
return <CommunityRepostPost event={event} approvals={approvals} community={community} {...props} />;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useMemo, useState } from "react";
|
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 { useCurrentAccount } from "../../../hooks/use-current-account";
|
||||||
import useEventReactions from "../../../hooks/use-event-reactions";
|
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 { ChevronDownIcon, ChevronUpIcon } from "../../../components/icons";
|
||||||
import { NostrEvent } from "../../../types/nostr-event";
|
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 account = useCurrentAccount();
|
||||||
const reactions = useEventReactions(event.id);
|
const reactions = useEventReactions(event.id);
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -50,7 +54,7 @@ export default function PostVoteButtons({ event, community }: { event: NostrEven
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card direction="column" alignItems="center" borderRadius="lg">
|
<Card direction="column" alignItems="center" borderRadius="lg" {...props}>
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label="up vote"
|
aria-label="up vote"
|
||||||
title="up vote"
|
title="up vote"
|
||||||
|
@@ -11,15 +11,14 @@ import TimelineActionAndStatus from "../../../components/timeline-page/timeline-
|
|||||||
import PostVoteButtons from "../components/post-vote-buttions";
|
import PostVoteButtons from "../components/post-vote-buttions";
|
||||||
import TimelineLoader from "../../../classes/timeline-loader";
|
import TimelineLoader from "../../../classes/timeline-loader";
|
||||||
import CommunityPost from "../components/community-post";
|
import CommunityPost from "../components/community-post";
|
||||||
|
import useUserMuteFilter from "../../../hooks/use-user-mute-filter";
|
||||||
|
|
||||||
const ApprovedEvent = memo(
|
const ApprovedEvent = memo(
|
||||||
({ event, approvals, community }: { event: NostrEvent; approvals: NostrEvent[]; community: NostrEvent }) => {
|
({ event, approvals, community }: { event: NostrEvent; approvals: NostrEvent[]; community: NostrEvent }) => {
|
||||||
return (
|
return (
|
||||||
<Flex gap="2" alignItems="flex-start" overflow="hidden">
|
<Flex gap="2" alignItems="flex-start">
|
||||||
<PostVoteButtons event={event} community={community} />
|
<PostVoteButtons event={event} community={community} flexShrink={0} />
|
||||||
<Flex gap="2" direction="column" flex={1} overflow="hidden">
|
<CommunityPost event={event} community={community} approvals={approvals} flex={1} />
|
||||||
<CommunityPost event={event} community={community} approvals={approvals} />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -27,6 +26,7 @@ const ApprovedEvent = memo(
|
|||||||
|
|
||||||
export default function CommunityNewestView() {
|
export default function CommunityNewestView() {
|
||||||
const { community, timeline } = useOutletContext() as { community: NostrEvent; timeline: TimelineLoader };
|
const { community, timeline } = useOutletContext() as { community: NostrEvent; timeline: TimelineLoader };
|
||||||
|
const muteFilter = useUserMuteFilter();
|
||||||
const mods = getCommunityMods(community);
|
const mods = getCommunityMods(community);
|
||||||
|
|
||||||
const events = useSubject(timeline.timeline);
|
const events = useSubject(timeline.timeline);
|
||||||
@@ -34,7 +34,8 @@ export default function CommunityNewestView() {
|
|||||||
|
|
||||||
const approved = events
|
const approved = events
|
||||||
.filter((e) => approvalMap.has(e.id))
|
.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);
|
const callback = useTimelineCurserIntersectionCallback(timeline);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user