Improve display of unknown events

This commit is contained in:
hzrd149 2024-01-12 21:08:09 +00:00
parent 5ba0bc4f0f
commit 7640beb0c2
3 changed files with 127 additions and 23 deletions

View File

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Improve display of unknown events

View File

@ -1,27 +1,109 @@
import { useMemo } from "react";
import { Box, Button, Card, CardBody, CardHeader, CardProps, Flex, Link, Text, useDisclosure } from "@chakra-ui/react";
import { MouseEventHandler, useCallback, useMemo } from "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 { NostrEvent } from "../../../types/nostr-event";
import { NostrEvent, Tag, isATag, isETag, isPTag } from "../../../types/nostr-event";
import UserAvatarLink from "../../user-avatar-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 { UserDnsIdentityIcon } from "../../user-dns-identity-icon";
import {
embedEmoji,
embedNostrHashtags,
embedNostrLinks,
embedNostrMentions,
renderGenericUrl,
renderImageUrl,
renderVideoUrl,
} from "../../embed-types";
import { EmbedableContent, embedUrls } from "../../../helpers/embeds";
import Timestamp from "../../timestamp";
import { CodeIcon } from "../../icons";
import { CodeIcon, ExternalLinkIcon } from "../../icons";
import NoteDebugModal from "../../debug-modals/note-debug-modal";
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 }) {
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 content = useMemo(() => {
let jsx: EmbedableContent = [alt || event.content];
let jsx: EmbedableContent = [event.content];
jsx = embedNostrLinks(jsx);
jsx = embedNostrMentions(jsx, event);
jsx = embedNostrHashtags(jsx, event);
jsx = embedEmoji(jsx, event);
jsx = embedUrls(jsx, [renderImageUrl, renderVideoUrl, renderAudioUrl, renderGenericUrl]);
return jsx;
}, [event.content, alt]);
}, [event.content]);
return (
<>
@ -47,23 +128,41 @@ export default function EmbeddedUnknown({ event, ...props }: Omit<CardProps, "ch
<UserAvatarLink pubkey={event.pubkey} size="xs" />
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="md" />
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
<Link ml="auto" href={address ? buildAppSelectUrl(address) : ""} isExternal>
<Timestamp timestamp={event.created_at} />
</Link>
<Text>kind: {event.kind}</Text>
<Timestamp timestamp={event.created_at} />
<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>
<CardBody p="2">
<Flex gap="2">
<Text>Kind: {event.kind}</Text>
<Link href={address ? buildAppSelectUrl(address) : ""} isExternal color="blue.500">
{address && truncatedId(address)}
</Link>
<Button leftIcon={<CodeIcon />} ml="auto" size="sm" variant="outline" onClick={debugModal.onOpen}>
View Raw
</Button>
</Flex>
<Box whiteSpace="pre-wrap" noOfLines={5}>
{alt && (
<Text isTruncated fontStyle="italic">
{alt}
</Text>
)}
<Box whiteSpace="pre-wrap" noOfLines={3}>
{content}
</Box>
<Flex direction="column" gap="1" px="2" my="2">
{event.tags.map((tag, i) => (
<EventTag key={i} tag={tag} />
))}
</Flex>
</CardBody>
</Card>
{debugModal.isOpen && <NoteDebugModal isOpen={debugModal.isOpen} onClose={debugModal.onClose} event={event} />}

View File

@ -60,7 +60,7 @@ export default function DatabaseSettings() {
</h2>
<AccordionPanel>
<DatabaseStats />
<ButtonGroup>
<ButtonGroup mt="2">
<Button onClick={handleClearData} isLoading={clearing} isDisabled={clearing}>
Clear cache data
</Button>