"use client"; import { useEffect, useMemo } from "react"; import Link from "next/link"; import BookmarkFormattedCreatedAt from "@/components/dashboard/bookmarks/BookmarkFormattedCreatedAt"; import { BookmarkMarkdownComponent } from "@/components/dashboard/bookmarks/BookmarkMarkdownComponent"; import FooterLinkURL from "@/components/dashboard/bookmarks/FooterLinkURL"; import { ActionButton } from "@/components/ui/action-button"; import { badgeVariants } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; import { api } from "@/lib/trpc"; import { cn } from "@/lib/utils"; import tailwindConfig from "@/tailwind.config"; import { Expand, FileIcon, ImageIcon } from "lucide-react"; import { useInView } from "react-intersection-observer"; import Masonry from "react-masonry-css"; import resolveConfig from "tailwindcss/resolveConfig"; import { BookmarkTypes, ZPublicBookmark, } from "@karakeep/shared/types/bookmarks"; import { ZCursor } from "@karakeep/shared/types/pagination"; function TagPill({ tag }: { tag: string }) { return (
{tag}
); } function BookmarkCard({ bookmark }: { bookmark: ZPublicBookmark }) { const renderContent = () => { switch (bookmark.content.type) { case BookmarkTypes.LINK: return (
{bookmark.bannerImageUrl && (
{/* oxlint-disable-next-line no-img-element */} {bookmark.title
)}
{bookmark.title}
); case BookmarkTypes.TEXT: return (
{bookmark.title && (

{bookmark.title}

)}
{{ id: bookmark.id, content: { text: bookmark.content.text, }, }} {{ id: bookmark.id, content: { text: bookmark.content.text, }, }}
); case BookmarkTypes.ASSET: return (
{bookmark.bannerImageUrl ? (
{/* oxlint-disable-next-line no-img-element */} {bookmark.title
) : (
{bookmark.content.assetType === "image" ? ( ) : ( )}
)}
{bookmark.title}
); } }; return ( {renderContent()} {/* Tags */} {bookmark.tags.length > 0 && (
{bookmark.tags.map((tag, index) => ( ))}
)} {/* Footer */}
{bookmark.content.type === BookmarkTypes.LINK && ( <> )}
); } function getBreakpointConfig() { const fullConfig = resolveConfig(tailwindConfig); const breakpointColumnsObj: { [key: number]: number; default: number } = { default: 3, }; breakpointColumnsObj[parseInt(fullConfig.theme.screens.lg)] = 2; breakpointColumnsObj[parseInt(fullConfig.theme.screens.md)] = 1; breakpointColumnsObj[parseInt(fullConfig.theme.screens.sm)] = 1; return breakpointColumnsObj; } export default function PublicBookmarkGrid({ bookmarks: initialBookmarks, nextCursor, list, }: { list: { id: string; name: string; description: string | null | undefined; icon: string; numItems: number; ownerName: string; }; bookmarks: ZPublicBookmark[]; nextCursor: ZCursor | null; }) { const { ref: loadMoreRef, inView: loadMoreButtonInView } = useInView(); const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = api.publicBookmarks.getPublicBookmarksInList.useInfiniteQuery( { listId: list.id }, { initialData: () => ({ pages: [{ bookmarks: initialBookmarks, nextCursor, list }], pageParams: [null], }), initialCursor: null, getNextPageParam: (lastPage) => lastPage.nextCursor, refetchOnMount: true, }, ); useEffect(() => { if (loadMoreButtonInView && hasNextPage && !isFetchingNextPage) { fetchNextPage(); } }, [loadMoreButtonInView]); const breakpointConfig = useMemo(() => getBreakpointConfig(), []); const bookmarks = useMemo(() => { return data.pages.flatMap((b) => b.bookmarks); }, [data]); return ( <> {bookmarks.map((bookmark) => ( ))} {hasNextPage && (
fetchNextPage()} variant="ghost" > Load More
)} ); }