94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
"use client"
|
|
|
|
import { type Color, type Image, type ImageColor } from "@/generated/prisma"
|
|
import { useGlobalSettings } from "@/hooks/useGlobalSettings"
|
|
import clsx from "clsx"
|
|
import { BookOpenIcon, EyeOffIcon, ImagePlusIcon } from "lucide-react"
|
|
import NextImage from "next/image"
|
|
import Link from "next/link"
|
|
import { GlowingBorderWrapper } from "./GlowingBorderWrapper"
|
|
import { TagBadge } from "./TagBadge"
|
|
|
|
type CoverCardProps = {
|
|
item: {
|
|
id: string
|
|
name: string
|
|
slug: string
|
|
coverImage?: (Image & {
|
|
colors?: (ImageColor & { color: Color })[]
|
|
}) | null
|
|
type: "album" | "gallery"
|
|
gallerySlug?: string
|
|
}
|
|
}
|
|
|
|
export default function CoverCard({ item }: CoverCardProps) {
|
|
const { showNSFW, animateGlow } = useGlobalSettings()
|
|
const { coverImage } = item
|
|
|
|
const href =
|
|
item.type === "album"
|
|
? `/galleries/${item.gallerySlug}/${item.slug}`
|
|
: `/galleries/${item.slug}`
|
|
|
|
const badgeText = item.type === "album" ? "Album" : "Gallery"
|
|
const badgeIcon =
|
|
item.type === "album" ? <BookOpenIcon size={12} className="mr-1" /> : <ImagePlusIcon size={12} className="mr-1" />
|
|
|
|
const borderColors =
|
|
coverImage?.colors?.map((c) => c.color?.hex).filter((hex): hex is string => Boolean(hex)) ?? []
|
|
|
|
const shouldBlur = coverImage?.nsfw && !showNSFW
|
|
|
|
const content = (
|
|
<div className="group rounded-lg border overflow-hidden hover:shadow-lg transition-shadow bg-background relative">
|
|
<div className="relative aspect-[4/3] w-full bg-muted items-center justify-center">
|
|
{coverImage?.fileKey ? (
|
|
<NextImage
|
|
src={`/api/image/thumbnails/${coverImage.fileKey}.webp`}
|
|
alt={coverImage.imageName}
|
|
fill
|
|
className={clsx(
|
|
"object-cover transition duration-300",
|
|
shouldBlur && "blur-md scale-105"
|
|
)}
|
|
/>
|
|
) : (
|
|
<div className="flex items-center justify-center h-full text-muted-foreground text-sm">
|
|
No cover image
|
|
</div>
|
|
)}
|
|
<div className="absolute top-1 left-1 z-10 flex gap-1">
|
|
<TagBadge
|
|
label={badgeText}
|
|
variant="secondary"
|
|
className="text-xs px-2 py-0.5 inline-flex items-center"
|
|
icon={badgeIcon}
|
|
/>
|
|
{coverImage?.nsfw && (
|
|
<TagBadge
|
|
label="NSFW"
|
|
variant="destructive"
|
|
icon={<EyeOffIcon size={12} />}
|
|
className="text-xs px-2 py-0.5 inline-flex items-center"
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="p-4">
|
|
<h2 className="text-lg font-semibold truncate text-center">{item.name}</h2>
|
|
</div>
|
|
</div>
|
|
)
|
|
|
|
return (
|
|
<Link href={href}>
|
|
{coverImage && animateGlow && borderColors.length > 0 ? (
|
|
<GlowingBorderWrapper colors={borderColors}>{content}</GlowingBorderWrapper>
|
|
) : (
|
|
content
|
|
)}
|
|
</Link>
|
|
)
|
|
}
|