add event delete modal

This commit is contained in:
hzrd149 2023-04-12 11:40:54 -05:00
parent 85c95101da
commit 5cfdd909c1
7 changed files with 103 additions and 51 deletions

View File

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Add simple event deletion modal

View File

@ -11,9 +11,8 @@ export default function NoteDebugModal({ event, ...props }: { event: NostrEvent
<Modal {...props}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Event Debug</ModalHeader>
<ModalCloseButton />
<ModalBody overflow="auto" fontSize="sm" padding="2">
<ModalBody overflow="auto" p="4">
<Flex gap="2" direction="column">
<RawValue heading="Event Id" value={event.id} />
<RawValue heading="Encoded id (NIP-19)" value={hexToBech32(event.id, Bech32Prefix.Note) ?? "failed"} />

View File

@ -15,7 +15,7 @@ export default function UserDebugModal({ pubkey, ...props }: { pubkey: string }
<ModalOverlay />
<ModalContent>
<ModalCloseButton />
<ModalBody overflow="auto" fontSize="sm" padding="2">
<ModalBody overflow="auto" p="4">
<Flex gap="2" direction="column">
<RawValue heading="Hex pubkey" value={pubkey} />
{npub && <RawValue heading="Encoded pubkey (NIP-19)" value={npub} />}

View File

@ -1,12 +1,17 @@
import {
Button,
Input,
MenuItem,
useDisclosure,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Toast,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import { useCopyToClipboard } from "react-use";
import { nip19 } from "nostr-tools";
@ -15,12 +20,18 @@ import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip19";
import { NostrEvent } from "../../types/nostr-event";
import { MenuIconButton, MenuIconButtonProps } from "../menu-icon-button";
import { ClipboardIcon, CodeIcon, LikeIcon, RepostIcon } from "../icons";
import { getReferences } from "../../helpers/nostr-event";
import { ClipboardIcon, CodeIcon, LikeIcon, RepostIcon, TrashIcon } from "../icons";
import NoteReactionsModal from "./note-zaps-modal";
import { getEventRelays } from "../../services/event-relays";
import relayScoreboardService from "../../services/relay-scoreboard";
import NoteDebugModal from "../debug-modals/note-debug-modal";
import { useCurrentAccount } from "../../hooks/use-current-account";
import { useCallback, useState } from "react";
import QuoteNote from "./quote-note";
import { buildDeleteEvent } from "../../helpers/nostr-event";
import signingService from "../../services/signing";
import { nostrPostAction } from "../../classes/nostr-post-action";
import clientRelaysService from "../../services/client-relays";
function getShareLink(eventId: string) {
const relays = getEventRelays(eventId).value;
@ -33,11 +44,37 @@ function getShareLink(eventId: string) {
}
export const NoteMenu = ({ event, ...props }: { event: NostrEvent } & Omit<MenuIconButtonProps, "children">) => {
const account = useCurrentAccount();
const toast = useToast();
const infoModal = useDisclosure();
const reactionsModal = useDisclosure();
const deleteModal = useDisclosure();
const [reason, setReason] = useState("");
const [deleting, setDeleting] = useState(false);
const [_clipboardState, copyToClipboard] = useCopyToClipboard();
const noteId = normalizeToBech32(event.id, Bech32Prefix.Note);
const deleteNote = useCallback(async () => {
try {
setDeleting(true);
const deleteEvent = buildDeleteEvent([event.id], reason);
const signed = await signingService.requestSignature(deleteEvent, account);
const results = nostrPostAction(clientRelaysService.getWriteUrls(), signed);
await results.onComplete;
deleteModal.onClose();
} catch (e) {
if (e instanceof Error) {
toast({
status: "error",
description: e.message,
});
}
} finally {
setDeleting(false);
}
}, [event]);
return (
<>
<MenuIconButton {...props}>
@ -52,16 +89,54 @@ export const NoteMenu = ({ event, ...props }: { event: NostrEvent } & Omit<MenuI
Copy Note ID
</MenuItem>
)}
{account.pubkey === event.pubkey && (
<MenuItem icon={<TrashIcon />} color="red.500" onClick={deleteModal.onOpen}>
Delete Note
</MenuItem>
)}
<MenuItem onClick={infoModal.onOpen} icon={<CodeIcon />}>
View Raw
</MenuItem>
</MenuIconButton>
{infoModal.isOpen && (
<NoteDebugModal event={event} isOpen={infoModal.isOpen} onClose={infoModal.onClose} size="6xl" />
)}
{reactionsModal.isOpen && (
<NoteReactionsModal noteId={event.id} isOpen={reactionsModal.isOpen} onClose={reactionsModal.onClose} />
)}
{deleteModal.isOpen && (
<Modal isOpen={deleteModal.isOpen} onClose={deleteModal.onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader px="4" py="2">
Delete Note?
</ModalHeader>
<ModalCloseButton />
<ModalBody px="4" py="0">
<QuoteNote noteId={event.id} />
<Input
name="reason"
value={reason}
onChange={(e) => setReason(e.target.value)}
placeholder="Reason (optional)"
mt="2"
/>
</ModalBody>
<ModalFooter px="4" py="4">
<Button variant="ghost" size="sm" mr={2} onClick={deleteModal.onClose}>
Cancel
</Button>
<Button colorScheme="red" variant="solid" onClick={deleteNote} size="sm" isLoading={deleting}>
Delete
</Button>
</ModalFooter>
</ModalContent>
</Modal>
)}
</>
);
};

View File

@ -9,7 +9,7 @@ import { NoteMenu } from "./note/note-menu";
import { UserAvatar } from "./user-avatar";
import { UserDnsIdentityIcon } from "./user-dns-identity";
import { UserLink } from "./user-link";
import { getUserDisplayName } from "../helpers/user-metadata";
import { unique } from "../helpers/array";
export default function RepostNote({ event, maxHeight }: { event: NostrEvent; maxHeight?: number }) {
const {
@ -19,7 +19,9 @@ export default function RepostNote({ event, maxHeight }: { event: NostrEvent; ma
} = useAsync(async () => {
const [_, eventId, relay] = event.tags.find(isETag) ?? [];
if (eventId) {
return singleEventService.requestEvent(eventId, relay ? [relay] : clientRelaysService.getReadUrls());
const readRelays = clientRelaysService.getReadUrls();
if (relay) readRelays.push(relay);
return singleEventService.requestEvent(eventId, unique(readRelays));
}
return null;
}, [event]);

View File

@ -143,6 +143,15 @@ export function buildQuoteRepost(event: NostrEvent): DraftNostrEvent {
};
}
export function buildDeleteEvent(eventIds: string[], reason = ""): DraftNostrEvent {
return {
kind: Kind.EventDeletion,
tags: eventIds.map((id) => ["e", id]),
content: reason,
created_at: moment().unix(),
};
}
export function parseRTag(tag: RTag): RelayConfig {
switch (tag[2]) {
case "write":

View File

@ -21,6 +21,7 @@ import { getUserDisplayName } from "../../../helpers/user-metadata";
import { useUserRelays } from "../../../hooks/use-user-relays";
import { RelayMode } from "../../../classes/relay";
import { CopyIconButton } from "../../../components/copy-icon-button";
import UserDebugModal from "../../../components/debug-modals/user-debug-modal";
export const UserProfileMenu = ({ pubkey, ...props }: { pubkey: string } & Omit<MenuIconButtonProps, "children">) => {
const npub = normalizeToBech32(pubkey, Bech32Prefix.Pubkey);
@ -67,46 +68,7 @@ export const UserProfileMenu = ({ pubkey, ...props }: { pubkey: string } & Omit<
</MenuItem>
</MenuIconButton>
{infoModal.isOpen && (
<Modal isOpen={infoModal.isOpen} onClose={infoModal.onClose} size="6xl">
<ModalOverlay />
<ModalContent>
<ModalCloseButton />
<ModalBody overflow="auto" fontSize="sm" padding="2">
<Flex gap="2" direction="column">
<Heading size="sm" mt="2">
Hex pubkey
</Heading>
<Flex gap="2">
<Code fontSize="md" wordBreak="break-all">
{pubkey}
</Code>
<CopyIconButton text={pubkey} size="xs" aria-label="copy hex" />
</Flex>
{npub && (
<>
<Heading size="sm" mt="2">
Encoded pubkey (NIP-19)
</Heading>
<Flex gap="2">
<Code fontSize="md" wordBreak="break-all">
{npub}
</Code>
<CopyIconButton text={npub} size="xs" aria-label="copy npub" />
</Flex>
</>
)}
<Heading size="sm" mt="2">
Metadata (kind 0)
</Heading>
<Code whiteSpace="pre" overflowX="auto">
{JSON.stringify(metadata, null, 2)}
</Code>
</Flex>
</ModalBody>
</ModalContent>
</Modal>
<UserDebugModal pubkey={pubkey} isOpen={infoModal.isOpen} onClose={infoModal.onClose} size="6xl" />
)}
</>
);