make list card component more compact

hide empty lists events created by other clients
only show list favorite button for people lists
This commit is contained in:
hzrd149 2023-09-18 16:21:48 -05:00
parent 2d41abf44a
commit af973af93e
6 changed files with 57 additions and 24 deletions

View File

@ -18,6 +18,12 @@ export function getListName(event: NostrEvent) {
); );
} }
export function isJunkList(event: NostrEvent) {
const name = event.tags.find(isDTag)?.[1];
if (!name) return false;
if (event.kind !== PEOPLE_LIST_KIND) return false;
return /^(chats\/([0-9a-f]{64}|null)|notifications)\/lastOpened$/.test(name);
}
export function isSpecialListKind(kind: number) { export function isSpecialListKind(kind: number) {
return kind === Kind.Contacts || kind === PIN_LIST_KIND || kind === MUTE_LIST_KIND; return kind === Kind.Contacts || kind === PIN_LIST_KIND || kind === MUTE_LIST_KIND;
} }

View File

@ -9,6 +9,8 @@ export default function useFavoriteLists() {
const account = useCurrentAccount(); const account = useCurrentAccount();
const favoriteList = useReplaceableEvent( const favoriteList = useReplaceableEvent(
account ? { kind: 30078, pubkey: account.pubkey, identifier: FAVORITE_LISTS_IDENTIFIER } : undefined, account ? { kind: 30078, pubkey: account.pubkey, identifier: FAVORITE_LISTS_IDENTIFIER } : undefined,
[],
true,
); );
const lists = useReplaceableEvents(favoriteList ? getCoordinatesFromList(favoriteList).map((a) => a.coordinate) : []); const lists = useReplaceableEvents(favoriteList ? getCoordinatesFromList(favoriteList).map((a) => a.coordinate) : []);

View File

@ -1,10 +1,15 @@
import { NOTE_LIST_KIND, PEOPLE_LIST_KIND } from "../helpers/nostr/lists"; import { useCallback } from "react";
import { NOTE_LIST_KIND, PEOPLE_LIST_KIND, isJunkList } from "../helpers/nostr/lists";
import { useReadRelayUrls } from "./use-client-relays"; import { useReadRelayUrls } from "./use-client-relays";
import useSubject from "./use-subject"; import useSubject from "./use-subject";
import useTimelineLoader from "./use-timeline-loader"; import useTimelineLoader from "./use-timeline-loader";
import { NostrEvent } from "../types/nostr-event";
export default function useUserLists(pubkey?: string, additionalRelays: string[] = []) { export default function useUserLists(pubkey?: string, additionalRelays: string[] = []) {
const readRelays = useReadRelayUrls(additionalRelays); const readRelays = useReadRelayUrls(additionalRelays);
const eventFilter = useCallback((event: NostrEvent) => {
return !isJunkList(event);
}, []);
const timeline = useTimelineLoader( const timeline = useTimelineLoader(
`${pubkey}-lists`, `${pubkey}-lists`,
readRelays, readRelays,
@ -12,7 +17,7 @@ export default function useUserLists(pubkey?: string, additionalRelays: string[]
authors: pubkey ? [pubkey] : [], authors: pubkey ? [pubkey] : [],
kinds: [PEOPLE_LIST_KIND, NOTE_LIST_KIND], kinds: [PEOPLE_LIST_KIND, NOTE_LIST_KIND],
}, },
{ enabled: !!pubkey }, { enabled: !!pubkey, eventFilter },
); );
return useSubject(timeline.timeline); return useSubject(timeline.timeline);

View File

@ -55,20 +55,11 @@ function ListCardRender({ event, ...props }: Omit<CardProps, "children"> & { eve
{getListName(event)} {getListName(event)}
</Link> </Link>
</Heading> </Heading>
<ButtonGroup size="sm" ml="auto"> <Link as={RouterLink} to={`/lists/${link}`} ml="auto">
<ListFavoriteButton list={event} /> <Timestamp timestamp={event.created_at} />
<ListMenu list={event} aria-label="list menu" /> </Link>
</ButtonGroup>
</CardHeader> </CardHeader>
<CardBody p="2"> <CardBody py="0" px="2">
<Flex gap="2">
<Text>Created by:</Text>
<UserAvatarLink pubkey={event.pubkey} size="xs" />
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
</Flex>
<Text>
Updated: <Timestamp timestamp={event.created_at} />
</Text>
{people.length > 0 && ( {people.length > 0 && (
<> <>
<Text>People ({people.length}):</Text> <Text>People ({people.length}):</Text>
@ -83,7 +74,7 @@ function ListCardRender({ event, ...props }: Omit<CardProps, "children"> & { eve
<> <>
<Text>Notes ({notes.length}):</Text> <Text>Notes ({notes.length}):</Text>
<Flex gap="2" overflow="hidden"> <Flex gap="2" overflow="hidden">
{notes.map(({ id, relay }) => ( {notes.slice(0, 4).map(({ id, relay }) => (
<NoteLink key={id} noteId={id} /> <NoteLink key={id} noteId={id} />
))} ))}
</Flex> </Flex>
@ -92,11 +83,24 @@ function ListCardRender({ event, ...props }: Omit<CardProps, "children"> & { eve
{references.length > 0 && ( {references.length > 0 && (
<> <>
<Text>References ({references.length})</Text> <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>
</> </>
)} )}
</CardBody> </CardBody>
<CardFooter p="2" display="flex" pt="0"> <CardFooter p="2" display="flex" alignItems="center" whiteSpace="pre" gap="2">
<EventRelays event={event} ml="auto" /> <Text>Created by:</Text>
<UserAvatarLink pubkey={event.pubkey} size="xs" />
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
<ButtonGroup size="xs" variant="ghost" ml="auto">
<ListFavoriteButton list={event} />
<ListMenu list={event} aria-label="list menu" />
</ButtonGroup>
</CardFooter> </CardFooter>
</Card> </Card>
); );

View File

@ -10,6 +10,7 @@ import NostrPublishAction from "../../../classes/nostr-publish-action";
import clientRelaysService from "../../../services/client-relays"; import clientRelaysService from "../../../services/client-relays";
import replaceableEventLoaderService from "../../../services/replaceable-event-requester"; import replaceableEventLoaderService from "../../../services/replaceable-event-requester";
import useFavoriteLists, { FAVORITE_LISTS_IDENTIFIER } from "../../../hooks/use-favorite-lists"; import useFavoriteLists, { FAVORITE_LISTS_IDENTIFIER } from "../../../hooks/use-favorite-lists";
import { NOTE_LIST_KIND, isSpecialListKind } from "../../../helpers/nostr/lists";
export default function ListFavoriteButton({ export default function ListFavoriteButton({
list, list,
@ -22,6 +23,11 @@ export default function ListFavoriteButton({
const isFavorite = favoriteList?.tags.some((t) => t[1] === coordinate); const isFavorite = favoriteList?.tags.some((t) => t[1] === coordinate);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
if (isSpecialListKind(list.kind)) return null;
// NOTE: dont show favorite button for note lists
if (list.kind === NOTE_LIST_KIND) return null;
const handleClick = async () => { const handleClick = async () => {
const prev: DraftNostrEvent = favoriteList || { const prev: DraftNostrEvent = favoriteList || {
kind: 30078, kind: 30078,

View File

@ -1,25 +1,35 @@
import { useCallback } from "react";
import { useOutletContext } from "react-router-dom"; import { useOutletContext } from "react-router-dom";
import { Divider, Flex, Heading, SimpleGrid } from "@chakra-ui/react"; import { Divider, Heading, SimpleGrid } from "@chakra-ui/react";
import { useAdditionalRelayContext } from "../../providers/additional-relay-context"; import { useAdditionalRelayContext } from "../../providers/additional-relay-context";
import useTimelineLoader from "../../hooks/use-timeline-loader"; import useTimelineLoader from "../../hooks/use-timeline-loader";
import useSubject from "../../hooks/use-subject"; import useSubject from "../../hooks/use-subject";
import { MUTE_LIST_KIND, NOTE_LIST_KIND, PEOPLE_LIST_KIND, PIN_LIST_KIND } from "../../helpers/nostr/lists"; import { MUTE_LIST_KIND, NOTE_LIST_KIND, PEOPLE_LIST_KIND, PIN_LIST_KIND, isJunkList } from "../../helpers/nostr/lists";
import { getEventUID } from "../../helpers/nostr/events"; import { getEventUID } from "../../helpers/nostr/events";
import ListCard from "../lists/components/list-card"; import ListCard from "../lists/components/list-card";
import IntersectionObserverProvider from "../../providers/intersection-observer"; import IntersectionObserverProvider from "../../providers/intersection-observer";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback"; import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import { Kind } from "nostr-tools"; import { Kind } from "nostr-tools";
import VerticalPageLayout from "../../components/vertical-page-layout"; import VerticalPageLayout from "../../components/vertical-page-layout";
import { NostrEvent } from "../../types/nostr-event";
export default function UserListsTab() { export default function UserListsTab() {
const { pubkey } = useOutletContext() as { pubkey: string }; const { pubkey } = useOutletContext() as { pubkey: string };
const readRelays = useAdditionalRelayContext(); const readRelays = useAdditionalRelayContext();
const timeline = useTimelineLoader(pubkey + "-lists", readRelays, { const eventFilter = useCallback((event: NostrEvent) => {
authors: [pubkey], return !isJunkList(event);
kinds: [PEOPLE_LIST_KIND, NOTE_LIST_KIND], }, []);
}); const timeline = useTimelineLoader(
pubkey + "-lists",
readRelays,
{
authors: [pubkey],
kinds: [PEOPLE_LIST_KIND, NOTE_LIST_KIND],
},
{ eventFilter },
);
const lists = useSubject(timeline.timeline); const lists = useSubject(timeline.timeline);
const callback = useTimelineCurserIntersectionCallback(timeline); const callback = useTimelineCurserIntersectionCallback(timeline);