aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/components/dashboard/highlights/AllHighlights.tsx
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2024-12-28 12:30:24 +0000
committerMohamed Bassem <me@mbassem.com>2024-12-28 12:30:24 +0000
commit7956e9fa6772ab57a0794fb7cba7e9d9c0bd7878 (patch)
tree10d6e47afed8be007361cd40ec8b05fc52d6d4d9 /apps/web/components/dashboard/highlights/AllHighlights.tsx
parent7dd5b2bc8751911f86448e6c4619b2583d9a4b53 (diff)
downloadkarakeep-7956e9fa6772ab57a0794fb7cba7e9d9c0bd7878.tar.zst
feat: Implement the all highlights page. Fixes #620
Diffstat (limited to 'apps/web/components/dashboard/highlights/AllHighlights.tsx')
-rw-r--r--apps/web/components/dashboard/highlights/AllHighlights.tsx98
1 files changed, 98 insertions, 0 deletions
diff --git a/apps/web/components/dashboard/highlights/AllHighlights.tsx b/apps/web/components/dashboard/highlights/AllHighlights.tsx
new file mode 100644
index 00000000..27dda6ff
--- /dev/null
+++ b/apps/web/components/dashboard/highlights/AllHighlights.tsx
@@ -0,0 +1,98 @@
+"use client";
+
+import { useEffect } from "react";
+import Link from "next/link";
+import { ActionButton } from "@/components/ui/action-button";
+import useRelativeTime from "@/lib/hooks/relative-time";
+import { api } from "@/lib/trpc";
+import { Separator } from "@radix-ui/react-dropdown-menu";
+import dayjs from "dayjs";
+import relativeTime from "dayjs/plugin/relativeTime";
+import { Dot, LinkIcon } from "lucide-react";
+import { useTranslation } from "react-i18next";
+import { useInView } from "react-intersection-observer";
+
+import {
+ ZGetAllHighlightsResponse,
+ ZHighlight,
+} from "@hoarder/shared/types/highlights";
+
+import HighlightCard from "./HighlightCard";
+
+dayjs.extend(relativeTime);
+
+function Highlight({ highlight }: { highlight: ZHighlight }) {
+ const { fromNow, localCreatedAt } = useRelativeTime(highlight.createdAt);
+ const { t } = useTranslation();
+ return (
+ <div className="flex flex-col gap-2">
+ <HighlightCard highlight={highlight} clickable={false} />
+ <span className="flex items-center gap-0.5 text-xs italic text-gray-400">
+ <span title={localCreatedAt}>{fromNow}</span>
+ <Dot />
+ <Link
+ href={`/dashboard/preview/${highlight.bookmarkId}`}
+ className="flex items-center gap-0.5"
+ >
+ <LinkIcon className="size-3 italic" />
+ {t("common.source")}
+ </Link>
+ </span>
+ </div>
+ );
+}
+
+export default function AllHighlights({
+ highlights: initialHighlights,
+}: {
+ highlights: ZGetAllHighlightsResponse;
+}) {
+ const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
+ api.highlights.getAll.useInfiniteQuery(
+ {},
+ {
+ initialData: () => ({
+ pages: [initialHighlights],
+ pageParams: [null],
+ }),
+ initialCursor: null,
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
+ },
+ );
+
+ const { ref: loadMoreRef, inView: loadMoreButtonInView } = useInView();
+ useEffect(() => {
+ if (loadMoreButtonInView && hasNextPage && !isFetchingNextPage) {
+ fetchNextPage();
+ }
+ }, [loadMoreButtonInView]);
+
+ return (
+ <div className="flex flex-col gap-2">
+ {data?.pages
+ .flatMap((p) => p.highlights)
+ .map((h) => (
+ <>
+ <Highlight key={h.id} highlight={h} />
+ <Separator
+ key={`sep-${h.id}`}
+ className="m-2 h-0.5 bg-gray-100 last:hidden"
+ />
+ </>
+ ))}
+ {hasNextPage && (
+ <div className="flex justify-center">
+ <ActionButton
+ ref={loadMoreRef}
+ ignoreDemoMode={true}
+ loading={isFetchingNextPage}
+ onClick={() => fetchNextPage()}
+ variant="ghost"
+ >
+ Load More
+ </ActionButton>
+ </div>
+ )}
+ </div>
+ );
+}