mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-09 20:29:17 +02:00
add event delete modal
This commit is contained in:
parent
85c95101da
commit
5cfdd909c1
5
.changeset/silly-rats-count.md
Normal file
5
.changeset/silly-rats-count.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"nostrudel": minor
|
||||
---
|
||||
|
||||
Add simple event deletion modal
|
@ -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"} />
|
||||
|
@ -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} />}
|
||||
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -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]);
|
||||
|
@ -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":
|
||||
|
@ -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" />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user