Skip to content

Commit

Permalink
Show articles in lists
Browse files Browse the repository at this point in the history
  • Loading branch information
hzrd149 committed Oct 4, 2023
1 parent 4a7c00b commit 2a17d9e
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 79 deletions.
5 changes: 5 additions & 0 deletions .changeset/twelve-chefs-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"nostrudel": minor
---

Show articles in lists
23 changes: 4 additions & 19 deletions src/components/embed-event/event-types/embedded-list.tsx
Original file line number Diff line number Diff line change
@@ -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 (
Expand All @@ -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>
);
Expand Down
140 changes: 90 additions & 50 deletions src/views/lists/components/list-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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
Expand All @@ -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>
Expand Down
31 changes: 21 additions & 10 deletions src/views/lists/list-details.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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 };
Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -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} />
Expand All @@ -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 }) => (
Expand All @@ -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 }) => (
Expand All @@ -131,15 +131,26 @@ 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 }} />
))}
</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>
);
}

0 comments on commit 2a17d9e

Please sign in to comment.