mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-10-05 17:55:01 +02:00
Improve display of unknown events
This commit is contained in:
5
.changeset/great-terms-flash.md
Normal file
5
.changeset/great-terms-flash.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Improve display of unknown events
|
@@ -1,27 +1,109 @@
|
|||||||
import { useMemo } from "react";
|
import { MouseEventHandler, useCallback, useMemo } from "react";
|
||||||
import { Box, Button, Card, CardBody, CardHeader, CardProps, Flex, Link, Text, useDisclosure } from "@chakra-ui/react";
|
import {
|
||||||
|
Box,
|
||||||
|
BoxProps,
|
||||||
|
Button,
|
||||||
|
ButtonGroup,
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
CardHeader,
|
||||||
|
CardProps,
|
||||||
|
Flex,
|
||||||
|
IconButton,
|
||||||
|
Link,
|
||||||
|
Text,
|
||||||
|
useDisclosure,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
|
import { nip19 } from "nostr-tools";
|
||||||
|
|
||||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||||
import { NostrEvent } from "../../../types/nostr-event";
|
import { NostrEvent, Tag, isATag, isETag, isPTag } from "../../../types/nostr-event";
|
||||||
import UserAvatarLink from "../../user-avatar-link";
|
import UserAvatarLink from "../../user-avatar-link";
|
||||||
import UserLink from "../../user-link";
|
import UserLink from "../../user-link";
|
||||||
import { truncatedId } from "../../../helpers/nostr/events";
|
import { aTagToAddressPointer, eTagToEventPointer } from "../../../helpers/nostr/events";
|
||||||
import { buildAppSelectUrl } from "../../../helpers/nostr/apps";
|
import { buildAppSelectUrl } from "../../../helpers/nostr/apps";
|
||||||
import { UserDnsIdentityIcon } from "../../user-dns-identity-icon";
|
import { UserDnsIdentityIcon } from "../../user-dns-identity-icon";
|
||||||
import {
|
import {
|
||||||
embedEmoji,
|
embedEmoji,
|
||||||
embedNostrHashtags,
|
embedNostrHashtags,
|
||||||
embedNostrLinks,
|
embedNostrLinks,
|
||||||
embedNostrMentions,
|
|
||||||
renderGenericUrl,
|
renderGenericUrl,
|
||||||
renderImageUrl,
|
renderImageUrl,
|
||||||
renderVideoUrl,
|
renderVideoUrl,
|
||||||
} from "../../embed-types";
|
} from "../../embed-types";
|
||||||
import { EmbedableContent, embedUrls } from "../../../helpers/embeds";
|
import { EmbedableContent, embedUrls } from "../../../helpers/embeds";
|
||||||
import Timestamp from "../../timestamp";
|
import Timestamp from "../../timestamp";
|
||||||
import { CodeIcon } from "../../icons";
|
import { CodeIcon, ExternalLinkIcon } from "../../icons";
|
||||||
import NoteDebugModal from "../../debug-modals/note-debug-modal";
|
import NoteDebugModal from "../../debug-modals/note-debug-modal";
|
||||||
import { renderAudioUrl } from "../../embed-types/audio";
|
import { renderAudioUrl } from "../../embed-types/audio";
|
||||||
|
import { EmbedEventPointer } from "..";
|
||||||
|
|
||||||
|
function EventTag({ tag }: { tag: Tag }) {
|
||||||
|
const expand = useDisclosure();
|
||||||
|
const content = `[${tag[0]}] ${tag.slice(1).join(", ")}`;
|
||||||
|
const props = {
|
||||||
|
fontWeight: "bold",
|
||||||
|
fontFamily: "monospace",
|
||||||
|
fontSize: "1.2em",
|
||||||
|
isTruncated: true,
|
||||||
|
color: "GrayText",
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggle = useCallback<MouseEventHandler>(
|
||||||
|
(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
expand.onToggle();
|
||||||
|
},
|
||||||
|
[expand.onToggle],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isETag(tag) && tag[1]) {
|
||||||
|
const pointer = eTagToEventPointer(tag);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Link as={RouterLink} to={`/l/${nip19.neventEncode(pointer)}`} onClick={toggle} {...props}>
|
||||||
|
{content}
|
||||||
|
</Link>
|
||||||
|
{expand.isOpen && <EmbedEventPointer pointer={{ type: "nevent", data: pointer }} />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else if (isATag(tag) && tag[1]) {
|
||||||
|
const pointer = aTagToAddressPointer(tag);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Link as={RouterLink} to={`/l/${nip19.naddrEncode(pointer)}`} onClick={toggle} {...props}>
|
||||||
|
{content}
|
||||||
|
</Link>
|
||||||
|
{expand.isOpen && <EmbedEventPointer pointer={{ type: "naddr", data: pointer }} />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else if (isPTag(tag) && tag[1]) {
|
||||||
|
const pubkey = tag[1];
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Link as={RouterLink} to={`/l/${nip19.npubEncode(pubkey)}`} onClick={toggle} {...props}>
|
||||||
|
{content}
|
||||||
|
</Link>
|
||||||
|
{expand.isOpen && (
|
||||||
|
<Flex gap="4" p="2">
|
||||||
|
<UserAvatarLink pubkey={pubkey} />
|
||||||
|
<Box>
|
||||||
|
<UserLink pubkey={pubkey} fontWeight="bold" />
|
||||||
|
<br />
|
||||||
|
<UserDnsIdentityIcon pubkey={pubkey} />
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else
|
||||||
|
return (
|
||||||
|
<Text title={content} {...props}>
|
||||||
|
{content}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function EmbeddedUnknown({ event, ...props }: Omit<CardProps, "children"> & { event: NostrEvent }) {
|
export default function EmbeddedUnknown({ event, ...props }: Omit<CardProps, "children"> & { event: NostrEvent }) {
|
||||||
const debugModal = useDisclosure();
|
const debugModal = useDisclosure();
|
||||||
@@ -29,16 +111,15 @@ export default function EmbeddedUnknown({ event, ...props }: Omit<CardProps, "ch
|
|||||||
|
|
||||||
const alt = event.tags.find((t) => t[0] === "alt")?.[1];
|
const alt = event.tags.find((t) => t[0] === "alt")?.[1];
|
||||||
const content = useMemo(() => {
|
const content = useMemo(() => {
|
||||||
let jsx: EmbedableContent = [alt || event.content];
|
let jsx: EmbedableContent = [event.content];
|
||||||
jsx = embedNostrLinks(jsx);
|
jsx = embedNostrLinks(jsx);
|
||||||
jsx = embedNostrMentions(jsx, event);
|
|
||||||
jsx = embedNostrHashtags(jsx, event);
|
jsx = embedNostrHashtags(jsx, event);
|
||||||
jsx = embedEmoji(jsx, event);
|
jsx = embedEmoji(jsx, event);
|
||||||
|
|
||||||
jsx = embedUrls(jsx, [renderImageUrl, renderVideoUrl, renderAudioUrl, renderGenericUrl]);
|
jsx = embedUrls(jsx, [renderImageUrl, renderVideoUrl, renderAudioUrl, renderGenericUrl]);
|
||||||
|
|
||||||
return jsx;
|
return jsx;
|
||||||
}, [event.content, alt]);
|
}, [event.content]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -47,23 +128,41 @@ export default function EmbeddedUnknown({ event, ...props }: Omit<CardProps, "ch
|
|||||||
<UserAvatarLink pubkey={event.pubkey} size="xs" />
|
<UserAvatarLink pubkey={event.pubkey} size="xs" />
|
||||||
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="md" />
|
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="md" />
|
||||||
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
|
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
|
||||||
<Link ml="auto" href={address ? buildAppSelectUrl(address) : ""} isExternal>
|
<Text>kind: {event.kind}</Text>
|
||||||
<Timestamp timestamp={event.created_at} />
|
<Timestamp timestamp={event.created_at} />
|
||||||
</Link>
|
<ButtonGroup ml="auto">
|
||||||
|
<Button
|
||||||
|
as={Link}
|
||||||
|
size="sm"
|
||||||
|
leftIcon={<ExternalLinkIcon />}
|
||||||
|
isExternal
|
||||||
|
href={address ? buildAppSelectUrl(address) : ""}
|
||||||
|
>
|
||||||
|
Open
|
||||||
|
</Button>
|
||||||
|
<IconButton
|
||||||
|
icon={<CodeIcon />}
|
||||||
|
aria-label="Raw Event"
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={debugModal.onOpen}
|
||||||
|
/>
|
||||||
|
</ButtonGroup>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody p="2">
|
<CardBody p="2">
|
||||||
<Flex gap="2">
|
{alt && (
|
||||||
<Text>Kind: {event.kind}</Text>
|
<Text isTruncated fontStyle="italic">
|
||||||
<Link href={address ? buildAppSelectUrl(address) : ""} isExternal color="blue.500">
|
{alt}
|
||||||
{address && truncatedId(address)}
|
</Text>
|
||||||
</Link>
|
)}
|
||||||
<Button leftIcon={<CodeIcon />} ml="auto" size="sm" variant="outline" onClick={debugModal.onOpen}>
|
<Box whiteSpace="pre-wrap" noOfLines={3}>
|
||||||
View Raw
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
<Box whiteSpace="pre-wrap" noOfLines={5}>
|
|
||||||
{content}
|
{content}
|
||||||
</Box>
|
</Box>
|
||||||
|
<Flex direction="column" gap="1" px="2" my="2">
|
||||||
|
{event.tags.map((tag, i) => (
|
||||||
|
<EventTag key={i} tag={tag} />
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
{debugModal.isOpen && <NoteDebugModal isOpen={debugModal.isOpen} onClose={debugModal.onClose} event={event} />}
|
{debugModal.isOpen && <NoteDebugModal isOpen={debugModal.isOpen} onClose={debugModal.onClose} event={event} />}
|
||||||
|
@@ -60,7 +60,7 @@ export default function DatabaseSettings() {
|
|||||||
</h2>
|
</h2>
|
||||||
<AccordionPanel>
|
<AccordionPanel>
|
||||||
<DatabaseStats />
|
<DatabaseStats />
|
||||||
<ButtonGroup>
|
<ButtonGroup mt="2">
|
||||||
<Button onClick={handleClearData} isLoading={clearing} isDisabled={clearing}>
|
<Button onClick={handleClearData} isLoading={clearing} isDisabled={clearing}>
|
||||||
Clear cache data
|
Clear cache data
|
||||||
</Button>
|
</Button>
|
||||||
|
Reference in New Issue
Block a user