"use client";
import React, { useEffect, useState } from "react";
import Link from "next/link";
import { ActionButton } from "@/components/ui/action-button";
import { Input } from "@/components/ui/input";
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, Search, X } from "lucide-react";
import { useTranslation } from "react-i18next";
import { useInView } from "react-intersection-observer";
import { useDebounce } from "@karakeep/shared-react/hooks/use-debounce";
import {
ZGetAllHighlightsResponse,
ZHighlight,
} from "@karakeep/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 (
{fromNow}
{t("common.source")}
);
}
export default function AllHighlights({
highlights: initialHighlights,
}: {
highlights: ZGetAllHighlightsResponse;
}) {
const { t } = useTranslation();
const [searchInput, setSearchInput] = useState("");
const debouncedSearch = useDebounce(searchInput, 300);
// Use search endpoint if searchQuery is provided, otherwise use getAll
const useSearchQuery = debouncedSearch.trim().length > 0;
const getAllQuery = api.highlights.getAll.useInfiniteQuery(
{},
{
enabled: !useSearchQuery,
initialData: !useSearchQuery
? () => ({
pages: [initialHighlights],
pageParams: [null],
})
: undefined,
initialCursor: null,
getNextPageParam: (lastPage) => lastPage.nextCursor,
},
);
const searchQueryResult = api.highlights.search.useInfiniteQuery(
{ text: debouncedSearch },
{
enabled: useSearchQuery,
initialCursor: null,
getNextPageParam: (lastPage) => lastPage.nextCursor,
},
);
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useSearchQuery ? searchQueryResult : getAllQuery;
const { ref: loadMoreRef, inView: loadMoreButtonInView } = useInView();
useEffect(() => {
if (loadMoreButtonInView && hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}, [loadMoreButtonInView]);
const allHighlights = data?.pages.flatMap((p) => p.highlights);
return (
{/* Search Input */}
setSearchInput(e.target.value)}
startIcon={}
endIcon={
searchInput && (
)
}
className="w-full"
/>
{/* Results */}
{allHighlights &&
allHighlights.length > 0 &&
allHighlights.map((h) => (
))}
{allHighlights && allHighlights.length == 0 && (
{t("highlights.no_highlights")}
)}
{hasNextPage && (
fetchNextPage()}
variant="ghost"
>
Load More
)}
);
}