"use client";
import type { BookmarksLayoutTypes } from "@/lib/userLocalSettings/types";
import type { ReactNode } from "react";
import { useEffect, useState } from "react";
import Image from "next/image";
import Link from "next/link";
import useBulkActionsStore from "@/lib/bulkActions";
import {
bookmarkLayoutSwitch,
useBookmarkDisplaySettings,
useBookmarkLayout,
} from "@/lib/userLocalSettings/bookmarksLayout";
import { cn } from "@/lib/utils";
import { Check, Image as ImageIcon, NotebookPen } from "lucide-react";
import { useSession } from "next-auth/react";
import { useTheme } from "next-themes";
import type { ZBookmark } from "@karakeep/shared/types/bookmarks";
import { BookmarkTypes } from "@karakeep/shared/types/bookmarks";
import { isBookmarkStillTagging } from "@karakeep/shared/utils/bookmarkUtils";
import { switchCase } from "@karakeep/shared/utils/switch";
import BookmarkActionBar from "./BookmarkActionBar";
import BookmarkFormattedCreatedAt from "./BookmarkFormattedCreatedAt";
import { NotePreview } from "./NotePreview";
import TagList from "./TagList";
interface Props {
bookmark: ZBookmark;
image: (layout: BookmarksLayoutTypes, className: string) => ReactNode;
title?: ReactNode;
content?: ReactNode;
footer?: ReactNode;
className?: string;
fitHeight?: boolean;
wrapTags: boolean;
}
function BottomRow({
footer,
bookmark,
}: {
footer?: ReactNode;
bookmark: ZBookmark;
}) {
return (
);
}
function MultiBookmarkSelector({ bookmark }: { bookmark: ZBookmark }) {
const { selectedBookmarks, isBulkEditEnabled } = useBulkActionsStore();
const toggleBookmark = useBulkActionsStore((state) => state.toggleBookmark);
const [isSelected, setIsSelected] = useState(false);
const { theme } = useTheme();
const { data: session } = useSession();
useEffect(() => {
setIsSelected(selectedBookmarks.some((item) => item.id === bookmark.id));
}, [selectedBookmarks]);
// Don't show selector for non-owned bookmarks or when bulk edit is disabled
const isOwner = session?.user?.id === bookmark.userId;
if (!isBulkEditEnabled || !isOwner) return null;
const getIconColor = () => {
if (theme === "dark") {
return isSelected ? "black" : "white";
}
return isSelected ? "white" : "black";
};
const getIconBackgroundColor = () => {
if (theme === "dark") {
return isSelected ? "bg-white" : "bg-white bg-opacity-10";
}
return isSelected ? "bg-black" : "bg-white bg-opacity-40";
};
return (
);
}
function ListView({
bookmark,
image,
title,
content,
footer,
className,
}: Props) {
const { showNotes, showTags, showTitle, imageFit } =
useBookmarkDisplaySettings();
const imgFitClass = switchCase(imageFit, {
cover: "object-cover",
contain: "object-contain",
});
const note = showNotes ? bookmark.note?.trim() : undefined;
return (
{image("list", cn("size-32 rounded-lg", imgFitClass))}
{showTitle && title && (
{title}
)}
{content &&
{content}
}
{note &&
}
{showTags && (
)}
);
}
function GridView({
bookmark,
image,
title,
content,
footer,
className,
wrapTags,
layout,
fitHeight = false,
}: Props & { layout: BookmarksLayoutTypes }) {
const { showNotes, showTags, showTitle, imageFit } =
useBookmarkDisplaySettings();
const imgFitClass = switchCase(imageFit, {
cover: "object-cover",
contain: "object-contain",
});
const note = showNotes ? bookmark.note?.trim() : undefined;
const img = image(
"grid",
cn("h-56 min-h-56 w-full rounded-t-lg", imgFitClass),
);
return (
{img &&
{img}
}
{showTitle && title && (
{title}
)}
{content &&
{content}
}
{note &&
}
{showTags && (
)}
);
}
function CompactView({ bookmark, title, footer, className }: Props) {
const { showTitle } = useBookmarkDisplaySettings();
return (
{bookmark.content.type === BookmarkTypes.LINK &&
bookmark.content.favicon && (
)}
{bookmark.content.type === BookmarkTypes.TEXT && (
)}
{bookmark.content.type === BookmarkTypes.ASSET && (
)}
{showTitle && (
{title ?? "Untitled"}
)}
{footer && (
•{footer}
)}
•
);
}
export function BookmarkLayoutAdaptingCard(props: Props) {
const layout = useBookmarkLayout();
return bookmarkLayoutSwitch(layout, {
masonry: ,
grid: ,
list: ,
compact: ,
});
}