mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-07 03:18:02 +02:00
Show articles in lists
This commit is contained in:
parent
4a7c00b0d0
commit
2a17d9ecf4
5
.changeset/twelve-chefs-kiss.md
Normal file
5
.changeset/twelve-chefs-kiss.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"nostrudel": minor
|
||||
---
|
||||
|
||||
Show articles in lists
|
@ -1,18 +1,16 @@
|
||||
import { AvatarGroup, Card, CardBody, CardHeader, CardProps, Flex, Heading, Link, Text } from "@chakra-ui/react";
|
||||
import { 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 { getListName, 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";
|
||||
import { ListCardContent } from "../../../views/lists/components/list-card";
|
||||
|
||||
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 (
|
||||
@ -31,20 +29,7 @@ export default function EmbeddedList({ list: list, ...props }: Omit<CardProps, "
|
||||
<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>
|
||||
)}
|
||||
<ListCardContent list={list} />
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
|
@ -11,9 +11,10 @@ import {
|
||||
Flex,
|
||||
Heading,
|
||||
Link,
|
||||
LinkProps,
|
||||
Text,
|
||||
} from "@chakra-ui/react";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { Kind, nip19 } from "nostr-tools";
|
||||
|
||||
import { UserAvatarLink } from "../../../components/user-avatar-link";
|
||||
import { UserLink } from "../../../components/user-link";
|
||||
@ -36,13 +37,99 @@ import { getEventUID } from "../../../helpers/nostr/events";
|
||||
import ListMenu from "./list-menu";
|
||||
import Timestamp from "../../../components/timestamp";
|
||||
import { COMMUNITY_DEFINITION_KIND } from "../../../helpers/nostr/communities";
|
||||
import { getArticleTitle } from "../../../helpers/nostr/long-form";
|
||||
import { buildAppSelectUrl } from "../../../helpers/nostr/apps";
|
||||
|
||||
function ListCardRender({ list, ...props }: Omit<CardProps, "children"> & { list: NostrEvent }) {
|
||||
function ArticleLinkLoader({ pointer, ...props }: { pointer: nip19.AddressPointer } & Omit<LinkProps, "children">) {
|
||||
const article = useReplaceableEvent(pointer);
|
||||
if (article) return <ArticleLink article={article} {...props} />;
|
||||
return null;
|
||||
}
|
||||
function ArticleLink({ article, ...props }: { article: NostrEvent } & Omit<LinkProps, "children">) {
|
||||
const title = getArticleTitle(article);
|
||||
const naddr = getSharableEventAddress(article);
|
||||
|
||||
return (
|
||||
<Link href={naddr ? buildAppSelectUrl(naddr, false) : undefined} isExternal color="blue.500" {...props}>
|
||||
{title}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export function ListCardContent({ list, ...props }: Omit<CardProps, "children"> & { list: NostrEvent }) {
|
||||
const people = getPubkeysFromList(list);
|
||||
const notes = getEventsFromList(list);
|
||||
const coordinates = getParsedCordsFromList(list);
|
||||
const communities = coordinates.filter((cord) => cord.kind === COMMUNITY_DEFINITION_KIND);
|
||||
const articles = coordinates.filter((cord) => cord.kind === Kind.Article);
|
||||
const references = getReferencesFromList(list);
|
||||
|
||||
return (
|
||||
<>
|
||||
{people.length > 0 && (
|
||||
<>
|
||||
<Text>People ({people.length}):</Text>
|
||||
<AvatarGroup overflow="hidden" mb="2" max={16} size="sm">
|
||||
{people.map(({ pubkey, relay }) => (
|
||||
<UserAvatarLink key={pubkey} pubkey={pubkey} relay={relay} />
|
||||
))}
|
||||
</AvatarGroup>
|
||||
</>
|
||||
)}
|
||||
{notes.length > 0 && (
|
||||
<>
|
||||
<Text>Notes ({notes.length}):</Text>
|
||||
<Flex gap="2" overflow="hidden">
|
||||
{notes.slice(0, 4).map(({ id, relay }) => (
|
||||
<NoteLink key={id} noteId={id} />
|
||||
))}
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{references.length > 0 && (
|
||||
<>
|
||||
<Text>References ({references.length})</Text>
|
||||
<Flex gap="2" overflow="hidden">
|
||||
{references.slice(0, 3).map(({ url, petname }) => (
|
||||
<Link maxW="200" href={url} isExternal whiteSpace="pre" color="blue.500" isTruncated>
|
||||
{petname || url}
|
||||
</Link>
|
||||
))}
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{communities.length > 0 && (
|
||||
<>
|
||||
<Text>Communities ({communities.length}):</Text>
|
||||
<Flex gap="2" overflow="hidden">
|
||||
{communities.map((pointer) => (
|
||||
<Link
|
||||
key={JSON.stringify(pointer)}
|
||||
as={RouterLink}
|
||||
to={`/c/${pointer.identifier}/${nip19.npubEncode(pointer.pubkey)}`}
|
||||
color="blue.500"
|
||||
>
|
||||
{pointer.identifier}
|
||||
</Link>
|
||||
))}
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{articles.length > 0 && (
|
||||
<>
|
||||
<Text>Articles ({articles.length}):</Text>
|
||||
<Flex overflow="hidden" direction="column">
|
||||
{articles.slice(0, 4).map((pointer) => (
|
||||
<ArticleLinkLoader key={JSON.stringify(pointer)} pointer={pointer} isTruncated />
|
||||
))}
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ListCardRender({ list, ...props }: Omit<CardProps, "children"> & { list: NostrEvent }) {
|
||||
const link = isSpecialListKind(list.kind) ? createCoordinate(list.kind, list.pubkey) : getSharableEventAddress(list);
|
||||
|
||||
// if there is a parent intersection observer, register this card
|
||||
@ -62,54 +149,7 @@ function ListCardRender({ list, ...props }: Omit<CardProps, "children"> & { list
|
||||
</Link>
|
||||
</CardHeader>
|
||||
<CardBody py="0" px="2">
|
||||
{people.length > 0 && (
|
||||
<>
|
||||
<Text>People ({people.length}):</Text>
|
||||
<AvatarGroup overflow="hidden" mb="2" max={16} size="sm">
|
||||
{people.map(({ pubkey, relay }) => (
|
||||
<UserAvatarLink key={pubkey} pubkey={pubkey} relay={relay} />
|
||||
))}
|
||||
</AvatarGroup>
|
||||
</>
|
||||
)}
|
||||
{notes.length > 0 && (
|
||||
<>
|
||||
<Text>Notes ({notes.length}):</Text>
|
||||
<Flex gap="2" overflow="hidden">
|
||||
{notes.slice(0, 4).map(({ id, relay }) => (
|
||||
<NoteLink key={id} noteId={id} />
|
||||
))}
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{references.length > 0 && (
|
||||
<>
|
||||
<Text>References ({references.length})</Text>
|
||||
<Flex gap="2" overflow="hidden">
|
||||
{references.slice(0, 3).map(({ url, petname }) => (
|
||||
<Link maxW="200" href={url} isExternal whiteSpace="pre" color="blue.500" isTruncated>
|
||||
{petname || url}
|
||||
</Link>
|
||||
))}
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{communities.length > 0 && (
|
||||
<>
|
||||
<Text>Communities ({communities.length}):</Text>
|
||||
<Flex gap="2" overflow="hidden">
|
||||
{communities.map((pointer) => (
|
||||
<Link
|
||||
as={RouterLink}
|
||||
to={`/c/${pointer.identifier}/${nip19.npubEncode(pointer.pubkey)}`}
|
||||
color="blue.500"
|
||||
>
|
||||
{pointer.identifier}
|
||||
</Link>
|
||||
))}
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
<ListCardContent list={list} />
|
||||
</CardBody>
|
||||
<CardFooter p="2" display="flex" alignItems="center" whiteSpace="pre" gap="2">
|
||||
<Text>Created by:</Text>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { Kind, nip19 } from "nostr-tools";
|
||||
|
||||
import { UserLink } from "../../components/user-link";
|
||||
import { Button, Divider, Flex, Heading, SimpleGrid, Spacer } from "@chakra-ui/react";
|
||||
import { Button, Flex, Heading, SimpleGrid, Spacer } from "@chakra-ui/react";
|
||||
import { ArrowLeftSIcon } from "../../components/icons";
|
||||
import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||
import { useDeleteEventContext } from "../../providers/delete-event-provider";
|
||||
@ -26,6 +26,8 @@ import ListFeedButton from "./components/list-feed-button";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import { COMMUNITY_DEFINITION_KIND } from "../../helpers/nostr/communities";
|
||||
import { EmbedEventPointer } from "../../components/embed-event";
|
||||
import { encodePointer } from "../../helpers/nip19";
|
||||
import { DecodeResult } from "nostr-tools/lib/nip19";
|
||||
|
||||
function useListCoordinate() {
|
||||
const { addr } = useParams() as { addr: string };
|
||||
@ -61,6 +63,7 @@ export default function ListDetailsView() {
|
||||
const notes = getEventsFromList(list);
|
||||
const coordinates = getParsedCordsFromList(list);
|
||||
const communities = coordinates.filter((cord) => cord.kind === COMMUNITY_DEFINITION_KIND);
|
||||
const articles = coordinates.filter((cord) => cord.kind === Kind.Article);
|
||||
const references = getReferencesFromList(list);
|
||||
|
||||
return (
|
||||
@ -88,8 +91,7 @@ export default function ListDetailsView() {
|
||||
|
||||
{people.length > 0 && (
|
||||
<>
|
||||
<Heading size="md">People</Heading>
|
||||
<Divider />
|
||||
<Heading size="lg">People</Heading>
|
||||
<SimpleGrid columns={{ base: 1, lg: 2, xl: 3 }} spacing="2">
|
||||
{people.map(({ pubkey, relay }) => (
|
||||
<UserCard pubkey={pubkey} relay={relay} list={list} />
|
||||
@ -100,8 +102,7 @@ export default function ListDetailsView() {
|
||||
|
||||
{notes.length > 0 && (
|
||||
<>
|
||||
<Heading size="md">Notes</Heading>
|
||||
<Divider />
|
||||
<Heading size="lg">Notes</Heading>
|
||||
<TrustProvider trust>
|
||||
<Flex gap="2" direction="column">
|
||||
{notes.map(({ id, relay }) => (
|
||||
@ -114,8 +115,7 @@ export default function ListDetailsView() {
|
||||
|
||||
{references.length > 0 && (
|
||||
<>
|
||||
<Heading size="md">References</Heading>
|
||||
<Divider />
|
||||
<Heading size="lg">References</Heading>
|
||||
<TrustProvider trust>
|
||||
<Flex gap="2" direction="column">
|
||||
{references.map(({ url, petname }) => (
|
||||
@ -131,8 +131,7 @@ export default function ListDetailsView() {
|
||||
|
||||
{communities.length > 0 && (
|
||||
<>
|
||||
<Heading size="md">Communities</Heading>
|
||||
<Divider />
|
||||
<Heading size="lg">Communities</Heading>
|
||||
<SimpleGrid spacing="2" columns={{ base: 1, lg: 2 }}>
|
||||
{communities.map((pointer) => (
|
||||
<EmbedEventPointer key={nip19.naddrEncode(pointer)} pointer={{ type: "naddr", data: pointer }} />
|
||||
@ -140,6 +139,18 @@ export default function ListDetailsView() {
|
||||
</SimpleGrid>
|
||||
</>
|
||||
)}
|
||||
|
||||
{articles.length > 0 && (
|
||||
<>
|
||||
<Heading size="lg">Articles</Heading>
|
||||
<Flex gap="2" direction="column">
|
||||
{articles.map((pointer) => {
|
||||
const decode: DecodeResult = { type: "naddr", data: pointer };
|
||||
return <EmbedEventPointer key={encodePointer(decode)} pointer={decode} />;
|
||||
})}
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
</VerticalPageLayout>
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user