Files
v2.app.gaertan.art/src/app/(normal)/artworks/single/[id]/page.tsx

122 lines
4.1 KiB
TypeScript

import ArtworkMetaCard from "@/components/artworks/ArtworkMetaCard";
import ArtworkTimelapseViewer from "@/components/artworks/ArtworkTimelapseViewer";
import { ContextBackButton } from "@/components/artworks/ContextBackButton";
import NsfwConsentDialog from "@/components/nsfw/NsfwConsentDialog";
import NsfwImage from "@/components/nsfw/NsfwImage";
import NsfwLink from "@/components/nsfw/NsfwLink";
import { Button } from "@/components/ui/button";
import { prisma } from "@/lib/prisma";
import { cn } from "@/lib/utils";
import { PlayCircle } from "lucide-react";
export default async function SingleArtworkPage({
params,
}: {
params: { id: string };
searchParams: Record<string, string | string[] | undefined>;
}) {
const { id } = await params;
const artwork = await prisma.artwork.findUnique({
where: {
id,
},
include: {
file: true,
gallery: true,
metadata: true,
albums: true,
categories: true,
colors: { include: { color: true } },
tags: true,
variants: true,
timelapse: { where: { enabled: true } },
},
});
if (!artwork) return <div>Artwork with this ID could not be found</div>;
const { width, height } = artwork.variants.find(
(v) => v.type === "resized",
) ?? { width: 0, height: 0 };
const colors =
artwork.colors
?.map((c) => c.color?.hex)
.filter((hex): hex is string => Boolean(hex)) ?? [];
const gradientColors = colors.length
? colors.join(", ")
: "rgba(0,0,0,0.1), rgba(0,0,0,0.03)";
return (
<div className="px-4 sm:px-8 py-4">
<NsfwConsentDialog hasNsfw={Boolean(artwork.nsfw)} />
<div className="relative w-full min-h-10 flex items-center mb-4">
<div className="z-10 hidden sm:block">
<ContextBackButton />
</div>
{artwork.name ? (
<div className="w-full text-center sm:pointer-events-none sm:absolute sm:left-1/2 sm:-translate-x-1/2">
<div className="sm:pointer-events-auto">
<h1 className="text-xl sm:text-2xl font-bold mb-2 sm:mb-4 py-2 sm:py-4 px-2 sm:px-0 wrap-break-word">
{artwork.name}
</h1>
</div>
</div>
) : null}
</div>
<div className="flex flex-col items-center gap-4">
<div className="group rounded-lg border overflow-hidden hover:shadow-lg transition-shadow bg-background relative">
<div
className="relative w-full bg-muted items-center justify-center"
style={{ aspectRatio: "4 / 3" }}
>
<NsfwLink href={`/raw/${artwork.id}`} nsfw={Boolean(artwork.nsfw)}>
<NsfwImage
src={`/api/image/resized/${artwork.file.fileKey}.webp`}
alt={artwork.altText || "Artwork"}
fill={!width || !height}
width={width}
height={height}
nsfw={Boolean(artwork.nsfw)}
className={cn("object-cover transition duration-300")}
/>
</NsfwLink>
</div>
</div>
{artwork.timelapse?.enabled ? (
<div className="flex justify-center">
<ArtworkTimelapseViewer
timelapse={artwork.timelapse}
artworkName={artwork.name}
trigger={
<Button size="lg" className="gap-2">
<PlayCircle className="h-5 w-5" />
Watch timelapse
</Button>
}
/>
</div>
) : null}
<div
className="rounded-lg"
style={{
background: `linear-gradient(135deg, ${gradientColors})`,
}}
>
<ArtworkMetaCard
gradientColors={gradientColors}
altText={artwork.altText}
description={artwork.description}
categories={artwork.categories}
tags={artwork.tags}
/>
</div>
<div className="w-full flex justify-center sm:hidden">
<ContextBackButton className="mx-auto flex justify-center" />
</div>
</div>
</div>
);
}