aboutsummaryrefslogtreecommitdiffstats
path: root/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx
blob: 56e3d2438258f207e6b87424eef1675a8cce5fb4 (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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
"use client";

import { Badge } from "@/components/ui/badge";
import {
  ImageCard,
  ImageCardBanner,
  ImageCardBody,
  ImageCardContent,
  ImageCardFooter,
  ImageCardTitle,
} from "@/components/ui/imageCard";
import { ZBookmark } from "@/lib/types/api/bookmarks";
import Link from "next/link";
import BookmarkOptions from "./BookmarkOptions";
import { api } from "@/lib/trpc";
import { Skeleton } from "@/components/ui/skeleton";
import { Star } from "lucide-react";

function isStillCrawling(bookmark: ZBookmark) {
  return (
    !bookmark.content.crawledAt &&
    Date.now().valueOf() - bookmark.createdAt.valueOf() < 30 * 1000
  );
}

function TagList(bookmark: ZBookmark, stillCrawling: boolean) {
  if (stillCrawling) {
    return (
      <ImageCardBody className="space-y-2">
        <Skeleton className="h-4 w-full" />
        <Skeleton className="h-4 w-full" />
        <Skeleton className="h-4 w-full" />
      </ImageCardBody>
    );
  }
  return (
    <ImageCardBody className="flex h-full flex-wrap space-x-1 overflow-hidden">
      {bookmark.tags.map((t) => (
        <Link
          className="flex h-full flex-col justify-end"
          key={t.id}
          href={`/dashboard/tags/${t.name}`}
        >
          <Badge
            variant="default"
            className="text-nowrap bg-gray-300 text-gray-500 hover:text-white"
          >
            #{t.name}
          </Badge>
        </Link>
      ))}
    </ImageCardBody>
  );
}

export default function LinkCard({
  bookmark: initialData,
}: {
  bookmark: ZBookmark;
}) {
  const { data: bookmark } = api.bookmarks.getBookmark.useQuery(
    {
      id: initialData.id,
    },
    {
      initialData,
      refetchInterval: (query) => {
        const data = query.state.data;
        if (!data) {
          return false;
        }
        // If the link is not crawled and
        if (isStillCrawling(data)) {
          return 1000;
        }
        return false;
      },
    },
  );
  const link = bookmark.content;
  const isCrawling = isStillCrawling(bookmark);
  const parsedUrl = new URL(link.url);

  // A dummy white pixel for when there's no image.
  // TODO: Better handling for cards with no images
  const image =
    link.imageUrl ??
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj+P///38ACfsD/QVDRcoAAAAASUVORK5CYII=";

  return (
    <ImageCard
      className={
        "border-grey-100 border bg-gray-50 duration-300 ease-in hover:border-blue-300 hover:transition-all"
      }
    >
      {bookmark.favourited && (
        <Star
          className="absolute m-1 size-8 rounded bg-gray-100 p-1"
          color="#ebb434"
          fill="#ebb434"
        />
      )}
      <Link href={link.url}>
        <ImageCardBanner src={isCrawling ? "/blur.avif" : image} />
      </Link>
      <ImageCardContent>
        <ImageCardTitle>
          <Link className="line-clamp-2" href={link.url}>
            {link?.title ?? parsedUrl.host}
          </Link>
        </ImageCardTitle>
        {/* There's a hack here. Every tag has the full hight of the container itself. That why, when we enable flex-wrap,
        the overflowed don't show up. */}
        {TagList(bookmark, isCrawling)}
        <ImageCardFooter>
          <div className="flex justify-between text-gray-500">
            <div className="my-auto">
              <Link className="line-clamp-1 hover:text-black" href={link.url}>
                {parsedUrl.host}
              </Link>
            </div>
            <BookmarkOptions bookmark={bookmark} />
          </div>
        </ImageCardFooter>
      </ImageCardContent>
    </ImageCard>
  );
}