aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/components/dashboard/bookmarks/BookmarksGrid.tsx
blob: 4b0dc4fde2b21d93015af465c4adb8e2e8a63736 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
"use client";

import { useMemo } from "react";
import { ActionButton } from "@/components/ui/action-button";
import { api } from "@/lib/trpc";
import tailwindConfig from "@/tailwind.config";
import { Slot } from "@radix-ui/react-slot";
import Masonry from "react-masonry-css";
import resolveConfig from "tailwindcss/resolveConfig";

import type {
  ZBookmark,
  ZGetBookmarksRequest,
  ZGetBookmarksResponse,
} from "@hoarder/trpc/types/bookmarks";

import AssetCard from "./AssetCard";
import EditorCard from "./EditorCard";
import LinkCard from "./LinkCard";
import TextCard from "./TextCard";

function BookmarkCard({ children }: { children: React.ReactNode }) {
  return (
    <Slot className="border-grey-100 mb-4 border bg-gray-50 duration-300 ease-in hover:shadow-lg hover:transition-all">
      {children}
    </Slot>
  );
}

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;
}

function renderBookmark(bookmark: ZBookmark) {
  let comp;
  switch (bookmark.content.type) {
    case "link":
      comp = <LinkCard bookmark={bookmark} />;
      break;
    case "text":
      comp = <TextCard bookmark={bookmark} />;
      break;
    case "asset":
      comp = <AssetCard bookmark={bookmark} />;
      break;
  }
  return <BookmarkCard key={bookmark.id}>{comp}</BookmarkCard>;
}

export default function BookmarksGrid({
  query,
  bookmarks: initialBookmarks,
  showEditorCard = false,
}: {
  query: ZGetBookmarksRequest;
  bookmarks: ZGetBookmarksResponse;
  showEditorCard?: boolean;
  itemsPerPage?: number;
}) {
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
    api.bookmarks.getBookmarks.useInfiniteQuery(query, {
      initialData: () => ({
        pages: [initialBookmarks],
        pageParams: [query.cursor],
      }),
      initialCursor: null,
      getNextPageParam: (lastPage) => lastPage.nextCursor,
    });

  const breakpointConfig = useMemo(() => getBreakpointConfig(), []);
  const bookmarks = data!.pages.flatMap((b) => b.bookmarks);
  if (bookmarks.length == 0) {
    return <p>No bookmarks</p>;
  }
  return (
    <>
      <Masonry className="flex gap-4" breakpointCols={breakpointConfig}>
        {showEditorCard && (
          <BookmarkCard>
            <EditorCard />
          </BookmarkCard>
        )}
        {bookmarks.map((b) => renderBookmark(b))}
      </Masonry>
      {hasNextPage && (
        <ActionButton
          ignoreDemoMode={true}
          loading={isFetchingNextPage}
          onClick={() => fetchNextPage()}
          className="mx-auto w-min"
          variant="ghost"
        >
          Load More
        </ActionButton>
      )}
    </>
  );
}