diff options
| author | Mohamed Bassem <me@mbassem.com> | 2024-12-28 12:30:24 +0000 |
|---|---|---|
| committer | Mohamed Bassem <me@mbassem.com> | 2024-12-28 12:30:24 +0000 |
| commit | 7956e9fa6772ab57a0794fb7cba7e9d9c0bd7878 (patch) | |
| tree | 10d6e47afed8be007361cd40ec8b05fc52d6d4d9 /apps/web/components/dashboard/highlights/AllHighlights.tsx | |
| parent | 7dd5b2bc8751911f86448e6c4619b2583d9a4b53 (diff) | |
| download | karakeep-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.tsx | 98 |
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> + ); +} |
