Show list embeds in notes

This commit is contained in:
hzrd149 2023-09-08 11:16:19 -05:00
parent 3d5d23407d
commit 6b4fd8ab42
7 changed files with 97 additions and 28 deletions

View File

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Show list embeds in notes

View File

@ -0,0 +1,51 @@
import { AvatarGroup, Card, CardBody, CardHeader, CardProps, Flex, Heading, Link, Text } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { NostrEvent } from "../../../types/nostr-event";
import { getEventsFromList, getListName, getPubkeysFromList, isSpecialListKind } from "../../../helpers/nostr/lists";
import { createCoordinate } from "../../../services/replaceable-event-requester";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { UserAvatarLink } from "../../user-avatar-link";
import { UserLink } from "../../user-link";
import { NoteLink } from "../../note-link";
import ListFeedButton from "../../../views/lists/components/list-feed-button";
export default function EmbeddedList({ list: list, ...props }: Omit<CardProps, "children"> & { list: NostrEvent }) {
const people = getPubkeysFromList(list);
const notes = getEventsFromList(list);
const link = isSpecialListKind(list.kind) ? createCoordinate(list.kind, list.pubkey) : getSharableEventAddress(list);
return (
<Card {...props}>
<CardHeader display="flex" alignItems="center" p="2" pb="0" gap="2">
<Heading size="md">
<Link as={RouterLink} to={`/lists/${link}`}>
{getListName(list)}
</Link>
</Heading>
<ListFeedButton list={list} ml="auto" size="sm" />
</CardHeader>
<CardBody p="2">
<Flex gap="2">
<Text>Created by:</Text>
<UserAvatarLink pubkey={list.pubkey} size="xs" />
<UserLink pubkey={list.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
</Flex>
{people.length > 0 && (
<AvatarGroup overflow="hidden" mb="2" max={16} size="sm">
{people.map(({ pubkey, relay }) => (
<UserAvatarLink key={pubkey} pubkey={pubkey} relay={relay} />
))}
</AvatarGroup>
)}
{notes.length > 0 && (
<Flex gap="2" overflow="hidden">
{notes.map(({ id, relay }) => (
<NoteLink key={id} noteId={id} />
))}
</Flex>
)}
</CardBody>
</Card>
);
}

View File

@ -15,6 +15,8 @@ import { EMOJI_PACK_KIND } from "../../helpers/nostr/emoji-packs";
import EmbeddedEmojiPack from "./event-types/embedded-emoji-pack"; import EmbeddedEmojiPack from "./event-types/embedded-emoji-pack";
import EmbeddedGoal, { EmbeddedGoalOptions } from "./event-types/embedded-goal"; import EmbeddedGoal, { EmbeddedGoalOptions } from "./event-types/embedded-goal";
import EmbeddedUnknown from "./event-types/embedded-unknown"; import EmbeddedUnknown from "./event-types/embedded-unknown";
import { NOTE_LIST_KIND, PEOPLE_LIST_KIND } from "../../helpers/nostr/lists";
import EmbeddedList from "./event-types/embedded-list";
export type EmbedProps = { export type EmbedProps = {
goalProps?: EmbeddedGoalOptions; goalProps?: EmbeddedGoalOptions;
@ -30,6 +32,9 @@ export function EmbedEvent({ event, goalProps }: { event: NostrEvent } & EmbedPr
return <EmbeddedGoal goal={event} {...goalProps} />; return <EmbeddedGoal goal={event} {...goalProps} />;
case EMOJI_PACK_KIND: case EMOJI_PACK_KIND:
return <EmbeddedEmojiPack pack={event} />; return <EmbeddedEmojiPack pack={event} />;
case PEOPLE_LIST_KIND:
case NOTE_LIST_KIND:
return <EmbeddedList list={event} />;
} }
return <EmbeddedUnknown event={event} />; return <EmbeddedUnknown event={event} />;

View File

@ -20,6 +20,7 @@ import clientRelaysService from "../../../services/client-relays";
import QuoteNote from "../quote-note"; import QuoteNote from "../quote-note";
import NostrPublishAction from "../../../classes/nostr-publish-action"; import NostrPublishAction from "../../../classes/nostr-publish-action";
import { useSigningContext } from "../../../providers/signing-provider"; import { useSigningContext } from "../../../providers/signing-provider";
import { EmbedEvent } from "../../embed-event";
export function RepostButton({ event }: { event: NostrEvent }) { export function RepostButton({ event }: { event: NostrEvent }) {
const { isOpen, onClose, onOpen } = useDisclosure(); const { isOpen, onClose, onOpen } = useDisclosure();
@ -51,7 +52,7 @@ export function RepostButton({ event }: { event: NostrEvent }) {
isLoading={loading} isLoading={loading}
/> />
{isOpen && ( {isOpen && (
<Modal isOpen={isOpen} onClose={onClose}> <Modal isOpen={isOpen} onClose={onClose} size="2xl">
<ModalOverlay /> <ModalOverlay />
<ModalContent> <ModalContent>
<ModalHeader px="4" py="2"> <ModalHeader px="4" py="2">
@ -59,7 +60,7 @@ export function RepostButton({ event }: { event: NostrEvent }) {
</ModalHeader> </ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody px="4" py="0"> <ModalBody px="4" py="0">
<QuoteNote noteId={event.id} /> <EmbedEvent event={event} />
</ModalBody> </ModalBody>
<ModalFooter px="4" py="4"> <ModalFooter px="4" py="4">

View File

@ -7,7 +7,6 @@ import {
AccordionPanel, AccordionPanel,
Box, Box,
Button, Button,
Code,
Flex, Flex,
Input, Input,
Link, Link,
@ -21,12 +20,11 @@ import {
Text, Text,
useToast, useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { Event, Kind, nip19 } from "nostr-tools"; import { Event, Kind } from "nostr-tools";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { useCurrentAccount } from "../hooks/use-current-account"; import { useCurrentAccount } from "../hooks/use-current-account";
import signingService from "../services/signing"; import signingService from "../services/signing";
import QuoteNote from "../components/note/quote-note";
import createDefer, { Deferred } from "../classes/deferred"; import createDefer, { Deferred } from "../classes/deferred";
import useEventRelays from "../hooks/use-event-relays"; import useEventRelays from "../hooks/use-event-relays";
import { useWriteRelayUrls } from "../hooks/use-client-relays"; import { useWriteRelayUrls } from "../hooks/use-client-relays";
@ -36,6 +34,7 @@ import { getEventCoordinate, getEventUID, isReplaceable } from "../helpers/nostr
import NostrPublishAction from "../classes/nostr-publish-action"; import NostrPublishAction from "../classes/nostr-publish-action";
import { Tag } from "../types/nostr-event"; import { Tag } from "../types/nostr-event";
import deleteEventService from "../services/delete-events"; import deleteEventService from "../services/delete-events";
import { EmbedEvent } from "../components/embed-event";
type DeleteEventContextType = { type DeleteEventContextType = {
isLoading: boolean; isLoading: boolean;
@ -51,13 +50,6 @@ export function useDeleteEventContext() {
return useContext(DeleteEventContext); return useContext(DeleteEventContext);
} }
function EventPreview({ event }: { event: Event }) {
if (event.kind === Kind.Text) {
return <QuoteNote noteId={event.id} />;
}
return <Code>{nip19.noteEncode(event.id)}</Code>;
}
export default function DeleteEventProvider({ children }: PropsWithChildren) { export default function DeleteEventProvider({ children }: PropsWithChildren) {
const toast = useToast(); const toast = useToast();
const account = useCurrentAccount(); const account = useCurrentAccount();
@ -124,11 +116,11 @@ export default function DeleteEventProvider({ children }: PropsWithChildren) {
<ModalOverlay /> <ModalOverlay />
<ModalContent> <ModalContent>
<ModalHeader px="4" py="2"> <ModalHeader px="4" py="2">
Delete Note? Delete Event?
</ModalHeader> </ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody px="4" py="0"> <ModalBody px="4" py="0">
<EventPreview event={event} /> <EmbedEvent event={event} />
<Input <Input
name="reason" name="reason"
value={reason} value={reason}

View File

@ -0,0 +1,23 @@
import { Button, ButtonProps } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { Kind } from "nostr-tools";
import { NostrEvent } from "../../../types/nostr-event";
import { getEventCoordinate } from "../../../helpers/nostr/events";
import { PEOPLE_LIST_KIND } from "../../../helpers/nostr/lists";
export default function ListFeedButton({ list, ...props }: { list: NostrEvent } & Omit<ButtonProps, "children">) {
const shouldShowFeedButton = list.kind === PEOPLE_LIST_KIND || list.kind === Kind.Contacts;
if (!shouldShowFeedButton) return null;
return (
<Button
as={RouterLink}
to={{ pathname: "/", search: new URLSearchParams({ people: getEventCoordinate(list) }).toString() }}
{...props}
>
View Feed
</Button>
);
}

View File

@ -1,14 +1,13 @@
import { Link as RouterList, useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { Kind, nip19 } from "nostr-tools"; import { nip19 } from "nostr-tools";
import { Link as RouterLink } from "react-router-dom";
import { UserLink } from "../../components/user-link"; import { UserLink } from "../../components/user-link";
import { Button, Divider, Flex, Heading, SimpleGrid, Spacer } from "@chakra-ui/react"; import { Button, Divider, Flex, Heading, SimpleGrid, Spacer } from "@chakra-ui/react";
import { ArrowLeftSIcon } from "../../components/icons"; import { ArrowLeftSIcon } from "../../components/icons";
import { useCurrentAccount } from "../../hooks/use-current-account"; import { useCurrentAccount } from "../../hooks/use-current-account";
import { useDeleteEventContext } from "../../providers/delete-event-provider"; import { useDeleteEventContext } from "../../providers/delete-event-provider";
import { getEventCoordinate, parseCoordinate } from "../../helpers/nostr/events"; import { parseCoordinate } from "../../helpers/nostr/events";
import { PEOPLE_LIST_KIND, getEventsFromList, getListName, getPubkeysFromList } from "../../helpers/nostr/lists"; import { getEventsFromList, getListName, getPubkeysFromList } from "../../helpers/nostr/lists";
import useReplaceableEvent from "../../hooks/use-replaceable-event"; import useReplaceableEvent from "../../hooks/use-replaceable-event";
import { EventRelays } from "../../components/note/note-relays"; import { EventRelays } from "../../components/note/note-relays";
import UserCard from "./components/user-card"; import UserCard from "./components/user-card";
@ -16,6 +15,7 @@ import NoteCard from "./components/note-card";
import { TrustProvider } from "../../providers/trust"; import { TrustProvider } from "../../providers/trust";
import ListMenu from "./components/list-menu"; import ListMenu from "./components/list-menu";
import ListFavoriteButton from "./components/list-favorite-button"; import ListFavoriteButton from "./components/list-favorite-button";
import ListFeedButton from "./components/list-feed-button";
function useListCoordinate() { function useListCoordinate() {
const { addr } = useParams() as { addr: string }; const { addr } = useParams() as { addr: string };
@ -47,7 +47,6 @@ export default function ListDetailsView() {
); );
const isAuthor = account?.pubkey === event.pubkey; const isAuthor = account?.pubkey === event.pubkey;
const shouldShowFeedButton = event.kind === PEOPLE_LIST_KIND || event.kind === Kind.Contacts;
const people = getPubkeysFromList(event); const people = getPubkeysFromList(event);
const notes = getEventsFromList(event); const notes = getEventsFromList(event);
@ -67,14 +66,7 @@ export default function ListDetailsView() {
<EventRelays event={event} /> <EventRelays event={event} />
{shouldShowFeedButton && ( <ListFeedButton list={event} />
<Button
as={RouterLink}
to={{ pathname: "/", search: new URLSearchParams({ people: getEventCoordinate(event) }).toString() }}
>
View Feed
</Button>
)}
{isAuthor && ( {isAuthor && (
<Button colorScheme="red" onClick={() => deleteEvent(event).then(() => navigate("/lists"))}> <Button colorScheme="red" onClick={() => deleteEvent(event).then(() => navigate("/lists"))}>
Delete Delete